summaryrefslogtreecommitdiff
path: root/i386/i386at
diff options
context:
space:
mode:
authorThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
committerThomas Bushnell <thomas@gnu.org>1997-02-25 21:28:37 +0000
commitf07a4c844da9f0ecae5bbee1ab94be56505f26f7 (patch)
tree12b07c7e578fc1a5f53dbfde2632408491ff2a70 /i386/i386at
Initial source
Diffstat (limited to 'i386/i386at')
-rw-r--r--i386/i386at/asm_startup.h42
-rw-r--r--i386/i386at/autoconf.c484
-rw-r--r--i386/i386at/blit.c948
-rw-r--r--i386/i386at/blitreg.h404
-rw-r--r--i386/i386at/blituser.h73
-rw-r--r--i386/i386at/blitvar.h116
-rw-r--r--i386/i386at/boothdr.S62
-rw-r--r--i386/i386at/com.c891
-rw-r--r--i386/i386at/comreg.h134
-rw-r--r--i386/i386at/conf.c399
-rw-r--r--i386/i386at/cons_conf.c50
-rw-r--r--i386/i386at/cram.h75
-rw-r--r--i386/i386at/dev_hdr.h43
-rw-r--r--i386/i386at/device_emul.h64
-rw-r--r--i386/i386at/disk.h186
-rw-r--r--i386/i386at/ds8390.h166
-rw-r--r--i386/i386at/eisa.h110
-rw-r--r--i386/i386at/fd.c1701
-rw-r--r--i386/i386at/fdreg.h368
-rw-r--r--i386/i386at/gpl/if_hpp.c690
-rw-r--r--i386/i386at/gpl/if_ns.c642
-rw-r--r--i386/i386at/gpl/if_nsreg.h169
-rw-r--r--i386/i386at/gpl/if_ul.c489
-rw-r--r--i386/i386at/gpl/if_wd.c581
-rw-r--r--i386/i386at/gpl/linux/block/cmd640.c738
-rw-r--r--i386/i386at/gpl/linux/block/floppy.c4100
-rw-r--r--i386/i386at/gpl/linux/block/genhd.c610
-rw-r--r--i386/i386at/gpl/linux/block/ide-cd.c2770
-rw-r--r--i386/i386at/gpl/linux/block/ide.c3087
-rw-r--r--i386/i386at/gpl/linux/block/ide.h655
-rw-r--r--i386/i386at/gpl/linux/block/ide_modes.h142
-rw-r--r--i386/i386at/gpl/linux/block/rz1000.c56
-rw-r--r--i386/i386at/gpl/linux/block/triton.c459
-rw-r--r--i386/i386at/gpl/linux/include/asm/bitops.h137
-rw-r--r--i386/i386at/gpl/linux/include/asm/byteorder.h90
-rw-r--r--i386/i386at/gpl/linux/include/asm/delay.h59
-rw-r--r--i386/i386at/gpl/linux/include/asm/dma.h271
-rw-r--r--i386/i386at/gpl/linux/include/asm/errno.h252
-rw-r--r--i386/i386at/gpl/linux/include/asm/fcntl.h64
-rw-r--r--i386/i386at/gpl/linux/include/asm/floppy.h56
-rw-r--r--i386/i386at/gpl/linux/include/asm/io.h213
-rw-r--r--i386/i386at/gpl/linux/include/asm/ioctl.h75
-rw-r--r--i386/i386at/gpl/linux/include/asm/irq.h346
-rw-r--r--i386/i386at/gpl/linux/include/asm/page.h64
-rw-r--r--i386/i386at/gpl/linux/include/asm/param.h20
-rw-r--r--i386/i386at/gpl/linux/include/asm/processor.h146
-rw-r--r--i386/i386at/gpl/linux/include/asm/ptrace.h52
-rw-r--r--i386/i386at/gpl/linux/include/asm/resource.h37
-rw-r--r--i386/i386at/gpl/linux/include/asm/segment.h347
-rw-r--r--i386/i386at/gpl/linux/include/asm/sigcontext.h29
-rw-r--r--i386/i386at/gpl/linux/include/asm/signal.h95
-rw-r--r--i386/i386at/gpl/linux/include/asm/socket.h31
-rw-r--r--i386/i386at/gpl/linux/include/asm/stat.h41
-rw-r--r--i386/i386at/gpl/linux/include/asm/statfs.h21
-rw-r--r--i386/i386at/gpl/linux/include/asm/string.h593
-rw-r--r--i386/i386at/gpl/linux/include/asm/system.h301
-rw-r--r--i386/i386at/gpl/linux/include/asm/termios.h304
-rw-r--r--i386/i386at/gpl/linux/include/asm/types.h109
-rw-r--r--i386/i386at/gpl/linux/include/asm/unistd.h322
-rw-r--r--i386/i386at/gpl/linux/include/linux/autoconf.h210
-rw-r--r--i386/i386at/gpl/linux/include/linux/binfmts.h60
-rw-r--r--i386/i386at/gpl/linux/include/linux/bios32.h61
-rw-r--r--i386/i386at/gpl/linux/include/linux/blk.h424
-rw-r--r--i386/i386at/gpl/linux/include/linux/blkdev.h56
-rw-r--r--i386/i386at/gpl/linux/include/linux/cdrom.h465
-rw-r--r--i386/i386at/gpl/linux/include/linux/config.h41
-rw-r--r--i386/i386at/gpl/linux/include/linux/delay.h14
-rw-r--r--i386/i386at/gpl/linux/include/linux/errno.h16
-rw-r--r--i386/i386at/gpl/linux/include/linux/etherdevice.h55
-rw-r--r--i386/i386at/gpl/linux/include/linux/fcntl.h6
-rw-r--r--i386/i386at/gpl/linux/include/linux/fd.h368
-rw-r--r--i386/i386at/gpl/linux/include/linux/fdreg.h127
-rw-r--r--i386/i386at/gpl/linux/include/linux/fs.h720
-rw-r--r--i386/i386at/gpl/linux/include/linux/genhd.h73
-rw-r--r--i386/i386at/gpl/linux/include/linux/hdreg.h171
-rw-r--r--i386/i386at/gpl/linux/include/linux/head.h19
-rw-r--r--i386/i386at/gpl/linux/include/linux/if.h167
-rw-r--r--i386/i386at/gpl/linux/include/linux/if_arp.h103
-rw-r--r--i386/i386at/gpl/linux/include/linux/if_ether.h96
-rw-r--r--i386/i386at/gpl/linux/include/linux/if_tr.h104
-rw-r--r--i386/i386at/gpl/linux/include/linux/igmp.h117
-rw-r--r--i386/i386at/gpl/linux/include/linux/in.h149
-rw-r--r--i386/i386at/gpl/linux/include/linux/inet.h52
-rw-r--r--i386/i386at/gpl/linux/include/linux/interrupt.h91
-rw-r--r--i386/i386at/gpl/linux/include/linux/ioctl.h7
-rw-r--r--i386/i386at/gpl/linux/include/linux/ioport.h31
-rw-r--r--i386/i386at/gpl/linux/include/linux/ip.h113
-rw-r--r--i386/i386at/gpl/linux/include/linux/ipc.h67
-rw-r--r--i386/i386at/gpl/linux/include/linux/kdev_t.h114
-rw-r--r--i386/i386at/gpl/linux/include/linux/kernel.h94
-rw-r--r--i386/i386at/gpl/linux/include/linux/kernel_stat.h32
-rw-r--r--i386/i386at/gpl/linux/include/linux/limits.h17
-rw-r--r--i386/i386at/gpl/linux/include/linux/linkage.h59
-rw-r--r--i386/i386at/gpl/linux/include/linux/locks.h66
-rw-r--r--i386/i386at/gpl/linux/include/linux/major.h119
-rw-r--r--i386/i386at/gpl/linux/include/linux/malloc.h16
-rw-r--r--i386/i386at/gpl/linux/include/linux/math_emu.h43
-rw-r--r--i386/i386at/gpl/linux/include/linux/mc146818rtc.h109
-rw-r--r--i386/i386at/gpl/linux/include/linux/minix_fs.h135
-rw-r--r--i386/i386at/gpl/linux/include/linux/minix_fs_sb.h25
-rw-r--r--i386/i386at/gpl/linux/include/linux/mm.h297
-rw-r--r--i386/i386at/gpl/linux/include/linux/module.h115
-rw-r--r--i386/i386at/gpl/linux/include/linux/mount.h30
-rw-r--r--i386/i386at/gpl/linux/include/linux/net.h132
-rw-r--r--i386/i386at/gpl/linux/include/linux/netdevice.h332
-rw-r--r--i386/i386at/gpl/linux/include/linux/nfs.h172
-rw-r--r--i386/i386at/gpl/linux/include/linux/notifier.h100
-rw-r--r--i386/i386at/gpl/linux/include/linux/pagemap.h131
-rw-r--r--i386/i386at/gpl/linux/include/linux/param.h6
-rw-r--r--i386/i386at/gpl/linux/include/linux/pci.h618
-rw-r--r--i386/i386at/gpl/linux/include/linux/personality.h51
-rw-r--r--i386/i386at/gpl/linux/include/linux/proc_fs.h269
-rw-r--r--i386/i386at/gpl/linux/include/linux/ptrace.h26
-rw-r--r--i386/i386at/gpl/linux/include/linux/quota.h219
-rw-r--r--i386/i386at/gpl/linux/include/linux/resource.h60
-rw-r--r--i386/i386at/gpl/linux/include/linux/route.h78
-rw-r--r--i386/i386at/gpl/linux/include/linux/sched.h492
-rw-r--r--i386/i386at/gpl/linux/include/linux/scsi.h198
-rw-r--r--i386/i386at/gpl/linux/include/linux/scsicam.h17
-rw-r--r--i386/i386at/gpl/linux/include/linux/sem.h112
-rw-r--r--i386/i386at/gpl/linux/include/linux/signal.h6
-rw-r--r--i386/i386at/gpl/linux/include/linux/skbuff.h474
-rw-r--r--i386/i386at/gpl/linux/include/linux/smp.h54
-rw-r--r--i386/i386at/gpl/linux/include/linux/socket.h126
-rw-r--r--i386/i386at/gpl/linux/include/linux/sockios.h91
-rw-r--r--i386/i386at/gpl/linux/include/linux/stat.h53
-rw-r--r--i386/i386at/gpl/linux/include/linux/stddef.h15
-rw-r--r--i386/i386at/gpl/linux/include/linux/string.h44
-rw-r--r--i386/i386at/gpl/linux/include/linux/tasks.h19
-rw-r--r--i386/i386at/gpl/linux/include/linux/tcp.h71
-rw-r--r--i386/i386at/gpl/linux/include/linux/termios.h7
-rw-r--r--i386/i386at/gpl/linux/include/linux/time.h50
-rw-r--r--i386/i386at/gpl/linux/include/linux/timer.h101
-rw-r--r--i386/i386at/gpl/linux/include/linux/tqueue.h163
-rw-r--r--i386/i386at/gpl/linux/include/linux/trdevice.h40
-rw-r--r--i386/i386at/gpl/linux/include/linux/tty.h340
-rw-r--r--i386/i386at/gpl/linux/include/linux/tty_driver.h189
-rw-r--r--i386/i386at/gpl/linux/include/linux/tty_ldisc.h46
-rw-r--r--i386/i386at/gpl/linux/include/linux/types.h72
-rw-r--r--i386/i386at/gpl/linux/include/linux/uio.h25
-rw-r--r--i386/i386at/gpl/linux/include/linux/unistd.h11
-rw-r--r--i386/i386at/gpl/linux/include/linux/utsname.h35
-rw-r--r--i386/i386at/gpl/linux/include/linux/version.h8
-rw-r--r--i386/i386at/gpl/linux/include/linux/vfs.h6
-rw-r--r--i386/i386at/gpl/linux/include/linux/vm86.h109
-rw-r--r--i386/i386at/gpl/linux/include/linux/wait.h38
-rw-r--r--i386/i386at/gpl/linux/include/net/af_unix.h4
-rw-r--r--i386/i386at/gpl/linux/include/net/arp.h17
-rw-r--r--i386/i386at/gpl/linux/include/net/atalkcall.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/ax25.h246
-rw-r--r--i386/i386at/gpl/linux/include/net/ax25call.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/checksum.h25
-rw-r--r--i386/i386at/gpl/linux/include/net/datalink.h16
-rw-r--r--i386/i386at/gpl/linux/include/net/icmp.h40
-rw-r--r--i386/i386at/gpl/linux/include/net/ip.h154
-rw-r--r--i386/i386at/gpl/linux/include/net/ip_alias.h23
-rw-r--r--i386/i386at/gpl/linux/include/net/ip_forward.h10
-rw-r--r--i386/i386at/gpl/linux/include/net/ipip.h4
-rw-r--r--i386/i386at/gpl/linux/include/net/ipx.h85
-rw-r--r--i386/i386at/gpl/linux/include/net/ipxcall.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/netlink.h26
-rw-r--r--i386/i386at/gpl/linux/include/net/netrom.h139
-rw-r--r--i386/i386at/gpl/linux/include/net/nrcall.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/p8022.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/p8022call.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/protocol.h55
-rw-r--r--i386/i386at/gpl/linux/include/net/psnap.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/psnapcall.h2
-rw-r--r--i386/i386at/gpl/linux/include/net/rarp.h12
-rw-r--r--i386/i386at/gpl/linux/include/net/raw.h34
-rw-r--r--i386/i386at/gpl/linux/include/net/route.h280
-rw-r--r--i386/i386at/gpl/linux/include/net/slhc.h6
-rw-r--r--i386/i386at/gpl/linux/include/net/snmp.h107
-rw-r--r--i386/i386at/gpl/linux/include/net/sock.h486
-rw-r--r--i386/i386at/gpl/linux/include/net/tcp.h329
-rw-r--r--i386/i386at/gpl/linux/include/net/udp.h52
-rw-r--r--i386/i386at/gpl/linux/linux_autoirq.c161
-rw-r--r--i386/i386at/gpl/linux/linux_block.c2579
-rw-r--r--i386/i386at/gpl/linux/linux_dma.c52
-rw-r--r--i386/i386at/gpl/linux/linux_emul.h32
-rw-r--r--i386/i386at/gpl/linux/linux_init.c412
-rw-r--r--i386/i386at/gpl/linux/linux_irq.c246
-rw-r--r--i386/i386at/gpl/linux/linux_kmem.c481
-rw-r--r--i386/i386at/gpl/linux/linux_misc.c303
-rw-r--r--i386/i386at/gpl/linux/linux_net.c520
-rw-r--r--i386/i386at/gpl/linux/linux_port.c79
-rw-r--r--i386/i386at/gpl/linux/linux_printk.c47
-rw-r--r--i386/i386at/gpl/linux/linux_sched.c237
-rw-r--r--i386/i386at/gpl/linux/linux_soft.c74
-rw-r--r--i386/i386at/gpl/linux/linux_timer.c190
-rw-r--r--i386/i386at/gpl/linux/linux_version.c33
-rw-r--r--i386/i386at/gpl/linux/linux_vsprintf.c341
-rw-r--r--i386/i386at/gpl/linux/net/3c501.c860
-rw-r--r--i386/i386at/gpl/linux/net/3c503.c627
-rw-r--r--i386/i386at/gpl/linux/net/3c503.h91
-rw-r--r--i386/i386at/gpl/linux/net/3c505.c1518
-rw-r--r--i386/i386at/gpl/linux/net/3c505.h245
-rw-r--r--i386/i386at/gpl/linux/net/3c507.c923
-rw-r--r--i386/i386at/gpl/linux/net/3c509.c739
-rw-r--r--i386/i386at/gpl/linux/net/3c59x.c1066
-rw-r--r--i386/i386at/gpl/linux/net/8390.c727
-rw-r--r--i386/i386at/gpl/linux/net/8390.h168
-rw-r--r--i386/i386at/gpl/linux/net/Space.c400
-rw-r--r--i386/i386at/gpl/linux/net/ac3200.c385
-rw-r--r--i386/i386at/gpl/linux/net/apricot.c1046
-rw-r--r--i386/i386at/gpl/linux/net/at1700.c677
-rw-r--r--i386/i386at/gpl/linux/net/atp.c787
-rw-r--r--i386/i386at/gpl/linux/net/atp.h264
-rw-r--r--i386/i386at/gpl/linux/net/de4x5.c2788
-rw-r--r--i386/i386at/gpl/linux/net/de4x5.h645
-rw-r--r--i386/i386at/gpl/linux/net/de600.c853
-rw-r--r--i386/i386at/gpl/linux/net/de620.c1045
-rw-r--r--i386/i386at/gpl/linux/net/de620.h117
-rw-r--r--i386/i386at/gpl/linux/net/depca.c1901
-rw-r--r--i386/i386at/gpl/linux/net/depca.h185
-rw-r--r--i386/i386at/gpl/linux/net/dev.c1413
-rw-r--r--i386/i386at/gpl/linux/net/e2100.c456
-rw-r--r--i386/i386at/gpl/linux/net/eepro.c1169
-rw-r--r--i386/i386at/gpl/linux/net/eexpress.c1034
-rw-r--r--i386/i386at/gpl/linux/net/eth16i.c1214
-rw-r--r--i386/i386at/gpl/linux/net/ewrk3.c1933
-rw-r--r--i386/i386at/gpl/linux/net/ewrk3.h322
-rw-r--r--i386/i386at/gpl/linux/net/hp-plus.c483
-rw-r--r--i386/i386at/gpl/linux/net/hp.c451
-rw-r--r--i386/i386at/gpl/linux/net/hp100.c1144
-rw-r--r--i386/i386at/gpl/linux/net/hp100.h374
-rw-r--r--i386/i386at/gpl/linux/net/i82586.h408
-rw-r--r--i386/i386at/gpl/linux/net/iow.h6
-rw-r--r--i386/i386at/gpl/linux/net/lance.c1129
-rw-r--r--i386/i386at/gpl/linux/net/ne.c733
-rw-r--r--i386/i386at/gpl/linux/net/net_init.c380
-rw-r--r--i386/i386at/gpl/linux/net/ni52.c1110
-rw-r--r--i386/i386at/gpl/linux/net/ni52.h284
-rw-r--r--i386/i386at/gpl/linux/net/ni65.c648
-rw-r--r--i386/i386at/gpl/linux/net/ni65.h130
-rw-r--r--i386/i386at/gpl/linux/net/seeq8005.c760
-rw-r--r--i386/i386at/gpl/linux/net/seeq8005.h156
-rw-r--r--i386/i386at/gpl/linux/net/sk_g16.c2111
-rw-r--r--i386/i386at/gpl/linux/net/sk_g16.h171
-rw-r--r--i386/i386at/gpl/linux/net/smc-ultra.c419
-rw-r--r--i386/i386at/gpl/linux/net/tulip.c782
-rw-r--r--i386/i386at/gpl/linux/net/wavelan.c2526
-rw-r--r--i386/i386at/gpl/linux/net/wavelan.h252
-rw-r--r--i386/i386at/gpl/linux/net/wd.c513
-rw-r--r--i386/i386at/gpl/linux/net/znet.c746
-rw-r--r--i386/i386at/gpl/linux/pci/bios32.c460
-rw-r--r--i386/i386at/gpl/linux/pci/pci.c915
-rw-r--r--i386/i386at/gpl/linux/scsi/53c7,8xx.c6381
-rw-r--r--i386/i386at/gpl/linux/scsi/53c7,8xx.h1584
-rw-r--r--i386/i386at/gpl/linux/scsi/53c8xx_d.h2677
-rw-r--r--i386/i386at/gpl/linux/scsi/53c8xx_u.h97
-rw-r--r--i386/i386at/gpl/linux/scsi/AM53C974.c2249
-rw-r--r--i386/i386at/gpl/linux/scsi/AM53C974.h419
-rw-r--r--i386/i386at/gpl/linux/scsi/BusLogic.c2779
-rw-r--r--i386/i386at/gpl/linux/scsi/BusLogic.h977
-rw-r--r--i386/i386at/gpl/linux/scsi/NCR5380.h363
-rw-r--r--i386/i386at/gpl/linux/scsi/NCR5380.src3035
-rw-r--r--i386/i386at/gpl/linux/scsi/NCR53c406a.c1079
-rw-r--r--i386/i386at/gpl/linux/scsi/NCR53c406a.h83
-rw-r--r--i386/i386at/gpl/linux/scsi/advansys.c9061
-rw-r--r--i386/i386at/gpl/linux/scsi/advansys.h131
-rw-r--r--i386/i386at/gpl/linux/scsi/aha152x.c2985
-rw-r--r--i386/i386at/gpl/linux/scsi/aha152x.h373
-rw-r--r--i386/i386at/gpl/linux/scsi/aha1542.c1323
-rw-r--r--i386/i386at/gpl/linux/scsi/aha1542.h177
-rw-r--r--i386/i386at/gpl/linux/scsi/aha1740.c528
-rw-r--r--i386/i386at/gpl/linux/scsi/aha1740.h193
-rw-r--r--i386/i386at/gpl/linux/scsi/aic7xxx.c4645
-rw-r--r--i386/i386at/gpl/linux/scsi/aic7xxx.h67
-rw-r--r--i386/i386at/gpl/linux/scsi/aic7xxx_proc.src271
-rw-r--r--i386/i386at/gpl/linux/scsi/aic7xxx_reg.h746
-rw-r--r--i386/i386at/gpl/linux/scsi/aic7xxx_seq.h374
-rw-r--r--i386/i386at/gpl/linux/scsi/constants.c649
-rw-r--r--i386/i386at/gpl/linux/scsi/constants.h6
-rw-r--r--i386/i386at/gpl/linux/scsi/eata.c1099
-rw-r--r--i386/i386at/gpl/linux/scsi/eata.h41
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_dma.c1375
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_dma.h119
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_dma_proc.h260
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_dma_proc.src488
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_generic.h397
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_pio.c1051
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_pio.h116
-rw-r--r--i386/i386at/gpl/linux/scsi/eata_pio_proc.src150
-rw-r--r--i386/i386at/gpl/linux/scsi/fdomain.c2016
-rw-r--r--i386/i386at/gpl/linux/scsi/fdomain.h61
-rw-r--r--i386/i386at/gpl/linux/scsi/g_NCR5380.c588
-rw-r--r--i386/i386at/gpl/linux/scsi/g_NCR5380.h166
-rw-r--r--i386/i386at/gpl/linux/scsi/hosts.c436
-rw-r--r--i386/i386at/gpl/linux/scsi/hosts.h409
-rw-r--r--i386/i386at/gpl/linux/scsi/in2000.c731
-rw-r--r--i386/i386at/gpl/linux/scsi/in2000.h122
-rw-r--r--i386/i386at/gpl/linux/scsi/pas16.c553
-rw-r--r--i386/i386at/gpl/linux/scsi/pas16.h193
-rw-r--r--i386/i386at/gpl/linux/scsi/qlogic.c678
-rw-r--r--i386/i386at/gpl/linux/scsi/qlogic.h40
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi.c3204
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi.h618
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi_debug.c710
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi_debug.h30
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi_ioctl.c397
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi_ioctl.h21
-rw-r--r--i386/i386at/gpl/linux/scsi/scsi_proc.c317
-rw-r--r--i386/i386at/gpl/linux/scsi/scsicam.c214
-rw-r--r--i386/i386at/gpl/linux/scsi/sd.c1543
-rw-r--r--i386/i386at/gpl/linux/scsi/sd.h65
-rw-r--r--i386/i386at/gpl/linux/scsi/sd_ioctl.c94
-rw-r--r--i386/i386at/gpl/linux/scsi/seagate.c1744
-rw-r--r--i386/i386at/gpl/linux/scsi/seagate.h139
-rw-r--r--i386/i386at/gpl/linux/scsi/sr.c1191
-rw-r--r--i386/i386at/gpl/linux/scsi/sr.h40
-rw-r--r--i386/i386at/gpl/linux/scsi/sr_ioctl.c489
-rw-r--r--i386/i386at/gpl/linux/scsi/t128.c413
-rw-r--r--i386/i386at/gpl/linux/scsi/t128.h176
-rw-r--r--i386/i386at/gpl/linux/scsi/u14-34f.c1044
-rw-r--r--i386/i386at/gpl/linux/scsi/u14-34f.h38
-rw-r--r--i386/i386at/gpl/linux/scsi/ultrastor.c1160
-rw-r--r--i386/i386at/gpl/linux/scsi/ultrastor.h102
-rw-r--r--i386/i386at/gpl/linux/scsi/wd7000.c1237
-rw-r--r--i386/i386at/gpl/linux/scsi/wd7000.h55
-rw-r--r--i386/i386at/i386at_ds_routines.c270
-rw-r--r--i386/i386at/i8250.h129
-rw-r--r--i386/i386at/i82586.h264
-rw-r--r--i386/i386at/idt.h37
-rw-r--r--i386/i386at/if_3c501.c1240
-rw-r--r--i386/i386at/if_3c501.h175
-rw-r--r--i386/i386at/if_3c503.h116
-rw-r--r--i386/i386at/if_de6c.c1777
-rw-r--r--i386/i386at/if_de6c.h113
-rw-r--r--i386/i386at/if_de6s.S278
-rw-r--r--i386/i386at/if_ne.c1081
-rw-r--r--i386/i386at/if_nereg.h66
-rw-r--r--i386/i386at/if_ns8390.c2578
-rw-r--r--i386/i386at/if_ns8390.h203
-rw-r--r--i386/i386at/if_par.c456
-rw-r--r--i386/i386at/if_par.h36
-rw-r--r--i386/i386at/if_pc586.c2076
-rw-r--r--i386/i386at/if_pc586.h139
-rw-r--r--i386/i386at/if_wd8003.h315
-rw-r--r--i386/i386at/immc.c77
-rw-r--r--i386/i386at/int_init.c39
-rw-r--r--i386/i386at/interrupt.S48
-rw-r--r--i386/i386at/iopl.c287
-rw-r--r--i386/i386at/kd.c2990
-rw-r--r--i386/i386at/kd.h663
-rw-r--r--i386/i386at/kd_event.c560
-rw-r--r--i386/i386at/kd_mouse.c899
-rw-r--r--i386/i386at/kd_queue.c115
-rw-r--r--i386/i386at/kd_queue.h79
-rw-r--r--i386/i386at/kdasm.S145
-rw-r--r--i386/i386at/kdsoft.h201
-rw-r--r--i386/i386at/lpr.c419
-rw-r--r--i386/i386at/lprreg.h33
-rw-r--r--i386/i386at/model_dep.c651
-rw-r--r--i386/i386at/nfd.c1484
-rw-r--r--i386/i386at/nfdreg.h110
-rw-r--r--i386/i386at/nhd.c1430
-rw-r--r--i386/i386at/nhdreg.h120
-rw-r--r--i386/i386at/phys_mem_grab_page.c1
-rw-r--r--i386/i386at/pic_isa.c68
-rw-r--r--i386/i386at/rtc.c237
-rw-r--r--i386/i386at/rtc.h137
362 files changed, 178205 insertions, 0 deletions
diff --git a/i386/i386at/asm_startup.h b/i386/i386at/asm_startup.h
new file mode 100644
index 00000000..5b35293a
--- /dev/null
+++ b/i386/i386at/asm_startup.h
@@ -0,0 +1,42 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Startup code for an i386 on an AT.
+ * Kernel is loaded starting at 1MB.
+ * Protected mode, paging disabled.
+ *
+ * %esp -> boottype
+ * size of extended memory (K)
+ * size of conventional memory (K)
+ * boothowto
+ *
+ */
+
+ popl _boottype+KVTOPHYS /* get boottype */
+ popl _extmem+KVTOPHYS /* extended memory, in K */
+ popl _cnvmem+KVTOPHYS /* conventional memory, in K */
+ popl _boothowto+KVTOPHYS /* boot flags */
+
diff --git a/i386/i386at/autoconf.c b/i386/i386at/autoconf.c
new file mode 100644
index 00000000..50eec98b
--- /dev/null
+++ b/i386/i386at/autoconf.c
@@ -0,0 +1,484 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1992,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#ifdef MACH_KERNEL
+#include <mach_ttd.h>
+#include <mach/std_types.h>
+#else /* MACH_KERNEL */
+#include <cpus.h>
+#include <platforms.h>
+#include <generic.h>
+#include <sys/param.h>
+#include <mach/machine.h>
+#include <machine/cpu.h>
+#endif /* MACH_KERNEL */
+#ifdef LINUX_DEV
+#include <i386/pic.h>
+#endif
+#include <i386/ipl.h>
+#include <chips/busses.h>
+
+/* initialization typecasts */
+#define SPL_FIVE (vm_offset_t)SPL5
+#define SPL_SIX (vm_offset_t)SPL6
+#define SPL_TTY (vm_offset_t)SPLTTY
+
+
+#include <hd.h>
+#if NHD > 0
+extern struct bus_driver hddriver;
+extern int hdintr();
+#endif /* NHD */
+
+#include <fd.h>
+#if NFD > 0
+extern struct bus_driver fddriver;
+extern int fdintr();
+#endif /* NFD */
+
+#include <aha.h>
+#if NAHA > 0
+extern struct bus_driver aha_driver;
+extern int aha_intr();
+#endif /* NAHA */
+
+#include <eaha.h>
+#if NEAHA > 0
+extern struct bus_driver eaha_driver;
+extern int eaha_intr();
+#endif /* NEAHA */
+
+#include <pc586.h>
+#if NPC586 > 0
+extern struct bus_driver pcdriver;
+extern int pc586intr();
+#endif /* NPC586 */
+
+#include <ne.h>
+#if NNE > 0
+extern struct bus_driver nedriver;
+extern int neintr();
+#endif NNE
+
+#include <ns8390.h>
+#if NNS8390 > 0
+extern struct bus_driver ns8390driver;
+extern int ns8390intr();
+#endif /* NNS8390 */
+
+#include <at3c501.h>
+#if NAT3C501 > 0
+extern struct bus_driver at3c501driver;
+extern int at3c501intr();
+#endif /* NAT3C501 */
+
+#include <ul.h>
+#if NUL > 0
+extern struct bus_driver uldriver;
+extern int ulintr();
+#endif
+
+#include <wd.h>
+#if NWD > 0
+extern struct bus_driver wddriver;
+extern int wdintr();
+#endif
+
+#include <hpp.h>
+#if NHPP > 0
+extern struct bus_driver hppdriver;
+extern int hppintr();
+#endif
+
+#include <com.h>
+#if NCOM > 0
+extern struct bus_driver comdriver;
+extern int comintr();
+#endif /* NCOM */
+
+#include <lpr.h>
+#if NLPR > 0
+extern struct bus_driver lprdriver;
+extern int lprintr();
+#endif /* NLPR */
+
+#include <wt.h>
+#if NWT > 0
+extern struct bus_driver wtdriver;
+extern int wtintr();
+#endif /* NWT */
+
+struct bus_ctlr bus_master_init[] = {
+
+/* driver name unit intr address len phys_address
+ adaptor alive flags spl pic */
+
+#ifndef LINUX_DEV
+#if NHD > 0
+ {&hddriver, "hdc", 0, hdintr, 0x1f0, 8, 0x1f0,
+ '?', 0, 0, SPL_FIVE, 14},
+
+ {&hddriver, "hdc", 1, hdintr, 0x170, 8, 0x170,
+ '?', 0, 0, SPL_FIVE, 15},
+#endif /* NHD > 0 */
+
+#if NAHA > 0
+ {&aha_driver, "ahac", 0, aha_intr, 0x330, 4, 0x330,
+ '?', 0, 0, SPL_FIVE, 11},
+
+#if NAHA > 1
+
+ {&aha_driver, "ahac", 1, aha_intr, 0x234, 4, 0x234,
+ '?', 0, 0, SPL_FIVE, 12},
+ {&aha_driver, "ahac", 1, aha_intr, 0x230, 4, 0x230,
+ '?', 0, 0, SPL_FIVE, 12},
+ {&aha_driver, "ahac", 1, aha_intr, 0x134, 4, 0x134,
+ '?', 0, 0, SPL_FIVE, 12},
+ {&aha_driver, "ahac", 1, aha_intr, 0x130, 4, 0x130,
+ '?', 0, 0, SPL_FIVE, 12},
+
+#else
+
+ {&aha_driver, "ahac", 0, aha_intr, 0x334, 4, 0x334,
+ '?', 0, 0, SPL_FIVE, 11},
+ {&aha_driver, "ahac", 0, aha_intr, 0x234, 4, 0x234,
+ '?', 0, 0, SPL_FIVE, 11},
+ {&aha_driver, "ahac", 0, aha_intr, 0x230, 4, 0x230,
+ '?', 0, 0, SPL_FIVE, 11},
+ {&aha_driver, "ahac", 0, aha_intr, 0x134, 4, 0x134,
+ '?', 0, 0, SPL_FIVE, 11},
+ {&aha_driver, "ahac", 0, aha_intr, 0x130, 4, 0x130,
+ '?', 0, 0, SPL_FIVE, 11},
+
+#endif /* NAHA > 1 */
+#endif /* NAHA > 0*/
+
+#if NEAHA > 0
+{&eaha_driver, "eahac", 0, eaha_intr, 0x0000, 4, 0x0000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x1000, 4, 0x1000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x2000, 4, 0x2000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x3000, 4, 0x3000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x4000, 4, 0x4000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x5000, 4, 0x5000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x6000, 4, 0x6000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x7000, 4, 0x7000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x8000, 4, 0x8000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0x9000, 4, 0x9000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0xa000, 4, 0xa000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0xb000, 4, 0xb000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0xc000, 4, 0xc000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0xd000, 4, 0xd000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0xe000, 4, 0xe000,
+ '?', 0, 0, SPL_FIVE, 12},
+{&eaha_driver, "eahac", 0, eaha_intr, 0xf000, 4, 0xf000,
+ '?', 0, 0, SPL_FIVE, 12},
+#endif /* NEAHA > 0 */
+
+#if NFD > 0
+ {&fddriver, "fdc", 0, fdintr, 0x3f2, 6, 0x3f2,
+ '?', 0, 0, SPL_FIVE, 6},
+
+ {&fddriver, "fdc", 1, fdintr, 0x372, 6, 0x372,
+ '?', 0, 0, SPL_FIVE, 10},
+#endif /* NFD > 0 */
+#endif /* ! LINUX_DEV */
+
+ 0
+};
+
+
+struct bus_device bus_device_init[] = {
+
+/* driver name unit intr address am phys_address
+ adaptor alive ctlr slave flags *mi *next sysdep sysdep */
+
+#ifndef LINUX_DEV
+#if NHD > 0
+ {&hddriver, "hd", 0, hdintr, 0x104, 8, 0x1f0,
+ '?', 0, 0, 0, 0, 0, 0, SPL_FIVE, 14},
+ {&hddriver, "hd", 1, hdintr, 0x118, 8, 0x1f0,
+ '?', 0, 0, 1, 0, 0, 0, SPL_FIVE, 14},
+ {&hddriver, "hd", 2, hdintr, 0x104, 8, 0x170, /*??*/
+ '?', 0, 1, 0, 0, 0, 0, SPL_FIVE, 15},
+ {&hddriver, "hd", 3, hdintr, 0x118, 8, 0x170,
+ '?', 0, 1, 1, 0, 0, 0, SPL_FIVE, 15},
+#endif /* NHD > 0 */
+
+#if NAHA > 0
+{ &aha_driver, "rz", 0, 0, 0x0,0, 0, '?', 0, 0, 0, 0, },
+{ &aha_driver, "rz", 1, 0, 0x0,0, 0, '?', 0, 0, 1, 0, },
+{ &aha_driver, "rz", 2, 0, 0x0,0, 0, '?', 0, 0, 2, 0, },
+{ &aha_driver, "rz", 3, 0, 0x0,0, 0, '?', 0, 0, 3, 0, },
+{ &aha_driver, "rz", 4, 0, 0x0,0, 0, '?', 0, 0, 4, 0, },
+{ &aha_driver, "rz", 5, 0, 0x0,0, 0, '?', 0, 0, 5, 0, },
+{ &aha_driver, "rz", 6, 0, 0x0,0, 0, '?', 0, 0, 6, 0, },
+{ &aha_driver, "rz", 7, 0, 0x0,0, 0, '?', 0, 0, 7, 0, },
+
+{ &aha_driver, "tz", 0, 0, 0x0,0, 0, '?', 0, 0, 0, 0, },
+{ &aha_driver, "tz", 1, 0, 0x0,0, 0, '?', 0, 0, 1, 0, },
+{ &aha_driver, "tz", 2, 0, 0x0,0, 0, '?', 0, 0, 2, 0, },
+{ &aha_driver, "tz", 3, 0, 0x0,0, 0, '?', 0, 0, 3, 0, },
+{ &aha_driver, "tz", 4, 0, 0x0,0, 0, '?', 0, 0, 4, 0, },
+{ &aha_driver, "tz", 5, 0, 0x0,0, 0, '?', 0, 0, 5, 0, },
+{ &aha_driver, "tz", 6, 0, 0x0,0, 0, '?', 0, 0, 6, 0, },
+{ &aha_driver, "tz", 7, 0, 0x0,0, 0, '?', 0, 0, 7, 0, },
+
+#if NAHA > 1
+
+{ &aha_driver, "rz", 8, 0, 0x0,0, 0, '?', 0, 1, 0, 0, },
+{ &aha_driver, "rz", 9, 0, 0x0,0, 0, '?', 0, 1, 1, 0, },
+{ &aha_driver, "rz", 10, 0, 0x0,0, 0, '?', 0, 1, 2, 0, },
+{ &aha_driver, "rz", 11, 0, 0x0,0, 0, '?', 0, 1, 3, 0, },
+{ &aha_driver, "rz", 12, 0, 0x0,0, 0, '?', 0, 1, 4, 0, },
+{ &aha_driver, "rz", 13, 0, 0x0,0, 0, '?', 0, 1, 5, 0, },
+{ &aha_driver, "rz", 14, 0, 0x0,0, 0, '?', 0, 1, 6, 0, },
+{ &aha_driver, "rz", 15, 0, 0x0,0, 0, '?', 0, 1, 7, 0, },
+
+{ &aha_driver, "tz", 8, 0, 0x0,0, 0, '?', 0, 1, 0, 0, },
+{ &aha_driver, "tz", 9, 0, 0x0,0, 0, '?', 0, 1, 1, 0, },
+{ &aha_driver, "tz", 10, 0, 0x0,0, 0, '?', 0, 1, 2, 0, },
+{ &aha_driver, "tz", 11, 0, 0x0,0, 0, '?', 0, 1, 3, 0, },
+{ &aha_driver, "tz", 12, 0, 0x0,0, 0, '?', 0, 1, 4, 0, },
+{ &aha_driver, "tz", 13, 0, 0x0,0, 0, '?', 0, 1, 5, 0, },
+{ &aha_driver, "tz", 14, 0, 0x0,0, 0, '?', 0, 1, 6, 0, },
+{ &aha_driver, "tz", 15, 0, 0x0,0, 0, '?', 0, 1, 7, 0, },
+#endif /* NAHA > 1 */
+#endif /* NAHA > 0 */
+
+#if NEAHA > 0
+{ &eaha_driver, "rz", 0, 0, 0x0,0, 0, '?', 0, 0, 0, 0, },
+{ &eaha_driver, "rz", 1, 0, 0x0,0, 0, '?', 0, 0, 1, 0, },
+{ &eaha_driver, "rz", 2, 0, 0x0,0, 0, '?', 0, 0, 2, 0, },
+{ &eaha_driver, "rz", 3, 0, 0x0,0, 0, '?', 0, 0, 3, 0, },
+{ &eaha_driver, "rz", 4, 0, 0x0,0, 0, '?', 0, 0, 4, 0, },
+{ &eaha_driver, "rz", 5, 0, 0x0,0, 0, '?', 0, 0, 5, 0, },
+{ &eaha_driver, "rz", 6, 0, 0x0,0, 0, '?', 0, 0, 6, 0, },
+{ &eaha_driver, "rz", 7, 0, 0x0,0, 0, '?', 0, 0, 7, 0, },
+
+{ &eaha_driver, "tz", 0, 0, 0x0,0, 0, '?', 0, 0, 0, 0, },
+{ &eaha_driver, "tz", 1, 0, 0x0,0, 0, '?', 0, 0, 1, 0, },
+{ &eaha_driver, "tz", 2, 0, 0x0,0, 0, '?', 0, 0, 2, 0, },
+{ &eaha_driver, "tz", 3, 0, 0x0,0, 0, '?', 0, 0, 3, 0, },
+{ &eaha_driver, "tz", 4, 0, 0x0,0, 0, '?', 0, 0, 4, 0, },
+{ &eaha_driver, "tz", 5, 0, 0x0,0, 0, '?', 0, 0, 5, 0, },
+{ &eaha_driver, "tz", 6, 0, 0x0,0, 0, '?', 0, 0, 6, 0, },
+{ &eaha_driver, "tz", 7, 0, 0x0,0, 0, '?', 0, 0, 7, 0, },
+#endif /* NEAHA > 0*/
+
+#if NFD > 0
+ {&fddriver, "fd", 0, fdintr, 0x3f2, 6, 0x3f2,
+ '?', 0, 0, 0, 0, 0, 0, SPL_FIVE, 6},
+ {&fddriver, "fd", 1, fdintr, 0x3f2, 6, 0x3f2,
+ '?', 0, 0, 1, 0, 0, 0, SPL_FIVE, 6},
+
+ {&fddriver, "fd", 2, fdintr, 0x372, 6, 0x372,
+ '?', 0, 1, 0, 0, 0, 0, SPL_FIVE, 10},
+ {&fddriver, "fd", 3, fdintr, 0x372, 6, 0x372,
+ '?', 0, 1, 1, 0, 0, 0, SPL_FIVE, 10},
+#endif /* NFD > 0 */
+
+#if NPC586 > 0
+ /* For MACH Default */
+ {&pcdriver, "pc", 0, pc586intr, 0xd0000, 0, 0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_FIVE, 9},
+ /* For Factory Default */
+ {&pcdriver, "pc", 0, pc586intr, 0xc0000, 0, 0xc0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_FIVE, 5},
+ /* For what Intel Ships */
+ {&pcdriver, "pc", 0, pc586intr, 0xf00000, 0, 0xf00000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_FIVE, 12},
+#endif /* NPC586 > 0 */
+
+#if NNE > 0
+{&nedriver, "ne", 0, neintr, 0x280,0x4000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 5},
+{&nedriver, "ne", 1, neintr, 0x300,0x4000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 10},
+#endif NNE > 0
+
+#if NNS8390 > 0
+ /* "wd" and "el" */
+ {&ns8390driver, "wd", 0, ns8390intr, 0x280,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 9},
+ {&ns8390driver, "wd", 0, ns8390intr, 0x2a0,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 9},
+ {&ns8390driver, "wd", 0, ns8390intr, 0x2e0,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 5},
+ {&ns8390driver, "wd", 0, ns8390intr, 0x300,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 5},
+ {&ns8390driver, "wd", 0, ns8390intr, 0x250,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 5},
+ {&ns8390driver, "wd", 0, ns8390intr, 0x350,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 5},
+ {&ns8390driver, "wd", 0, ns8390intr, 0x240,0x2000,0xd0000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 11},
+ {&ns8390driver, "wd", 1, ns8390intr, 0x340,0x2000,0xe8000,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 15},
+#endif /* NNS8390 > 0 */
+
+#if NAT3C501 > 0
+ {&at3c501driver, "et", 0, at3c501intr, 0x300, 0,0x300,
+ '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 9},
+#endif /* NAT3C501 > 0 */
+
+#if NUL > 0
+ {&uldriver, "ul", 0, ulintr, 0, 0, 0, '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 0},
+ {&uldriver, "ul", 1, ulintr, 0, 0, 0, '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 0},
+#endif
+
+#if NWD > 0
+ {&wddriver, "wd", 0, wdintr, 0, 0, 0, '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 9},
+ {&wddriver, "wd", 1, wdintr, 0, 0, 0, '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 15},
+#endif
+
+#if NHPP > 0
+ {&hppdriver, "hpp", 0, hppintr, 0, 0, 0, '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 0},
+ {&hppdriver, "hpp", 1, hppintr, 0, 0, 0, '?', 0, -1, -1, 0, 0, 0, SPL_SIX, 0},
+#endif
+#endif /* ! LINUX_DEV */
+
+#if NCOM > 0
+ {&comdriver, "com", 0, comintr, 0x3f8, 8, 0x3f8,
+ '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 4},
+ {&comdriver, "com", 1, comintr, 0x2f8, 8, 0x2f8,
+ '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 3},
+ {&comdriver, "com", 2, comintr, 0x3e8, 8, 0x3e8,
+ '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 5},
+#endif /* NCOM > 0 */
+
+#ifndef LINUX_DEV
+#if NLPR > 0
+ {&lprdriver, "lpr", 0, lprintr, 0x378, 3, 0x378,
+ '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7},
+ {&lprdriver, "lpr", 0, lprintr, 0x278, 3, 0x278,
+ '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7},
+ {&lprdriver, "lpr", 0, lprintr, 0x3bc, 3, 0x3bc,
+ '?', 0, -1, -1, 0, 0, 0, SPL_TTY, 7},
+#endif /* NLPR > 0 */
+
+#if NWT > 0
+ {&wtdriver, "wt", 0, wtintr, 0x300, 2, 0x300,
+ '?', 0, -1, -1, 0, 0, 0, SPL_FIVE, 5},
+ {&wtdriver, "wt", 0, wtintr, 0x288, 2, 0x288,
+ '?', 0, -1, -1, 0, 0, 0, SPL_FIVE, 5},
+ {&wtdriver, "wt", 0, wtintr, 0x388, 2, 0x388,
+ '?', 0, -1, -1, 0, 0, 0, SPL_FIVE, 5},
+#endif /* NWT > 0 */
+#endif /* ! LINUX_DEV */
+
+ 0
+};
+
+/*
+ * probeio:
+ *
+ * Probe and subsequently attach devices out on the AT bus.
+ *
+ *
+ */
+void probeio(void)
+{
+ register struct bus_device *device;
+ register struct bus_ctlr *master;
+ int i = 0;
+
+ for (master = bus_master_init; master->driver; master++)
+ {
+ if (configure_bus_master(master->name, master->address,
+ master->phys_address, i, "atbus"))
+ i++;
+ }
+
+ for (device = bus_device_init; device->driver; device++)
+ {
+ /* ignore what we (should) have found already */
+ if (device->alive || device->ctlr >= 0)
+ continue;
+ if (configure_bus_device(device->name, device->address,
+ device->phys_address, i, "atbus"))
+ i++;
+ }
+
+#if MACH_TTD
+ /*
+ * Initialize Remote kernel debugger.
+ */
+ ttd_init();
+#endif /* MACH_TTD */
+}
+
+void take_dev_irq(
+ struct bus_device *dev)
+{
+ int pic = (int)dev->sysdep1;
+
+ if (intpri[pic] == 0) {
+ iunit[pic] = dev->unit;
+ ivect[pic] = dev->intr;
+ intpri[pic] = (int)dev->sysdep;
+ form_pic_mask();
+ } else {
+ printf("The device below will clobber IRQ %d.\n", pic);
+ printf("You have two devices at the same IRQ.\n");
+ printf("This won't work. Reconfigure your hardware and try again.\n");
+ printf("%s%d: port = %x, spl = %d, pic = %d.\n",
+ dev->name, dev->unit, dev->address,
+ dev->sysdep, dev->sysdep1);
+ while (1);
+ }
+
+}
+
+void take_ctlr_irq(
+ struct bus_ctlr *ctlr)
+{
+ int pic = ctlr->sysdep1;
+ if (intpri[pic] == 0) {
+ iunit[pic] = ctlr->unit;
+ ivect[pic] = ctlr->intr;
+ intpri[pic] = (int)ctlr->sysdep;
+ form_pic_mask();
+ } else {
+ printf("The device below will clobber IRQ %d.\n", pic);
+ printf("You have two devices at the same IRQ. This won't work.\n");
+ printf("Reconfigure your hardware and try again.\n");
+ while (1);
+ }
+}
diff --git a/i386/i386at/blit.c b/i386/i386at/blit.c
new file mode 100644
index 00000000..e97b0eea
--- /dev/null
+++ b/i386/i386at/blit.c
@@ -0,0 +1,948 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* **********************************************************************
+ File: blit.c
+ Description: Device Driver for Bell Tech Blit card
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifdef MACH_KERNEL
+#include <sys/types.h>
+#include <device/errno.h>
+#else MACH_KERNEL
+#include <sys/types.h>
+#include <sys/errno.h>
+#include <sys/param.h>
+#include <sys/dir.h>
+#include <sys/signal.h>
+#include <sys/user.h>
+#endif MACH_KERNEL
+#include <vm/vm_kern.h>
+#include <mach/vm_param.h>
+#include <machine/machspl.h>
+
+#include <i386at/blitreg.h>
+#include <i386at/blitvar.h>
+#include <i386at/blituser.h>
+#include <i386at/kd.h>
+#include <i386at/kdsoft.h>
+
+#include <blit.h>
+
+
+/*
+ * This driver really only supports 1 card, though parts of it were
+ * written to support multiple cards. If you want to finish the job
+ * and really support multiple cards, then you'll have to:
+ *
+ * (1) make sure that driver functions pass around a pointer telling
+ * which card they're talking about.
+ *
+ * (2) coordinate things with the kd driver, so that one card is used
+ * for the console and the other is simply an additional display.
+ */
+#define MAXBLITS 1
+
+#if NBLIT > MAXBLITS
+/* oh, no, you don't want to do this...; */
+
+#else
+#if NBLIT > 0
+
+#define AUTOINIT 0
+
+ /*
+ * Forward Declarations
+ */
+static tiledesc();
+static loadall();
+
+#if AUTOINIT
+int blitattach(), blitprobe();
+#endif
+
+int blitioctl(), blitopen(), blitclose(), blitmmap();
+
+
+static void setstatus();
+#define CARD_RESET 0
+#define CARD_MAPPED 1
+#define CARD_MAYBE_PRESENT 2
+#define CARD_PRESENT 3
+#define BIU_INIT 4
+#define UNUSED1 5
+#define DP_INIT 6
+#define UNUSED2 7
+
+
+#if AUTOINIT
+struct mb_device *blitinfo[NBLIT];
+
+struct mb_driver blitdriver = {
+ blitprobe,
+ 0, /* slave routine */
+ blitattach,
+ 0, 0, 0, /* go, done, intr routines */
+ BLIT_MAPPED_SIZE,
+ "blit", blitinfo, /* device info */
+ 0, 0, /* no controller */
+ 0 /* no flags */
+ /* rest zeros */
+};
+#endif /* AUTOINIT */
+
+
+/*
+ * Per-card bookkeeping information for driver.
+ *
+ * "scrstrip" and "dpctlregs" point to data areas that are passed to
+ * the Display Processor. They are allocated out of the spare
+ * graphics memory. "scrstrip" is used to describe an entire screen.
+ * "dpctlregs" contains assorted parameters for the display
+ * controller.
+ *
+ * "firstfree" is an offset into the graphics memory. Memory starting
+ * there can be allocated by users.
+ */
+
+struct blitsoft {
+ struct blitdev *blt; /* ptr to mapped card */
+ caddr_t physaddr; /* start of mapped card */
+ boolean_t open; /* is device open? */
+ struct screen_descrip *scrstrip;
+ DPCONTROLBLK *dpctlregs;
+ int firstfree;
+} blitsoft[NBLIT];
+
+
+/*
+ * The following array contains the initial settings for
+ * the Display Processor Control Block Registers.
+ * The video timing signals in this array are for the
+ * Bell Technologies Blit Express running in 1664 x 1200 x 1 mode.
+ * Please treat as read-only.
+ */
+
+DPCONTROLBLK blit_mparm = {
+ DP_DSP_ON, /* video status */
+ 0x00ff, /* interrupt mask - all disabled */
+ 0x0010, /* trip point */
+ 0x00ff, /* frame interrupt interval */
+ 0x0000, /* reserved */
+ CRTM_NONINTER | CRTM_SUPHIGH_SPEED, /* CRT controller mode */
+ 41, /* horizontal synch stop */
+ 57, /* horiz field start */
+ 265, /* horiz field stop */
+ 265, /* line length */
+ 15, /* vert synch stop */
+ 43, /* vert field start */
+ 1243, /* vert field stop */
+ 1244, /* frame length */
+ 0x0000, 0x0000, /* descriptor pointer */
+ 0x0000, /* reserved */
+ 0x0101, /* x, y zoom factors */
+ 0x0000, /* FldColor */
+ 0x00ff, /* BdrColor */
+ 0x0000, /* 1Bpp Pad */
+ 0x0000, /* 2Bpp Pad */
+ 0x0000, /* 4Bpp Pad */
+ DP_CURSOR_CROSSHAIR, /* cursor style & mode */
+ 0x00A0, 0x0050, /* cursor x & y loc. */
+ /* cursor pattern */
+ 0xfffe, 0xfffc, 0xc018, 0xc030, 0xc060, 0xc0c0, 0xc0c0, 0xc060,
+ 0xc430, 0xce18, 0xdb0c, 0xf186, 0xe0c3, 0xc066, 0x803c, 0x0018
+};
+
+void blitreboot();
+
+/***********
+ *
+ * Initialization.
+ *
+ ***********/
+
+
+/*
+ * Probe - is the board there?
+ *
+ * in: reg = start of mapped Blit memory.
+ *
+ * out: returns size of mapped Blit memory if the board is present,
+ * 0 otherwise.
+ *
+ * effects: if the board is present, it is reset and left visible in
+ * Unix mode.
+ */
+
+#if AUTOINIT
+/*ARGSUSED*/
+int
+blitprobe(reg, unit)
+ caddr_t reg;
+ int unit;
+{
+ struct blitdev *blt = (struct blitdev *)reg;
+
+ if (blit_present())
+ return(BLIT_MAPPED_SIZE); /* go */
+ else
+ return(0); /* no-go */
+}
+#endif /* AUTOINIT */
+
+
+/*
+ * Temporary initialization routine. This will go away when we have
+ * autoconfig.
+ */
+
+blitinit()
+{
+ if (!blit_present())
+ return;
+
+ blit_init();
+}
+
+
+/*
+ * Allocate needed objects from Blit's memory.
+ */
+blit_memory_init(bs)
+ struct blitsoft *bs;
+{
+ struct blitdev *blt = bs->blt;
+ struct blitmem *bm = (struct blitmem *)blt->graphmem;
+ u_char *p = bm->spare;
+
+ if ((int)p % 2 == 1)
+ ++p;
+
+ bs->scrstrip = (struct screen_descrip *)p;
+ p += sizeof(struct screen_descrip);
+ if ((int)p % 2 == 1)
+ ++p;
+
+ bs->dpctlregs = (DPCONTROLBLK *)p;
+ p += sizeof(DPCONTROLBLK);
+ if ((int)p % 2 == 1)
+ ++p;
+
+ /*
+ * Note: if you use the 786 graphics processor for character
+ * processing, you should copy the font from the ROM into
+ * graphics memory and change font_start to point to it.
+ * Otherwise, the 786 will have problems accessing the font.
+ */
+
+ bs->firstfree = p - blt->graphmem;
+}
+
+
+/*
+ * Reset the Blit board and leave it visible.
+ */
+
+blit_reset_board()
+{
+ union blit_config_reg config;
+
+ config.byte = inb(BLIT_CONFIG_ADDR);
+ config.reg.reset = 1;
+ outb(BLIT_CONFIG_ADDR, config.byte);
+ config.reg.reset = 0;
+ config.reg.mode = BLIT_UNIX_MODE;
+ config.reg.invisible = BLIT_VISIBLE;
+ outb(BLIT_CONFIG_ADDR, config.byte);
+ setstatus(CARD_RESET);
+}
+
+
+#if AUTOINIT
+/*
+ * Attach - finish initialization by setting up the 786.
+ */
+
+blitattach(md)
+ struct mb_device *md;
+{
+ struct blitdev *blt = (struct blitdev *)md->md_addr;
+
+ blit_init(xyz);
+}
+#endif /* AUTOINIT */
+
+
+/*
+ * Initialize Bus Interface Unit.
+ */
+
+init_biu(blt)
+ struct blitdev *blt;
+{
+ WRITEREG8(blt, INTER_RELOC, 0);
+ WRITEREG8(blt, BIU_CONTROL, BIU_16BIT);
+
+ /* WRITEREG16(blt, DRAM_REFRESH, 0x003f); */
+ WRITEREG16(blt, DRAM_REFRESH, 0x0018); /* refresh rate */
+ WRITEREG16(blt, DRAM_CONTROL,
+ MEMROWS1 | FASTPG_INTERLV | HEIGHT_256K);
+ WRITEREG16(blt, DP_PRIORITY, (7 << 3) | 7); /* max pri */
+ WRITEREG16(blt, GP_PRIORITY, (1 << 3) | 1); /* almost min pri */
+ WRITEREG16(blt, EXT_PRIORITY, 5 << 3);
+
+ /* now freeze the settings */
+ WRITEREG16(blt, BIU_CONTROL, BIU_16BIT | BIU_WP1);
+
+ /* Put graphics processor into Poll state. */
+ WRITEREG16(blt, GP_OPCODE_REG, (OP_LINK|GECL));
+}
+
+
+/*
+ * Initialize the Display Processor.
+ * XXX - assumes only 1 card is installed, assumes monochrome display.
+ */
+
+init_dp(bs)
+ struct blitsoft *bs;
+{
+ struct blitdev *blt = bs->blt;
+ struct blitmem *bm = (struct blitmem *)blt->graphmem;
+
+ /*
+ * Set up strip header and tile descriptor for the whole
+ * screen. It's not clear why the C bit should be turned on,
+ * but it seems to get rid of the nasty flickering you can get
+ * by positioning an xterm window along the top of the screen.
+ */
+ bs->scrstrip->strip.lines = BLIT_MONOHEIGHT - 1;
+ bs->scrstrip->strip.linkl = 0;
+ bs->scrstrip->strip.linkh = 0;
+ bs->scrstrip->strip.tiles = DP_C_BIT | (1 - 1);
+ tiledesc(&bs->scrstrip->tile,
+ 0, 0, /* x, y */
+ BLIT_MONOWIDTH, /* width of strip */
+ BLIT_MONOWIDTH, /* width of bitmap */
+ VM_TO_ADDR786(bm->fb.mono_fb, blt), /* the actual bitmap */
+ 1); /* bits per pixel */
+
+ /* Copy into DP register block. */
+ *(bs->dpctlregs) = blit_mparm;
+ bs->dpctlregs->descl = DP_ADDRLOW(VM_TO_ADDR786(bs->scrstrip, blt));
+ bs->dpctlregs->desch = DP_ADDRHIGH(VM_TO_ADDR786(bs->scrstrip, blt));
+
+ /* Load the DP with the register block */
+ loadall(blt, bs->dpctlregs);
+}
+
+
+/*
+ * Fill in a tile descriptor.
+ */
+
+static
+tiledesc(tile, x, y, w, ww, adx, bpp)
+ TILEDESC *tile; /* pointer to tile descriptor */
+ int x; /* starting x in bitmap */
+ int y; /* starting y in bitmap */
+ int w; /* width of strip (in bits_) */
+ int ww; /* actual width of bitmap (bits) */
+ addr786_t adx; /* start of bitmap */
+ int bpp; /* bits per pixel */
+{
+ u_short bm_width;
+ short rghtp;
+ short adr_left, adr_right;
+ addr786_t bmstadr;
+ u_short start_stop_bit;
+
+ bm_width = 2 * (((ww + 1) * bpp) / 16);
+ rghtp = x + w - 1;
+ adr_left = ((x * bpp) / 16) * 2;
+ adr_right = ((rghtp * bpp) / 16) * 2;
+ bmstadr = (ww * y) + adr_left + (int)adx;
+ start_stop_bit = ((((16 - 1) - ((x * bpp) % 16)) << 4) +
+ ((16 - ((rghtp + 1) * bpp) % 16) % 16) +
+ (bpp << 8));
+
+ tile->bitmapw = bm_width;
+ tile->meml = DP_ADDRLOW(bmstadr);
+ tile->memh = DP_ADDRHIGH(bmstadr);
+ tile->bppss = start_stop_bit;
+ tile->fetchcnt = adr_right - adr_left;
+ tile->flags = 0;
+}
+
+
+/*
+ * Cause the Display Processor to load its Control Registers from
+ * "vm_addr".
+ */
+
+static
+loadall(blt, vm_addr)
+struct blitdev *blt;
+DPCONTROLBLK *vm_addr;
+{
+ addr786_t blit_addr = VM_TO_ADDR786(vm_addr, blt);
+ int i;
+
+ /* set up dp address */
+ WRITEREG16(blt, DP_PARM1_REG, DP_ADDRLOW(blit_addr));
+ WRITEREG16(blt, DP_PARM2_REG, DP_ADDRHIGH(blit_addr));
+
+ /* set blanking video */
+ WRITEREG16(blt, DEF_VIDEO_REG, 0);
+
+ /* load opcode to start dp */
+ WRITEREG16(blt, DP_OPCODE_REG, DP_LOADALL);
+
+ /* wait for acceptance */
+ for (i = 0; i < DP_RDYTIMEOUT; ++i)
+ if (READREG(blt, DP_OPCODE_REG) & DECL)
+ break;
+
+ if (i >= DP_RDYTIMEOUT) {
+ printf("Blit Display Processor timeout (loading registers)\n");
+ hang:
+ goto hang;
+ }
+
+#ifdef notdef
+ /* wait for acceptance */
+ CDELAY((READREG(blt, DP_OPCODE_REG) & DECL) != 0, DP_RDYTIMEOUT);
+ if ((READREG(blt, DP_OPCODE_REG) & DECL) == 0) {
+ printf("Blit Display Processor timeout (loading registers)\n");
+ hang:
+ goto hang;
+ }
+#endif /* notdef */
+}
+
+
+/*
+ * blit_present: returns YES if Blit is present. For the first call,
+ * the hardware is probed. After that, a flag is used.
+ * Sets blitsoft[0].blt and blitsoft[0].physaddr.
+ */
+
+#define TEST_BYTE 0xa5 /* should not be all 0's or 1's */
+
+boolean_t
+blit_present()
+{
+ static boolean_t present = FALSE;
+ static boolean_t initialized = FALSE;
+ struct blitdev *blt;
+ boolean_t blit_rom_ok();
+ struct blitdev *mapblit();
+ void freeblit();
+
+ /*
+ * We set "initialized" early on so that if the Blit init. code
+ * fails, kdb will still be able to use the EGA or VGA display
+ * (if present).
+ */
+ if (initialized)
+ return(present);
+ initialized = TRUE;
+
+ blit_reset_board();
+ blt = mapblit((caddr_t)BLIT_BASE_ADDR, BLIT_MAPPED_SIZE);
+ setstatus(CARD_MAPPED);
+ if (blt == NULL)
+ panic("blit: can't map display");
+ blt->graphmem[0] = TEST_BYTE;
+ present = FALSE;
+ if (blt->graphmem[0] == TEST_BYTE) {
+ setstatus(CARD_MAYBE_PRESENT);
+ present = blit_rom_ok(blt);
+ }
+ if (present) {
+ blitsoft[0].blt = blt;
+ blitsoft[0].physaddr = (caddr_t)BLIT_BASE_ADDR;
+ setstatus(CARD_PRESENT);
+ }
+ else
+ freeblit((vm_offset_t)blt, BLIT_MAPPED_SIZE);
+ return(present);
+}
+
+#undef TEST_BYTE
+
+
+/*
+ * mapblit: map the card into kernel vm and return the (virtual)
+ * address.
+ */
+struct blitdev *
+mapblit(physaddr, length)
+caddr_t physaddr; /* start of card */
+int length; /* num bytes to map */
+{
+ vm_offset_t vmaddr;
+#ifdef MACH_KERNEL
+ vm_offset_t io_map();
+#else MACH_KERNEL
+ vm_offset_t pmap_map_bd();
+#endif MACH_KERNEL
+
+ if (physaddr != (caddr_t)trunc_page(physaddr))
+ panic("Blit card not on page boundary");
+
+#ifdef MACH_KERNEL
+ vmaddr = io_map((vm_offset_t)physaddr, length);
+ if (vmaddr == 0)
+#else MACH_KERNEL
+ if (kmem_alloc_pageable(kernel_map,
+ &vmaddr, round_page(BLIT_MAPPED_SIZE))
+ != KERN_SUCCESS)
+#endif MACH_KERNEL
+ panic("can't alloc VM for Blit card");
+
+ (void)pmap_map_bd(vmaddr, (vm_offset_t)physaddr,
+ (vm_offset_t)physaddr+length,
+ VM_PROT_READ | VM_PROT_WRITE);
+ return((struct blitdev *)vmaddr);
+}
+
+
+/*
+ * freeblit: free card from memory.
+ * XXX - currently a no-op.
+ */
+void
+freeblit(va, length)
+vm_offset_t va; /* virt addr start of card */
+int length;
+{
+}
+
+
+/*
+ * blit_init: initialize globals & hardware, and set cursor. Could be
+ * called twice, once as part of kd initialization and once as part of
+ * blit initialization. Should not be called before blit_present() is
+ * called.
+ */
+
+void
+blit_init()
+{
+ static boolean_t initialized = FALSE;
+ struct blitmem *gmem; /* start of blit graphics memory */
+ int card;
+ void getfontinfo(), clear_blit();
+
+ if (initialized)
+ return;
+
+ for (card = 0; card < NBLIT; ++card) {
+ if (card > 0) {
+ blitsoft[card].blt = NULL;
+ blitsoft[card].physaddr = NULL;
+ }
+ blitsoft[card].open = FALSE;
+ blitsoft[card].scrstrip = NULL;
+ blitsoft[card].dpctlregs = NULL;
+ blitsoft[card].firstfree = 0;
+ }
+
+ /*
+ * blit_memory_init allocates memory used by the Display Processor,
+ * so it comes before the call to init_dp. blit_memory_init
+ * potentially copies the font from ROM into the graphics memory,
+ * so it comes after the call to getfontinfo.
+ */
+ getfontinfo(blitsoft[0].blt); /* get info & check assumptions */
+ blit_memory_init(&blitsoft[0]);
+
+ /* init 786 */
+ init_biu(blitsoft[0].blt);
+ setstatus(BIU_INIT);
+ init_dp(&blitsoft[0]);
+ setstatus(DP_INIT);
+
+ gmem = (struct blitmem *)blitsoft[0].blt->graphmem;
+ vid_start = gmem->fb.mono_fb;
+ kd_lines = 25;
+ kd_cols = 80;
+ kd_attr = KA_NORMAL;
+
+ /*
+ * Use generic bitmap routines, no 786 assist (see
+ * blit_memory_init).
+ */
+ kd_dput = bmpput;
+ kd_dmvup = bmpmvup;
+ kd_dmvdown = bmpmvdown;
+ kd_dclear = bmpclear;
+ kd_dsetcursor = bmpsetcursor;
+ kd_dreset = blitreboot;
+
+ clear_blit(blitsoft[0].blt);
+ (*kd_dsetcursor)(0);
+
+ initialized = TRUE;
+}
+
+
+/*
+ * blit_rom_ok: make sure we're looking at the ROM for a monochrome
+ * Blit.
+ */
+
+boolean_t
+blit_rom_ok(blt)
+ struct blitdev *blt;
+{
+ short magic;
+ short bpp;
+
+ magic = READROM(blt->eprom, EP_MAGIC1);
+ if (magic != EP_MAGIC1_VAL) {
+#ifdef notdef
+ printf("blit: magic1 bad (0x%x)\n", magic);
+#endif
+ return(FALSE);
+ }
+ magic = READROM(blt->eprom, EP_MAGIC2);
+ if (magic != EP_MAGIC2_VAL) {
+#ifdef notdef
+ printf("blit: magic2 bad (0x%x)\n", magic);
+#endif
+ return(FALSE);
+ }
+ bpp = READROM(blt->eprom, EP_BPP);
+ if (bpp != 1) {
+#ifdef notdef
+ printf("blit: not monochrome board (bpp = 0x%x)\n", bpp);
+#endif
+ return(FALSE);
+ }
+
+ return(TRUE);
+}
+
+
+/*
+ * getfontinfo: get information about the font and make sure that
+ * our simplifying assumptions are valid.
+ */
+
+void
+getfontinfo(blt)
+ struct blitdev *blt;
+{
+ u_char *rom = blt->eprom;
+ short fontoffset;
+ short pick_cursor_height();
+
+ fb_width = BLIT_MONOWIDTH;
+ fb_height = BLIT_MONOHEIGHT;
+ chars_in_font = READROM(rom, EP_NUMCHARS);
+ char_width = READROM(rom, EP_CHARWIDTH);
+ char_height = READROM(rom, EP_CHARHEIGHT);
+ fontoffset = READROM(rom, EP_FONTSTART);
+ xstart = READROM(rom, EP_XSTART);
+ ystart = READROM(rom, EP_YSTART);
+ char_black = BLIT_BLACK_BYTE;
+ char_white = BLIT_WHITE_BYTE;
+
+ font_start = rom + fontoffset;
+
+ /*
+ * Check byte-alignment assumption.
+ * XXX - does it do any good to panic when initializing the
+ * console driver?
+ */
+ if (char_width % 8 != 0)
+ panic("blit: char width not integral num of bytes");
+ if (xstart % 8 != 0) {
+ /* move it to a more convenient location */
+ printf("blit: console corner moved.\n");
+ xstart = 8 * (xstart/8);
+ }
+
+ cursor_height = pick_cursor_height();
+ char_byte_width = char_width / 8;
+ fb_byte_width = BLIT_MONOWIDTH / 8;
+ font_byte_width = char_byte_width * chars_in_font;
+}
+
+
+/*
+ * pick_cursor_height: pick a size for the cursor, based on the font
+ * size.
+ */
+
+short
+pick_cursor_height()
+{
+ int scl_avail; /* scan lines available for console */
+ int scl_per_line; /* scan lines per console line */
+
+ /*
+ * scan lines avail. = total lines - top margin;
+ * no bottom margin (XXX).
+ */
+ scl_avail = BLIT_MONOHEIGHT - ystart;
+
+ scl_per_line = scl_avail / kd_lines;
+ if (scl_per_line < char_height)
+ return(1);
+ else
+ return(scl_per_line - char_height);
+}
+
+
+/*
+ * setstatus: Give a status indication to the user. Ideally, we'd
+ * just set the 3 user-controlled LED's. Unfortunately, that doesn't
+ * seem to work. So, we ring the bell.
+ */
+
+static void
+setstatus(val)
+ int val;
+{
+ union blit_diag_reg diag;
+
+ diag.byte = inb(BLIT_DIAG_ADDR);
+ diag.reg.led0 = (val & 1) ? BLIT_LED_ON : BLIT_LED_OFF;
+ diag.reg.led1 = (val & 2) ? BLIT_LED_ON : BLIT_LED_OFF;
+ diag.reg.led2 = (val & 4) ? BLIT_LED_ON : BLIT_LED_OFF;
+ outb(BLIT_DIAG_ADDR, diag.byte);
+
+#ifdef DEBUG
+ for (val &= 7; val > 0; val--) {
+ feep();
+ pause();
+ }
+ for (val = 0; val < 10; val++) {
+ pause();
+ }
+#endif
+}
+
+
+
+/***********
+ *
+ * Other (non-initialization) routines.
+ *
+ ***********/
+
+
+/*
+ * Open - Verify that minor device is OK and not in use, then clear
+ * the screen.
+ */
+
+/*ARGSUSED*/
+int
+blitopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ void clear_blit();
+ int which = minor(dev);
+
+ if (!blit_present() || which >= NBLIT)
+ return(ENXIO);
+ if (blitsoft[which].open)
+ return(EBUSY);
+
+ clear_blit(blitsoft[which].blt);
+ blitsoft[which].open = TRUE;
+ return(0); /* ok */
+}
+
+
+/*
+ * Close - free any kernel memory structures that were allocated while
+ * the device was open (currently none).
+ */
+
+/*ARGSUSED*/
+blitclose(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ int which = minor(dev);
+
+ if (!blitsoft[which].open)
+ panic("blit: closing not-open device??");
+ blitsoft[which].open = FALSE;
+}
+
+
+/*
+ * Mmap.
+ */
+
+/*ARGSUSED*/
+int
+blitmmap(dev, off, prot)
+ dev_t dev;
+ off_t off;
+ int prot;
+{
+ if ((u_int) off >= BLIT_MAPPED_SIZE)
+ return(-1);
+
+ /* Get page frame number for the page to be mapped. */
+ return(i386_btop(blitsoft[minor(dev)].physaddr + off));
+}
+
+
+/*
+ * Ioctl.
+ */
+
+#ifdef MACH_KERNEL
+io_return_t blit_get_stat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int *data; /* pointer to OUT array */
+ unsigned int *count; /* OUT */
+{
+ int which = minor(dev);
+
+ switch (flavor) {
+ case BLIT_1ST_UNUSED:
+ if (*count < 1)
+ return (D_INVALID_OPERATION);
+ *data = blitsoft[which].firstfree;
+ *count = 1;
+ break;
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+#else MACH_KERNEL
+/*ARGSUSED*/
+int
+blitioctl(dev, cmd, data, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ int which = minor(dev);
+ int err = 0;
+
+ switch (cmd) {
+ case BLIT_1ST_UNUSED:
+ *(int *)data = blitsoft[which].firstfree;
+ break;
+ default:
+ err = ENOTTY;
+ }
+
+ return(err);
+}
+#endif MACH_KERNEL
+
+/*
+ * clear_blit: clear blit's screen.
+ */
+
+void
+clear_blit(blt)
+ struct blitdev *blt;
+{
+ (*kd_dclear)(0, kd_lines*kd_cols, KA_NORMAL);
+}
+
+/*
+ * Put the board into DOS mode in preparation for rebooting.
+ */
+
+void
+blitreboot()
+{
+ union blit_config_reg config;
+
+ config.byte = inb(BLIT_CONFIG_ADDR);
+ config.reg.mode = BLIT_DOS_MODE;
+ config.reg.invisible = BLIT_VISIBLE;
+ outb(BLIT_CONFIG_ADDR, config.byte);
+}
+
+#endif /* NBLIT > 0 */
+#endif /* NBLIT > MAXBLITS */
diff --git a/i386/i386at/blitreg.h b/i386/i386at/blitreg.h
new file mode 100644
index 00000000..7226aca5
--- /dev/null
+++ b/i386/i386at/blitreg.h
@@ -0,0 +1,404 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: blitreg.h
+ Description: Bell Tech Blit card hardware description
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Some code taken from Bob Glossman's 1987 "minimal Blit Express
+ * driver", copyright unknown. Probably copyright Intel, too.
+ */
+
+
+#ifndef blitreg_DEFINED
+#define blitreg_DEFINED
+
+
+/*
+ * Registers accessible through AT I/O space. These addresses can be
+ * changed by changing bits 4-8 of the Blit's DIP switch.
+ */
+
+#define BLIT_CONFIG_ADDR 0x304
+#define BLIT_DIAG_ADDR 0x306
+
+#if defined(sun386) || defined(i386)
+
+
+/*
+ * Layout of Blit control register.
+ */
+
+union blit_config_reg {
+ struct config_bits {
+ unsigned dos_segment : 4;
+ unsigned reset : 1;
+ unsigned mode : 1;
+#define BLIT_UNIX_MODE 1
+#define BLIT_DOS_MODE 0
+ unsigned invisible : 1;
+#define BLIT_INVISIBLE 1
+#define BLIT_VISIBLE 0
+ unsigned unused : 1;
+ } reg;
+ u_char byte;
+};
+
+
+/*
+ * Blit Diag register.
+ * The UNIX base address is currently hardwired to BLIT_BASE_ADDR.
+ */
+
+#define BLIT_BASE_ADDR 0xd80000 /* base of blit memory (phys addr) */
+
+union blit_diag_reg {
+ struct diag_bits {
+ unsigned unix_base_addr : 5; /* phys addr (ignored) */
+ unsigned led0 : 1;
+ unsigned led1 : 1;
+ unsigned led2 : 1;
+#define BLIT_LED_ON 1
+#define BLIT_LED_OFF 0
+ } reg;
+ u_char byte;
+};
+
+#endif /* sun386 || i386 */
+
+
+/*
+ * Graphics memory, 786 registers, static RAM, and EPROM, all
+ * accessible through mapped memory.
+ */
+
+#define BLIT_MONOWIDTH 1664
+#define BLIT_MONOHEIGHT 1200
+#define BLIT_MONOFBSIZE ((BLIT_MONOWIDTH*BLIT_MONOHEIGHT)/8)
+ /* byte size of monochrome fb */
+
+#define BLIT_MEMSIZE 0x100000 /* num bytes mapped graphics memory */
+
+#define BLIT_REGSIZE 128 /* bytes taken by 786 registers */
+#define BLIT_REGPAD (0x10000 - BLIT_REGSIZE)
+ /* padding between reg's and SRAM */
+
+#define BLIT_SRAMSIZE 0x4000 /* num bytes mapped for SRAM */
+#define BLIT_SRAMPAD (0x10000 - BLIT_SRAMSIZE)
+ /* padding between SRAM and EPROM */
+
+#define BLIT_EPROMSIZE 0x20000 /* num bytes mapped for EPROM */
+
+
+/*
+ * Layout of the Blit's mapped memory. The physical address is (or
+ * will be, eventually) determined by the Diag register (above).
+ */
+
+struct blitdev {
+ u_char graphmem[BLIT_MEMSIZE];
+ u_char reg786[BLIT_REGSIZE];
+ u_char pad1[BLIT_REGPAD];
+ u_char sram[BLIT_SRAMSIZE];
+ u_char pad2[BLIT_SRAMPAD];
+ u_char eprom[BLIT_EPROMSIZE];
+};
+
+#define BLIT_MAPPED_SIZE sizeof(struct blitdev)
+
+
+/*
+ * Offsets for 786 registers (i.e., indices into reg786[]).
+ */
+
+#define INTER_RELOC 0x00 /* Internal Relocation Register */
+#define BIU_CONTROL 0x04 /* BIU Control Register */
+#define DRAM_REFRESH 0x06 /* DRAM Refresh control register */
+#define DRAM_CONTROL 0x08 /* DRAM control register */
+#define DP_PRIORITY 0x0A /* DP priority register */
+#define GP_PRIORITY 0x0C /* GP priority register*/
+#define EXT_PRIORITY 0x0E /* External Priority Register*/
+#define GP_OPCODE_REG 0x20 /* GP opcode register */
+#define GP_PARM1_REG 0x22 /* GP Parameter 1 Register */
+#define GP_PARM2_REG 0x24 /* GP Parameter 2 Register*/
+#define GP_STAT_REG 0x26 /* GP Status Register*/
+#define DP_OPCODE_REG 0x40 /* DP opcode register */
+#define DP_PARM1_REG 0x42 /* DP Parameter 1 Register*/
+#define DP_PARM2_REG 0x44 /* DP Parameter 2 Register*/
+#define DP_PARM3_REG 0x46 /* DP Parameter 3 Register*/
+#define DP_STAT_REG 0x48 /* DP Status Register*/
+#define DEF_VIDEO_REG 0x4A /* DP Default Video Register*/
+
+
+/*
+ * 786 BIU Control Register values.
+ */
+
+#define BIU_WP1 0x02 /* Write Protect One; 1 = on */
+#define BIU_16BIT 0x10 /* access 786 registers as words; 0 = bytes */
+
+
+/*
+ * 786 DRAM/VRAM Control Register values.
+ */
+
+/* RW bits */
+#define MEMROWS1 0
+#define MEMROWS2 0x20
+#define MEMROWS3 0x40
+#define MEMROWS4 0x60
+
+/* DC bits */
+#define PG_NONINTERLV 0
+#define FASTPG_NONINTERLV 0x10
+#define PG_INTERLV 0x08
+#define FASTPG_INTERLV 0x18
+
+/* HT bits */
+#define HEIGHT_8K 0
+#define HEIGHT_16K 0x1
+#define HEIGHT_32K 0x2
+#define HEIGHT_64K 0x3
+#define HEIGHT_128K 0x4
+#define HEIGHT_256K 0x5
+#define HEIGHT_512K 0x6
+#define HEIGHT_1M 0x7
+
+
+/*
+ * 786 Graphics Processor opcodes.
+ */
+
+#define GECL 0x001 /* end of command list */
+#define OP_LINK 0x200 /* LINK - "link next cmd" */
+
+
+/*
+ * 786 Display Processor opcodes.
+ */
+
+#define DECL 1 /* end of list */
+#define DP_LOADALL 0x500
+
+
+/*
+ * Macros for accessing 786 registers (see BIU_16BIT) and EPROM.
+ */
+
+#define WRITEREG8(base,offset,val) \
+ (base)->reg786[(offset)] = (val) & 0xff, \
+ (base)->reg786[(offset)+1] = ((val) & 0xff00) >> 8
+
+#define WRITEREG16(base,offset,val) \
+ (*((u_short *)((base)->reg786+(offset)))) = (val)
+
+#define READREG(base,offset) \
+ (*((u_short *)(((base)->reg786+(offset)))))
+
+#define WRITEROM(romp,offset,val) \
+ (*((u_short *)((romp)+(offset)))) = (val)
+
+#define READROM(romp,offset) \
+ (*((u_short *)(((romp)+(offset)))))
+
+
+/*
+ * Layout of Display Processor Control Block Registers. This block is
+ * allocated somewhere in the Blit's graphics memory, and a pointer to
+ * it is passed to the Display Processor.
+ *
+ * NOTE: The 786 only sees the memory mapped by the Blit. Thus all
+ * addresses passed to the 786 are relative to the start of the Blit's
+ * mapped memory.
+ */
+
+typedef int addr786_t; /* 0 = start of Blit mapped memory */
+
+typedef struct {
+ u_short vidstat; /* video status */
+ u_short intrmask; /* interrupt mask */
+ u_short trip_point;
+ u_short frame_intr; /* frame interrupt */
+ u_short reserved1;
+ u_short crtmode; /* CRT controller mode */
+ u_short hsyncstop; /* monitor parameters */
+ u_short hfldstart;
+ u_short hfldstop;
+ u_short linelength;
+ u_short vsyncstop;
+ u_short vfldstart;
+ u_short vfldstop;
+ u_short vframelen;
+ u_short descl; /* descriptor pointer low part */
+ u_short desch; /* descriptor pointer high part */
+ u_short reserved2;
+ u_short xyzoom;
+ u_short fldcolor;
+ u_short bordercolor;
+ u_short bpp_pad1;
+ u_short bpp_pad2;
+ u_short bpp_pad4;
+ u_short csrmode; /* & CsrPad */
+ u_short cursorx; /* cursor x location */
+ u_short cursory; /* cursor y location */
+ u_short cursorpat[16]; /* cursor pattern */
+} DPCONTROLBLK;
+
+
+/*
+ * Values for 786 Display Processor Control Block Registers.
+ */
+
+/* video status */
+#define DP_DSP_ON 1 /* display on */
+#define DP_CSR_ON 2 /* cursor on */
+
+/* CRT controller modes */
+#define CRTM_NONINTER 0 /* non-interlaced */
+#define CRTM_INTERLCD 0x40 /* interlaced */
+#define CRTM_INTERSYN 0x60 /* interlaced - sync */
+#define CRTM_WIN_STAT_ENABLE 0x10 /* window status enable */
+#define CRTM_SYNC_SLAVE_MODE 0x08 /* on = operate as slave */
+#define CRTM_BLANK_SLAVE_MODE 0x04 /* on = Blank is input */
+#define CRTM_NORMAL_SPEED 0x00
+#define CRTM_HIGH_SPEED 0x01
+#define CRTM_VRYHIGH_SPEED 0x02
+#define CRTM_SUPHIGH_SPEED 0x03
+
+/* cursor style */
+#define DP_CURSOR_16X16 0x8000 /* off = 8x8 */
+#define DP_CURSOR_CROSSHAIR 0x4000 /* off = block cursor */
+#define DP_CURSOR_TRANSPRNT 0x2000 /* off = cursor is opaque */
+
+
+/*
+ * Types for dealing with 786 Display Processor.
+ */
+
+typedef struct {
+ u_short lines; /* (lines in strip) - 1 */
+ u_short linkl; /* link to next strip low part */
+ u_short linkh; /* link to next strip high part */
+ u_short tiles; /* C bit, (tiles in strip) - 1 */
+} STRIPHEADER;
+
+/*
+ * If the C bit is turned on, the display processor "automatically
+ * displays the background color" for areas not defined by the strips.
+ * See section 3.1.3.2 of the '786 User's Manual.
+ */
+#define DP_C_BIT 0x8000
+
+typedef struct {
+ u_short bitmapw; /* width of bitmap */
+ u_short meml; /* btb mem address low part */
+ u_short memh; /* btb mem address high part */
+ u_short bppss; /* bpp, start and stop fields */
+ u_short fetchcnt; /* fetch count */
+ u_short flags; /* various flags */
+} TILEDESC;
+
+
+/*
+ * Macros for encoding addresses for strip headers & tile descriptors.
+ * addr786 is relative to the start of the Blit's mapped memory.
+ */
+
+#define DP_ADDRLOW(addr786) (((int)(addr786)) & 0xffff)
+#define DP_ADDRHIGH(addr786) ((((int)(addr786)) >> 16) & 0x3f)
+
+
+/*
+ * Byte offsets to useful data words within the EPROM.
+ */
+
+#define EP_MAGIC1 0
+#define EP_MAGIC1_VAL 0x7856
+#define EP_MAGIC2 2
+#define EP_MAGIC2_VAL 0x6587
+#define EP_DPSTART 4 /* start of DP ctl block */
+ /* (0 = start of EPROM) */
+#define EP_DPLEN 6 /* byte length of DP control block */
+
+#define EP_FONTSTART 8 /* start of font */
+ /* (0 = start of EPROM) */
+#define EP_FONTLEN 10 /* byte length of font */
+#define EP_CHARWIDTH 12 /* bit width of each char in font */
+#define EP_CHARHEIGHT 14
+#define EP_NUMCHARS 16 /* num chars in font */
+
+/* where in the bitmap the 25x80 console screen starts */
+#define EP_XSTART 18
+#define EP_YSTART 20
+
+#define EP_SCREENWIDTH 22 /* pixels per scan line */
+#define EP_SCREENHEIGHT 24 /* number of scan lines */
+
+#define EP_FIXUP_X 26 /* magic numbers for displaying */
+#define EP_FIXUP_Y 28 /* hardware cursor */
+
+#define EP_BPP 30 /* bits per pixel */
+
+
+/*
+ * Miscellaneous.
+ */
+
+#define BLIT_BLACK_BIT 0 /* try saying that 3 times fast */
+#define BLIT_WHITE_BIT 1
+#define BLIT_BLACK_BYTE 0
+#define BLIT_WHITE_BYTE 0xff
+
+
+#endif /* blitreg_DEFINED */
diff --git a/i386/i386at/blituser.h b/i386/i386at/blituser.h
new file mode 100644
index 00000000..0ebcfffb
--- /dev/null
+++ b/i386/i386at/blituser.h
@@ -0,0 +1,73 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: blituser.h
+ Description: User-program definitions for Bell Tech Blit card
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#ifndef _BLITUSER_
+#define _BLITUSER_
+
+#include <sys/ioctl.h>
+
+/*
+ * Ioctl's.
+ */
+
+/*
+ * BLIT_1ST_UNUSED returns a byte offset into the Blit graphics
+ * memory. The user is free to allocate and use any graphics memory
+ * starting at that offset.
+ */
+
+#define BLIT_1ST_UNUSED _IOR('b', 1, int)
+
+#endif /* _BLITUSER_ */
diff --git a/i386/i386at/blitvar.h b/i386/i386at/blitvar.h
new file mode 100644
index 00000000..58401f39
--- /dev/null
+++ b/i386/i386at/blitvar.h
@@ -0,0 +1,116 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: blitvar.h
+ Description: Definitions used by Blit driver other than h/w definition.
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <i386at/blitreg.h>
+#include <sys/types.h>
+#include <mach/boolean.h>
+
+
+/*
+ * This is how we use the Blit's graphics memory. The frame buffer
+ * goes at the front, and the rest is used for miscellaneous
+ * allocations. Users can use the "spare" memory, but they should do
+ * an ioctl to find out which part of the memory is really free.
+ */
+
+struct blitmem {
+ union blitfb {
+ u_char mono_fb[BLIT_MONOFBSIZE];
+ u_char color_fb[1]; /* place-holder */
+ } fb;
+ u_char spare[BLIT_MEMSIZE - sizeof(union blitfb)];
+};
+
+
+/*
+ * Macro to get from blitdev pointer to monochrome framebuffer.
+ */
+#define BLIT_MONOFB(blt, fbptr) \
+ { struct blitmem *mymem = (struct blitmem *)((blt)->graphmem); \
+ fbptr = mymem->fb.mono_fb; \
+ }
+
+
+/*
+ * Single-tile description that can be used to describe the entire
+ * screen.
+ */
+
+struct screen_descrip {
+ STRIPHEADER strip;
+ TILEDESC tile;
+};
+
+
+/*
+ * Number of microseconds we're willing to wait for display processor
+ * to load its command block.
+ */
+
+#define DP_RDYTIMEOUT 1000000
+
+
+/*
+ * Conversion macros.
+ */
+
+#define VM_TO_ADDR786(vmaddr, blit_base) \
+ ((int)(vmaddr) - (int)(blit_base))
+
+
+extern boolean_t blit_present();
+extern void blit_init();
diff --git a/i386/i386at/boothdr.S b/i386/i386at/boothdr.S
new file mode 100644
index 00000000..de807538
--- /dev/null
+++ b/i386/i386at/boothdr.S
@@ -0,0 +1,62 @@
+
+#include <mach/machine/asm.h>
+
+#include "i386asm.h"
+
+ .text
+
+ /* We should never be entered this way. */
+ .globl start,_start
+start:
+_start:
+ jmp boot_entry
+
+ /* MultiBoot header - see multiboot.h. */
+#define MULTIBOOT_MAGIC 0x1BADB002
+#ifdef __ELF__
+#define MULTIBOOT_FLAGS 0x00000002
+#else /* __ELF__ */
+#define MULTIBOOT_FLAGS 0x00010002
+#endif /* __ELF__ */
+ P2ALIGN(2)
+boot_hdr:
+ .long MULTIBOOT_MAGIC
+ .long MULTIBOOT_FLAGS
+ /*
+ * The next item here is the checksum.
+ * XX this works OK until we need at least the 30th bit.
+ */
+ .long - (MULTIBOOT_MAGIC+MULTIBOOT_FLAGS)
+#ifndef __ELF__ /* a.out kludge */
+ .long boot_hdr /* header_addr */
+ .long _start /* load_addr */
+ .long _edata /* load_end_addr */
+ .long _end /* bss_end_addr */
+ .long boot_entry /* entry */
+#endif /* __ELF__ */
+
+boot_entry:
+
+ /* Switch to our own interrupt stack. */
+ movl $_intstack+INTSTACK_SIZE,%esp
+
+ /* Reset EFLAGS to a known state. */
+ pushl $0
+ popf
+
+ /* Clear uninitialized data. */
+ lea _edata,%edi
+ lea _end,%ecx
+ subl %edi,%ecx
+ xorl %eax,%eax
+ rep
+ stosb
+
+ /* Push the boot_info pointer to be the second argument. */
+ pushl %ebx
+
+ /* Jump into C code. */
+ call EXT(c_boot_entry)
+
+ .comm _intstack,INTSTACK_SIZE
+
diff --git a/i386/i386at/com.c b/i386/i386at/com.c
new file mode 100644
index 00000000..113387f2
--- /dev/null
+++ b/i386/i386at/com.c
@@ -0,0 +1,891 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1994,1993,1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <com.h>
+#if NCOM > 0
+
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <device/conf.h>
+#include <device/errno.h>
+#include <device/tty.h>
+#include <device/io_req.h>
+
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <i386/machspl.h>
+#include <chips/busses.h>
+#include <i386at/comreg.h>
+
+#include <rc.h>
+#include <cons.h>
+
+extern void timeout(), ttrstrt();
+
+int comprobe(), comintr(), comstart(), commctl();
+void comattach();
+static void comparam();
+int comstop(), comgetstat(), comsetstat();
+
+static vm_offset_t com_std[NCOM] = { 0 };
+struct bus_device *cominfo[NCOM];
+struct bus_driver comdriver = {
+ comprobe, 0, comattach, 0, com_std, "com", cominfo, 0, 0, 0};
+
+struct tty com_tty[NCOM];
+int commodem[NCOM];
+int comcarrier[NCOM] = {0, 0,};
+boolean_t comfifo[NCOM];
+boolean_t comtimer_active;
+int comtimer_state[NCOM];
+
+#if RCLINE >= 0
+#define RCBAUD B9600
+static struct bus_device *comcndev;
+int comcnprobe(struct consdev *cp);
+int comcninit(struct consdev *cp);
+int comcngetc(dev_t dev, int wait);
+int comcnputc(dev_t dev, int c);
+#endif
+
+#ifndef PORTSELECTOR
+#define ISPEED B9600
+#define IFLAGS (EVENP|ODDP|ECHO|CRMOD)
+#else
+#define ISPEED B4800
+#define IFLAGS (EVENP|ODDP)
+#endif
+
+u_short divisorreg[] = {
+ 0, 2304, 1536, 1047, /* 0, 50, 75, 110*/
+ 857, 768, 576, 384, 192, /* 134.5, 150, 200, 300, 600*/
+ 96, 64, 48, /* 1200, 1800, 2000, 2400 */
+ 24, 12, /* 3600, 4800, 7200, 9600 */
+ 6, 3, 2}; /* 19200, 38400, 56000 */
+
+
+/*
+ *
+ * Probes are called during kernel boot: return 1 to mean that
+ * the relevant device is present today.
+ *
+ */
+int
+comprobe_general(struct bus_device *dev, int noisy)
+{
+ u_short addr = dev->address;
+ int unit = dev->unit;
+ int oldctl, oldmsb;
+ char *type = "8250";
+ int i;
+
+ if ((unit < 0) || (unit > NCOM)) {
+ printf("com %d out of range\n", unit);
+ return(0);
+ }
+ oldctl = inb(LINE_CTL(addr)); /* Save old value of LINE_CTL */
+ oldmsb = inb(BAUD_MSB(addr)); /* Save old value of BAUD_MSB */
+ outb(LINE_CTL(addr), 0); /* Select INTR_ENAB */
+ outb(BAUD_MSB(addr), 0);
+ if (inb(BAUD_MSB(addr)) != 0)
+ {
+ outb(LINE_CTL(addr), oldctl);
+ outb(BAUD_MSB(addr), oldmsb);
+ return 0;
+ }
+ outb(LINE_CTL(addr), iDLAB); /* Select BAUD_MSB */
+ outb(BAUD_MSB(addr), 255);
+ if (inb(BAUD_MSB(addr)) != 255)
+ {
+ outb(LINE_CTL(addr), oldctl);
+ outb(BAUD_MSB(addr), oldmsb);
+ return 0;
+ }
+ outb(LINE_CTL(addr), 0); /* Select INTR_ENAB */
+ if (inb(BAUD_MSB(addr)) != 0) /* Check that it has kept its value*/
+ {
+ outb(LINE_CTL(addr), oldctl);
+ outb(BAUD_MSB(addr), oldmsb);
+ return 0;
+ }
+
+ /* Com port found, now check what chip it has */
+
+ for(i = 0; i < 256; i++) /* Is there Scratch register */
+ {
+ outb(SCR(addr), i);
+ if (inb(SCR(addr)) != i)
+ break;
+ }
+ if (i == 256)
+ { /* Yes == 450 or 460 */
+ outb(SCR(addr), 0);
+ type = "82450 or 16450";
+ outb(FIFO_CTL(addr), iFIFOENA | iFIFO14CH); /* Enable fifo */
+ if ((inb(FIFO_CTL(addr)) & iFIFO14CH) != 0)
+ { /* Was it successfull */
+ /* if both bits are not set then broken xx550 */
+ if ((inb(FIFO_CTL(addr)) & iFIFO14CH) == iFIFO14CH)
+ {
+ type = "82550 or 16550";
+ comfifo[unit] = TRUE;
+ }
+ else
+ {
+ type = "82550 or 16550 with non-working FIFO";
+ }
+ outb(INTR_ID(addr), 0x00); /* Disable fifos */
+ }
+ }
+ if (noisy)
+ printf("com%d: %s chip.\n", unit, type);
+ return 1;
+}
+
+/*
+ * Probe routine for use during kernel startup when it is probing
+ * all of bus_device_init
+ */
+int
+comprobe(int port, struct bus_device *dev)
+{
+ return comprobe_general(dev, /*noisy*/ 1);
+}
+
+#if RCLINE >= 0
+/*
+ * Probe routine for use by the console
+ */
+int
+comcnprobe(struct consdev *cp)
+{
+ struct bus_device *b;
+ int maj, unit, pri;
+
+ maj = 0;
+ unit = -1;
+ pri = CN_DEAD;
+
+ for (b = bus_device_init; b->driver; b++)
+ if (strcmp(b->name, "com") == 0
+ && b->unit == RCLINE
+ && comprobe_general(b, /*quiet*/ 0))
+ {
+ /* Found one */
+ comcndev = b;
+ unit = b->unit;
+ pri = CN_REMOTE;
+ break;
+ }
+
+ cp->cn_dev = makedev(maj, unit);
+ cp->cn_pri = pri;
+}
+#endif
+
+
+/*
+ *
+ * Device Attach's are called during kernel boot, but only if the matching
+ * device Probe returned a 1.
+ *
+ */
+void
+comattach(struct bus_device *dev)
+{
+ u_char unit = dev->unit;
+ u_short addr = dev->address;
+
+ take_dev_irq(dev);
+ printf(", port = %x, spl = %d, pic = %d. (DOS COM%d)",
+ dev->address, dev->sysdep, dev->sysdep1, unit+1);
+
+/* comcarrier[unit] = addr->flags;*/
+ commodem[unit] = 0;
+
+ outb(INTR_ENAB(addr), 0);
+ outb(MODEM_CTL(addr), 0);
+ while (!(inb(INTR_ID(addr))&1)) {
+ (void) inb(LINE_STAT (addr)); /* reset overrun error etc */
+ (void) inb(TXRX (addr)); /* reset data-ready */
+ (void) inb(MODEM_STAT(addr)); /* reset modem status reg */
+ }
+}
+
+#if RCLINE >= 0
+/*
+ * Attach/init routine for console. This isn't called by
+ * configure_bus_device which sets the alive, adaptor, and minfo
+ * fields of the bus_device struct (comattach is), therefore we do
+ * that by hand.
+ */
+int
+comcninit(struct consdev *cp)
+{
+ u_char unit = comcndev->unit;
+ u_short addr = comcndev->address;
+
+ take_dev_irq(comcndev);
+
+ comcndev->alive = 1;
+ comcndev->adaptor = 0;
+ cominfo[minor(cp->cn_dev)] = comcndev;
+
+ outb(LINE_CTL(addr), iDLAB);
+ outb(BAUD_LSB(addr), divisorreg[RCBAUD] & 0xff);
+ outb(BAUD_MSB(addr), divisorreg[RCBAUD] >>8);
+ outb(LINE_CTL(addr), i7BITS|iPEN);
+ outb(INTR_ENAB(addr), 0);
+ outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2);
+
+ {
+ char msg[128];
+ volatile unsigned char *p = (volatile unsigned char *)0xb8000;
+ int i;
+
+ sprintf(msg, " **** using COM port %d for console ****",
+ unit+1);
+ for (i = 0; msg[i]; i++) {
+ p[2*i] = msg[i];
+ p[2*i+1] = (0<<7) /* blink */
+ | (0x0<<4) /* bg */
+ | (1<<3) /* hi-intensity */
+ | 0x4; /* fg */
+ }
+ }
+
+}
+#endif
+
+/*
+ * Probe for COM<dev> after autoconfiguration.
+ * Used to handle PCMCIA modems, which may appear
+ * at any time.
+ */
+boolean_t com_reprobe(
+ int unit)
+{
+ struct bus_device *device;
+
+ /*
+ * Look for COM device <unit> in the device
+ * initialization list. It must not be alive
+ * (otherwise we would have opened it already).
+ */
+ for (device = bus_device_init; device->driver; device++) {
+ if (device->driver == &comdriver && device->unit == unit &&
+ !device->alive && device->ctlr == (char)-1)
+ {
+ /*
+ * Found an entry for com port <unit>.
+ * Probe it.
+ */
+ if (configure_bus_device(device->name,
+ device->address,
+ device->phys_address,
+ 0,
+ "atbus"))
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+io_return_t comopen(
+ int dev,
+ int flag,
+ io_req_t ior)
+{
+ int unit = minor(dev);
+ u_short addr;
+ struct bus_device *isai;
+ struct tty *tp;
+ spl_t s;
+ io_return_t result;
+
+ if (unit >= NCOM)
+ return ENXIO; /* no such device */
+ if ((isai = cominfo[unit]) == 0 || isai->alive == 0) {
+ /*
+ * Try to probe it again
+ */
+ if (!com_reprobe(unit))
+ return ENXIO;
+ }
+ tp = &com_tty[unit];
+
+ if ((tp->t_state & (TS_ISOPEN|TS_WOPEN)) == 0) {
+ ttychars(tp);
+ tp->t_addr = (char *)isai->address;
+ tp->t_dev = dev;
+ tp->t_oproc = comstart;
+ tp->t_stop = comstop;
+ tp->t_mctl = commctl;
+ tp->t_getstat = comgetstat;
+ tp->t_setstat = comsetstat;
+#ifndef PORTSELECTOR
+ if (tp->t_ispeed == 0) {
+#else
+ tp->t_state |= TS_HUPCLS;
+#endif /* PORTSELECTOR */
+ tp->t_ispeed = ISPEED;
+ tp->t_ospeed = ISPEED;
+ tp->t_flags = IFLAGS;
+ tp->t_state &= ~TS_BUSY;
+#ifndef PORTSELECTOR
+ }
+#endif /* PORTSELECTOR */
+ }
+/*rvb tp->t_state |= TS_WOPEN; */
+ if ((tp->t_state & TS_ISOPEN) == 0)
+ comparam(unit);
+ addr = (int)tp->t_addr;
+
+ s = spltty();
+ if (!comcarrier[unit]) /* not originating */
+ tp->t_state |= TS_CARR_ON;
+ else {
+ int modem_stat = inb(MODEM_STAT(addr));
+ if (modem_stat & iRLSD)
+ tp->t_state |= TS_CARR_ON;
+ else
+ tp->t_state &= ~TS_CARR_ON;
+ fix_modem_state(unit, modem_stat);
+ }
+ splx(s);
+
+ result = char_open(dev, tp, flag, ior);
+
+ if (!comtimer_active) {
+ comtimer_active = TRUE;
+ comtimer();
+ }
+
+ s = spltty();
+ while(!(inb(INTR_ID(addr))&1)) { /* while pending interrupts */
+ (void) inb(LINE_STAT (addr)); /* reset overrun error */
+ (void) inb(TXRX (addr)); /* reset data-ready */
+ (void) inb(MODEM_STAT(addr)); /* reset modem status */
+ }
+ splx(s);
+ return result;
+}
+
+io_return_t comclose(dev, flag)
+int dev;
+int flag;
+{
+ struct tty *tp = &com_tty[minor(dev)];
+ u_short addr = (int)tp->t_addr;
+
+ ttyclose(tp);
+ if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) {
+ outb(INTR_ENAB(addr), 0);
+ outb(MODEM_CTL(addr), 0);
+ tp->t_state &= ~TS_BUSY;
+ commodem[minor(dev)] = 0;
+ if (comfifo[minor(dev)] != 0)
+ outb(INTR_ID(addr), 0x00); /* Disable fifos */
+ }
+ return 0;
+}
+
+io_return_t comread(dev, ior)
+int dev;
+io_req_t ior;
+{
+ return char_read(&com_tty[minor(dev)], ior);
+}
+
+io_return_t comwrite(dev, ior)
+int dev;
+io_req_t ior;
+{
+ return char_write(&com_tty[minor(dev)], ior);
+}
+
+io_return_t comportdeath(dev, port)
+dev_t dev;
+mach_port_t port;
+{
+ return (tty_portdeath(&com_tty[minor(dev)], port));
+}
+
+io_return_t
+comgetstat(dev, flavor, data, count)
+dev_t dev;
+int flavor;
+int *data; /* pointer to OUT array */
+unsigned int *count; /* out */
+{
+ io_return_t result = D_SUCCESS;
+ int unit = minor(dev);
+
+ switch (flavor) {
+ case TTY_MODEM:
+ fix_modem_state(unit, inb(MODEM_STAT(cominfo[unit]->address)));
+ *data = commodem[unit];
+ *count = 1;
+ break;
+ default:
+ result = tty_get_status(&com_tty[unit], flavor, data, count);
+ break;
+ }
+ return (result);
+}
+
+io_return_t
+comsetstat(dev, flavor, data, count)
+dev_t dev;
+int flavor;
+int * data;
+unsigned int count;
+{
+ io_return_t result = D_SUCCESS;
+ int unit = minor(dev);
+ struct tty *tp = &com_tty[unit];
+
+ switch (flavor) {
+ case TTY_SET_BREAK:
+ commctl(tp, TM_BRK, DMBIS);
+ break;
+ case TTY_CLEAR_BREAK:
+ commctl(tp, TM_BRK, DMBIC);
+ break;
+ case TTY_MODEM:
+ commctl(tp, *data, DMSET);
+ break;
+ default:
+ result = tty_set_status(&com_tty[unit], flavor, data, count);
+ if (result == D_SUCCESS && flavor == TTY_STATUS)
+ comparam(unit);
+ return (result);
+ }
+ return (D_SUCCESS);
+}
+
+comintr(unit)
+int unit;
+{
+ register struct tty *tp = &com_tty[unit];
+ u_short addr = cominfo[unit]->address;
+ static char comoverrun = 0;
+ char c, line, intr_id;
+ int line_stat;
+
+ while (! ((intr_id=(inb(INTR_ID(addr))&MASKi)) & 1))
+ switch (intr_id) {
+ case MODi:
+ /* modem change */
+ commodem_intr(unit, inb(MODEM_STAT(addr)));
+ break;
+
+ case TRAi:
+ comtimer_state[unit] = 0;
+ tp->t_state &= ~(TS_BUSY|TS_FLUSH);
+ tt_write_wakeup(tp);
+ (void) comstart(tp);
+ break;
+ case RECi:
+ case CTIi: /* Character timeout indication */
+ if (tp->t_state&TS_ISOPEN) {
+ while ((line = inb(LINE_STAT(addr))) & iDR) {
+ c = inb(TXRX(addr));
+ ttyinput(c, tp);
+ }
+ } else
+ tt_open_wakeup(tp);
+ break;
+ case LINi:
+ line_stat = inb(LINE_STAT(addr));
+
+ if ((line_stat & iPE) &&
+ ((tp->t_flags&(EVENP|ODDP)) == EVENP ||
+ (tp->t_flags&(EVENP|ODDP)) == ODDP)) {
+ /* parity error */;
+ } else if (line&iOR && !comoverrun) {
+ printf("com%d: overrun\n", unit);
+ comoverrun = 1;
+ } else if (line_stat & (iFE | iBRKINTR)) {
+ /* framing error or break */
+ ttyinput(tp->t_breakc, tp);
+ }
+ break;
+ }
+}
+
+static void
+comparam(unit)
+register int unit;
+{
+ struct tty *tp = &com_tty[unit];
+ u_short addr = (int)tp->t_addr;
+ spl_t s = spltty();
+ int mode;
+
+ if (tp->t_ispeed == B0) {
+ tp->t_state |= TS_HUPCLS;
+ outb(MODEM_CTL(addr), iOUT2);
+ commodem[unit] = 0;
+ splx(s);
+ return;
+ }
+
+ /* Do input buffering */
+ if (tp->t_ispeed >= B300)
+ tp->t_state |= TS_MIN;
+
+ outb(LINE_CTL(addr), iDLAB);
+ outb(BAUD_LSB(addr), divisorreg[tp->t_ispeed] & 0xff);
+ outb(BAUD_MSB(addr), divisorreg[tp->t_ispeed] >> 8);
+
+ if (tp->t_flags & (RAW|LITOUT|PASS8))
+ mode = i8BITS;
+ else
+ mode = i7BITS | iPEN;
+ if (tp->t_flags & EVENP)
+ mode |= iEPS;
+ if (tp->t_ispeed == B110)
+ /*
+ * 110 baud uses two stop bits -
+ * all other speeds use one
+ */
+ mode |= iSTB;
+
+ outb(LINE_CTL(addr), mode);
+
+ outb(INTR_ENAB(addr), iTX_ENAB|iRX_ENAB|iMODEM_ENAB|iERROR_ENAB);
+ if (comfifo[unit])
+ outb(FIFO_CTL(addr), iFIFOENA|iFIFO14CH);
+ outb(MODEM_CTL(addr), iDTR|iRTS|iOUT2);
+ commodem[unit] |= (TM_DTR|TM_RTS);
+ splx(s);
+}
+
+comparm(int unit, int baud, int intr, int mode, int modem)
+{
+ u_short addr = (u_short)(cominfo[unit]->address);
+ spl_t s = spltty();
+
+ if (unit != 0 && unit != 1) {
+ printf("comparm(unit, baud, mode, intr, modem)\n");
+ splx(s);
+ return;
+ }
+ outb(LINE_CTL(addr), iDLAB);
+ outb(BAUD_LSB(addr), divisorreg[baud] & 0xff);
+ outb(BAUD_MSB(addr), divisorreg[baud] >> 8);
+ outb(LINE_CTL(addr), mode);
+ outb(INTR_ENAB(addr), intr);
+ outb(MODEM_CTL(addr), modem);
+ splx(s);
+}
+
+int comst_1, comst_2, comst_3, comst_4, comst_5 = 14;
+
+int
+comstart(tp)
+struct tty *tp;
+{
+ char nch;
+ int i;
+
+ if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) {
+comst_1++;
+ return(0);
+ }
+ if ((!queue_empty(&tp->t_delayed_write)) &&
+ (tp->t_outq.c_cc <= TTLOWAT(tp))) {
+comst_2++;
+ tt_write_wakeup(tp);
+ }
+ if (!tp->t_outq.c_cc) {
+comst_3++;
+ return(0);
+ }
+
+#if 0
+ i = (comfifo[minor(tp->t_dev)]) ? /*14*/comst_5 : 1;
+
+ tp->t_state |= TS_BUSY;
+ while (i-- > 0) {
+ nch = getc(&tp->t_outq);
+ if (nch == -1) break;
+ if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) {
+ timeout(ttrstrt, (char *)tp, (nch & 0x7f) + 6);
+ tp->t_state |= TS_TIMEOUT;
+comst_4++;
+ return(0);
+ }
+ outb(TXRX((int)tp->t_addr), nch);
+ }
+#else
+ nch = getc(&tp->t_outq);
+ if ((nch & 0200) && ((tp->t_flags & LITOUT) == 0)) {
+ timeout(ttrstrt, (char *)tp, (nch & 0x7f) + 6);
+ tp->t_state |= TS_TIMEOUT;
+comst_4++;
+ return(0);
+ }
+ outb(TXRX((int)tp->t_addr), nch);
+ tp->t_state |= TS_BUSY;
+#endif
+ return(0);
+}
+
+/* Check for stuck xmitters */
+int comtimer_interval = 5;
+
+comtimer()
+{
+ spl_t s = spltty();
+ struct tty *tp = com_tty;
+ int i, nch;
+
+ for (i = 0; i < NCOM; i++, tp++) {
+ if ((tp->t_state & TS_ISOPEN) == 0)
+ continue;
+ if (!tp->t_outq.c_cc)
+ continue;
+ if (++comtimer_state[i] < 2)
+ continue;
+ /* Its stuck */
+printf("Tty %x was stuck\n", tp);
+ nch = getc(&tp->t_outq);
+ outb(TXRX((int)tp->t_addr), nch);
+ }
+
+ splx(s);
+ timeout(comtimer, 0, comtimer_interval*hz);
+}
+
+/*
+ * Set receive modem state from modem status register.
+ */
+fix_modem_state(unit, modem_stat)
+int unit, modem_stat;
+{
+ int stat = 0;
+
+ if (modem_stat & iCTS)
+ stat |= TM_CTS; /* clear to send */
+ if (modem_stat & iDSR)
+ stat |= TM_DSR; /* data set ready */
+ if (modem_stat & iRI)
+ stat |= TM_RNG; /* ring indicator */
+ if (modem_stat & iRLSD)
+ stat |= TM_CAR; /* carrier? */
+
+ commodem[unit] = (commodem[unit] & ~(TM_CTS|TM_DSR|TM_RNG|TM_CAR))
+ | stat;
+}
+
+/*
+ * Modem change (input signals)
+ */
+commodem_intr(
+ int unit,
+ int stat)
+{
+ int changed;
+
+ changed = commodem[unit];
+ fix_modem_state(unit, stat);
+ stat = commodem[unit];
+
+ /* Assumption: if the other party can handle
+ modem signals then it should handle all
+ the necessary ones. Else fix the cable. */
+
+ changed ^= stat; /* what changed ? */
+
+ if (changed & TM_CTS)
+ tty_cts( &com_tty[unit], stat & TM_CTS );
+
+#if 0
+ if (changed & TM_CAR)
+ ttymodem( &com_tty[unit], stat & TM_CAR );
+#endif
+
+}
+
+/*
+ * Set/get modem bits
+ */
+commctl(
+ register struct tty *tp,
+ int bits,
+ int how)
+{
+ spl_t s;
+ int unit;
+ vm_offset_t dev_addr;
+ register int b;
+
+ unit = minor(tp->t_dev);
+
+ if (bits == TM_HUP) { /* close line (internal) */
+ bits = TM_DTR | TM_RTS;
+ how = DMBIC;
+ }
+
+ if (how == DMGET) return commodem[unit];
+
+ dev_addr = cominfo[unit]->address;
+
+ s = spltty();
+
+ switch (how) {
+ case DMSET:
+ b = bits; break;
+ case DMBIS:
+ b = commodem[unit] | bits; break;
+ case DMBIC:
+ b = commodem[unit] & ~bits; break;
+ }
+ commodem[unit] = b;
+
+ if (bits & TM_BRK) {
+ if (b & TM_BRK) {
+ outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) | iSETBREAK);
+ } else {
+ outb(LINE_CTL(dev_addr), inb(LINE_CTL(dev_addr)) & ~iSETBREAK);
+ }
+ }
+
+#if 0
+ /* do I need to do something on this ? */
+ if (bits & TM_LE) { /* line enable */
+ }
+#endif
+#if 0
+ /* Unsupported */
+ if (bits & TM_ST) { /* secondary transmit */
+ }
+ if (bits & TM_SR) { /* secondary receive */
+ }
+#endif
+ if (bits & (TM_DTR|TM_RTS)) { /* data terminal ready, request to send */
+ how = iOUT2;
+ if (b & TM_DTR) how |= iDTR;
+ if (b & TM_RTS) how |= iRTS;
+ outb(MODEM_CTL(dev_addr), how);
+ }
+
+ splx(s);
+
+ /* the rest are inputs */
+ return commodem[unit];
+}
+
+comstop(tp, flags)
+register struct tty *tp;
+int flags;
+{
+ if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0)
+ tp->t_state |= TS_FLUSH;
+}
+
+/*
+ *
+ * Code to be called from debugger.
+ *
+ */
+void compr_addr(addr)
+{
+ /* The two line_stat prints may show different values, since
+ * touching some of the registers constitutes changing them.
+ */
+ printf("LINE_STAT(%x) %x\n",
+ LINE_STAT(addr), inb(LINE_STAT(addr)));
+
+ printf("TXRX(%x) %x, INTR_ENAB(%x) %x, INTR_ID(%x) %x, LINE_CTL(%x) %x,\n\
+MODEM_CTL(%x) %x, LINE_STAT(%x) %x, MODEM_STAT(%x) %x\n",
+ TXRX(addr), inb(TXRX(addr)),
+ INTR_ENAB(addr), inb(INTR_ENAB(addr)),
+ INTR_ID(addr), inb(INTR_ID(addr)),
+ LINE_CTL(addr), inb(LINE_CTL(addr)),
+ MODEM_CTL(addr), inb(MODEM_CTL(addr)),
+ LINE_STAT(addr), inb(LINE_STAT(addr)),
+ MODEM_STAT(addr),inb(MODEM_STAT(addr)));
+}
+
+int compr(unit)
+{
+ compr_addr(cominfo[unit]->address);
+ return(0);
+}
+
+int
+comgetc(int unit)
+{
+ u_short addr = (u_short)(cominfo[unit]->address);
+ spl_t s = spltty();
+ int c;
+
+ while((inb(LINE_STAT(addr)) & iDR) == 0) ;
+
+ c = inb(TXRX(addr));
+ splx(s);
+ return c;
+}
+
+#if RCLINE >= 0
+/*
+ * Routines for the console
+ */
+int
+comcnputc(dev_t dev, int c)
+{
+ u_short addr = (u_short)(cominfo[minor(dev)]->address);
+
+ /* Wait for transmitter to empty */
+ while((inb(LINE_STAT(addr)) & iTHRE) == 0)
+ continue;
+
+ /* send the char */
+ if (c == '\n')
+ comcnputc(dev, '\r');
+ outb(addr, c);
+}
+
+int
+comcngetc(dev_t dev, int wait)
+{
+ u_short addr = (u_short)(cominfo[minor(dev)]->address);
+ int c;
+
+ while((inb(LINE_STAT(addr)) & iDR) == 0)
+ if (! wait)
+ return 0;
+
+ c = inb(TXRX(addr));
+ return c & 0x7f;
+}
+#endif /* RCLINE */
+
+#endif /* NCOM */
diff --git a/i386/i386at/comreg.h b/i386/i386at/comreg.h
new file mode 100644
index 00000000..12174a1c
--- /dev/null
+++ b/i386/i386at/comreg.h
@@ -0,0 +1,134 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Olivetti serial port driver v1.0
+ * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
+ * All rights reserved.
+ *
+ */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define TXRX(addr) (addr + 0)
+#define BAUD_LSB(addr) (addr + 0)
+#define BAUD_MSB(addr) (addr + 1)
+#define INTR_ENAB(addr) (addr + 1)
+#define INTR_ID(addr) (addr + 2)
+#define FIFO_CTL(addr) (addr + 2)
+#define LINE_CTL(addr) (addr + 3)
+#define MODEM_CTL(addr) (addr + 4)
+#define LINE_STAT(addr) (addr + 5)
+#define MODEM_STAT(addr)(addr + 6)
+#define SCR(addr) (addr + 7)
+
+#define MODi 0
+#define TRAi 2
+#define RECi 4
+#define LINi 6
+#define CTIi 0xc
+#define MASKi 0xf
+
+/* line control register */
+#define iWLS0 0x01 /*word length select bit 0 */
+#define iWLS1 0x02 /*word length select bit 2 */
+#define iSTB 0x04 /* number of stop bits */
+#define iPEN 0x08 /* parity enable */
+#define iEPS 0x10 /* even parity select */
+#define iSP 0x20 /* stick parity */
+#define iSETBREAK 0x40 /* break key */
+#define iDLAB 0x80 /* divisor latch access bit */
+#define i5BITS 0x00 /* 5 bits per char */
+#define i6BITS 0x01 /* 6 bits per char */
+#define i7BITS 0x02 /* 7 bits per char */
+#define i8BITS 0x03 /* 8 bits per char */
+
+/* line status register */
+#define iDR 0x01 /* data ready */
+#define iOR 0x02 /* overrun error */
+#define iPE 0x04 /* parity error */
+#define iFE 0x08 /* framing error */
+#define iBRKINTR 0x10 /* a break has arrived */
+#define iTHRE 0x20 /* tx hold reg is now empty */
+#define iTSRE 0x40 /* tx shift reg is now empty */
+
+/* interrupt id regisger */
+#define iMODEM_INTR 0x01
+#define iTX_INTR 0x02
+#define iRX_INTR 0x04
+#define iERROR_INTR 0x08
+
+/* interrupt enable register */
+#define iRX_ENAB 0x01
+#define iTX_ENAB 0x02
+#define iERROR_ENAB 0x04
+#define iMODEM_ENAB 0x08
+
+/* modem control register */
+#define iDTR 0x01 /* data terminal ready */
+#define iRTS 0x02 /* request to send */
+#define iOUT1 0x04 /* COM aux line -not used */
+#define iOUT2 0x08 /* turns intr to 386 on/off */
+#define iLOOP 0x10 /* loopback for diagnostics */
+
+/* modem status register */
+#define iDCTS 0x01 /* delta clear to send */
+#define iDDSR 0x02 /* delta data set ready */
+#define iTERI 0x04 /* trail edge ring indicator */
+#define iDRLSD 0x08 /* delta rx line sig detect */
+#define iCTS 0x10 /* clear to send */
+#define iDSR 0x20 /* data set ready */
+#define iRI 0x40 /* ring indicator */
+#define iRLSD 0x80 /* rx line sig detect */
+
+/* fifo control register (only in 16550) */
+#define iFIFOENA 0x01 /* Enable fifos */
+#define iCLRRCVRFIFO 0x02 /* Clear receive fifo */
+#define iCLRXMITFIFO 0x04 /* Clear transmit fifo */
+#define iDMAMODE 0x08 /* DMA transfer enable */
+#define iFIFO1CH 0x00 /* Receive fifo trigger level 1 char */
+#define iFIFO4CH 0x40 /* Receive fifo trigger level 4 chars*/
+#define iFIFO8CH 0x80 /* Receive fifo trigger level 8 chars*/
+#define iFIFO14CH 0xc0 /* Receive fifo trigger level 14 chars*/
diff --git a/i386/i386at/conf.c b/i386/i386at/conf.c
new file mode 100644
index 00000000..2bd2d2af
--- /dev/null
+++ b/i386/i386at/conf.c
@@ -0,0 +1,399 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Device switch for i386 AT bus.
+ */
+
+#include <mach/machine/vm_types.h>
+#include <device/conf.h>
+
+extern vm_offset_t block_io_mmap();
+
+extern int timeopen(), timeclose();
+extern vm_offset_t timemmap();
+#define timename "time"
+
+#include <hd.h>
+#if NHD > 0
+extern int hdopen(), hdclose(), hdread(), hdwrite();
+extern int hdgetstat(), hdsetstat(), hddevinfo();
+#define hdname "hd"
+
+#if 0
+extern int pchdopen(),pchdread(),pchdwrite(),pchdgetstat(),pchdsetstat();
+#define pchdname "pchd"
+#endif
+
+#endif NHD > 0
+
+#include <aha.h>
+#if NAHA > 0
+int rz_open(), rz_close(), rz_read(), rz_write();
+int rz_get_status(), rz_set_status(), rz_devinfo();
+int cd_open(), cd_close(), cd_read(), cd_write();
+#define rzname "sd"
+#define tzname "st"
+#define scname "sc" /* processors */
+#define cdname "cd_audio" /* CD-ROM DA */
+
+#endif /*NAHA > 0*/
+
+#include <fd.h>
+#if NFD > 0
+extern int fdopen(), fdclose(), fdread(), fdwrite();
+extern int fdgetstat(), fdsetstat(), fddevinfo();
+#define fdname "fd"
+#endif NFD > 0
+
+#include <wt.h>
+#if NWT > 0
+extern int wtopen(), wtread(), wtwrite(), wtclose();
+#define wtname "wt"
+#endif NWT > 0
+
+#include <pc586.h>
+#if NPC586 > 0
+extern int pc586open(), pc586output(), pc586getstat(), pc586setstat(),
+ pc586setinput();
+#define pc586name "pc"
+#endif NPC586 > 0
+
+#include <ne.h>
+#if NNE > 0
+extern int neopen(), neoutput(), negetstat(), nesetstat(), nesetinput();
+#ifdef FIPC
+extern int nefoutput();
+#endif /* FIPC */
+#define nename "ne"
+#endif NNE > 0
+
+#include <ns8390.h>
+#if NNS8390 > 0
+extern int wd8003open(), eliiopen();
+extern int ns8390output(), ns8390getstat(), ns8390setstat(),
+ ns8390setinput();
+#define ns8390wdname "wd"
+#define ns8390elname "el"
+#endif NNS8390 > 0
+
+#include <at3c501.h>
+#if NAT3C501 > 0
+extern int at3c501open(), at3c501output(),
+ at3c501getstat(), at3c501setstat(),
+ at3c501setinput();
+#define at3c501name "et"
+#endif NAT3C501 > 0
+
+#include <ul.h>
+#if NUL > 0
+extern int ulopen(), uloutput(), ulgetstat(), ulsetstat(),
+ ulsetinput();
+#define ulname "ul"
+#endif NUL > 0
+
+#include <wd.h>
+#if NWD > 0
+extern int wdopen(), wdoutput(), wdgetstat(), wdsetstat(),
+ wdsetinput();
+#define wdname "wd"
+#endif NWD > 0
+
+#include <hpp.h>
+#if NHPP > 0
+extern int hppopen(), hppoutput(), hppgetstat(), hppsetstat(),
+ hppsetinput();
+#define hppname "hpp"
+#endif /* NHPP > 0 */
+
+#include <par.h>
+#if NPAR > 0
+extern int paropen(), paroutput(), pargetstat(), parsetstat(),
+ parsetinput();
+#define parname "par"
+#endif NPAR > 0
+
+#include <de6c.h>
+#if NDE6C > 0
+extern int de6copen(), de6coutput(), de6cgetstat(), de6csetstat(),
+ de6csetinput();
+#define de6cname "de"
+#endif NDE6C > 0
+
+extern int kdopen(), kdclose(), kdread(), kdwrite();
+extern int kdgetstat(), kdsetstat(), kdportdeath();
+extern vm_offset_t kdmmap();
+#define kdname "kd"
+
+#include <com.h>
+#if NCOM > 0
+extern int comopen(), comclose(), comread(), comwrite();
+extern int comgetstat(), comsetstat(), comportdeath();
+#define comname "com"
+#endif NCOM > 0
+
+#include <lpr.h>
+#if NLPR > 0
+extern int lpropen(), lprclose(), lprread(), lprwrite();
+extern int lprgetstat(), lprsetstat(), lprportdeath();
+#define lprname "lpr"
+#endif NLPR > 0
+
+#include <blit.h>
+#if NBLIT > 0
+extern int blitopen(), blitclose(), blit_get_stat();
+extern vm_offset_t blitmmap();
+#define blitname "blit"
+
+extern int mouseinit(), mouseopen(), mouseclose();
+extern int mouseioctl(), mouseselect(), mouseread();
+#endif
+
+extern int kbdopen(), kbdclose(), kbdread();
+extern int kbdgetstat(), kbdsetstat();
+#define kbdname "kbd"
+
+extern int mouseopen(), mouseclose(), mouseread();
+#define mousename "mouse"
+
+extern int ioplopen(), ioplclose();
+extern vm_offset_t ioplmmap();
+#define ioplname "iopl"
+
+/*
+ * List of devices - console must be at slot 0
+ */
+struct dev_ops dev_name_list[] =
+{
+ /*name, open, close, read,
+ write, getstat, setstat, mmap,
+ async_in, reset, port_death, subdev,
+ dev_info */
+
+ /* We don't assign a console here, when we find one via
+ cninit() we stick something appropriate here through the
+ indirect list */
+ { "cn", nulldev, nulldev, nulldev,
+ nulldev, nulldev, nulldev, nulldev,
+ nodev, nulldev, nulldev, 0,
+ nodev },
+
+ { kdname, kdopen, kdclose, kdread,
+ kdwrite, kdgetstat, kdsetstat, kdmmap,
+ nodev, nulldev, kdportdeath, 0,
+ nodev },
+
+ { timename, timeopen, timeclose, nulldev,
+ nulldev, nulldev, nulldev, timemmap,
+ nodev, nulldev, nulldev, 0,
+ nodev },
+
+#ifndef LINUX_DEV
+#if NHD > 0
+ { hdname, hdopen, hdclose, hdread,
+ hdwrite, hdgetstat, hdsetstat, nomap,
+ nodev, nulldev, nulldev, 1024,
+ hddevinfo },
+#endif NHD > 0
+
+#if NAHA > 0
+ { rzname, rz_open, rz_close, rz_read,
+ rz_write, rz_get_status, rz_set_status, nomap,
+ nodev, nulldev, nulldev, 1024, /* 8 */
+ rz_devinfo },
+
+ { tzname, rz_open, rz_close, rz_read,
+ rz_write, rz_get_status, rz_set_status, nomap,
+ nodev, nulldev, nulldev, 8,
+ nodev },
+
+ { cdname, cd_open, cd_close, cd_read,
+ cd_write, nodev, nodev, nomap,
+ nodev, nulldev, nulldev, 8,
+ nodev },
+
+ { scname, rz_open, rz_close, rz_read,
+ rz_write, rz_get_status, rz_set_status, nomap,
+ nodev, nulldev, nulldev, 8,
+ nodev },
+
+#endif /*NAHA > 0*/
+
+#if NFD > 0
+ { fdname, fdopen, fdclose, fdread,
+ fdwrite, fdgetstat, fdsetstat, nomap,
+ nodev, nulldev, nulldev, 64,
+ fddevinfo },
+#endif NFD > 0
+
+#if NWT > 0
+ { wtname, wtopen, wtclose, wtread,
+ wtwrite, nulldev, nulldev, nomap,
+ nodev, nulldev, nulldev, 0,
+ nodev },
+#endif NWT > 0
+
+#if NPC586 > 0
+ { pc586name, pc586open, nulldev, nulldev,
+ pc586output, pc586getstat, pc586setstat, nomap,
+ pc586setinput,nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NNE > 0
+ { nename, neopen, nulldev, nulldev,
+ neoutput, negetstat, nesetstat, nulldev,
+#ifdef FIPC
+ nesetinput, nulldev, nefoutput, 0,
+#else
+ nesetinput, nulldev, nulldev, 0,
+#endif /* FIPC */
+ nodev },
+#endif
+
+#if NAT3C501 > 0
+ { at3c501name, at3c501open, nulldev, nulldev,
+ at3c501output,at3c501getstat, at3c501setstat, nomap,
+ at3c501setinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NNS8390 > 0
+ { ns8390wdname, wd8003open, nulldev, nulldev,
+ ns8390output, ns8390getstat, ns8390setstat, nomap,
+ ns8390setinput, nulldev, nulldev, 0,
+ nodev },
+
+ { ns8390elname, eliiopen, nulldev, nulldev,
+ ns8390output, ns8390getstat, ns8390setstat, nomap,
+ ns8390setinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NUL > 0
+ { ulname, ulopen, nulldev, nulldev,
+ uloutput, ulgetstat, ulsetstat, nulldev,
+ ulsetinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NWD > 0
+ { wdname, wdopen, nulldev, nulldev,
+ wdoutput, wdgetstat, wdsetstat, nulldev,
+ wdsetinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NHPP > 0
+ { hppname, hppopen, nulldev, nulldev,
+ hppoutput, hppgetstat, hppsetstat, nulldev,
+ hppsetinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NPAR > 0
+ { parname, paropen, nulldev, nulldev,
+ paroutput, pargetstat, parsetstat, nomap,
+ parsetinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+
+#if NDE6C > 0
+ { de6cname, de6copen, nulldev, nulldev,
+ de6coutput, de6cgetstat, de6csetstat, nomap,
+ de6csetinput, nulldev, nulldev, 0,
+ nodev },
+#endif
+#endif /* ! LINUX_DEV */
+
+#if NCOM > 0
+ { comname, comopen, comclose, comread,
+ comwrite, comgetstat, comsetstat, nomap,
+ nodev, nulldev, comportdeath, 0,
+ nodev },
+#endif
+
+#ifndef LINUX_DEV
+#if NLPR > 0
+ { lprname, lpropen, lprclose, lprread,
+ lprwrite, lprgetstat, lprsetstat, nomap,
+ nodev, nulldev, lprportdeath, 0,
+ nodev },
+#endif
+#endif /* ! LINUX_DEV */
+
+#if NBLIT > 0
+ { blitname, blitopen, blitclose, nodev,
+ nodev, blit_get_stat, nodev, blitmmap,
+ nodev, nodev, nodev, 0,
+ nodev },
+#endif
+
+ { mousename, mouseopen, mouseclose, mouseread,
+ nodev, nulldev, nulldev, nomap,
+ nodev, nulldev, nulldev, 0,
+ nodev },
+
+ { kbdname, kbdopen, kbdclose, kbdread,
+ nodev, kbdgetstat, kbdsetstat, nomap,
+ nodev, nulldev, nulldev, 0,
+ nodev },
+
+ { ioplname, ioplopen, ioplclose, nodev,
+ nodev, nodev, nodev, ioplmmap,
+ nodev, nulldev, nulldev, 0,
+ nodev },
+
+#if 0
+#if NHD > 0
+ { pchdname, pchdopen, hdclose, pchdread,
+ pchdwrite, pchdgetstat, pchdsetstat, nomap,
+ nodev, nulldev, nulldev, 16,
+ hddevinfo },
+#endif NHD > 0
+#endif
+
+#if 0
+#if NHD > 0
+ { hdname, hdopen, hdclose, hdread,
+ hdwrite, hdgetstat, hdsetstat, nomap,
+ nodev, nulldev, nulldev, 16,
+ hddevinfo },
+#endif NHD > 0
+#endif 0 /* Kevin doesn't know why this was here. */
+
+};
+int dev_name_count = sizeof(dev_name_list)/sizeof(dev_name_list[0]);
+
+/*
+ * Indirect list.
+ */
+struct dev_indirect dev_indirect_list[] = {
+
+ /* console */
+ { "console", &dev_name_list[0], 0 }
+};
+int dev_indirect_count = sizeof(dev_indirect_list)
+ / sizeof(dev_indirect_list[0]);
diff --git a/i386/i386at/cons_conf.c b/i386/i386at/cons_conf.c
new file mode 100644
index 00000000..49dc0238
--- /dev/null
+++ b/i386/i386at/cons_conf.c
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 1988-1994, The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Utah $Hdr: cons_conf.c 1.7 94/12/14$
+ */
+
+/*
+ * This entire table could be autoconfig()ed but that would mean that
+ * the kernel's idea of the console would be out of sync with that of
+ * the standalone boot. I think it best that they both use the same
+ * known algorithm unless we see a pressing need otherwise.
+ */
+#include <sys/types.h>
+#include <cons.h>
+#include <com.h>
+#include <rc.h>
+
+extern int kdcnprobe(), kdcninit(), kdcngetc(), kdcnputc();
+#if NCOM > 0 && RCLINE >= 0
+extern int comcnprobe(), comcninit(), comcngetc(), comcnputc();
+#endif
+
+/*
+ * The rest of the consdev fields are filled in by the respective
+ * cnprobe routine.
+ */
+struct consdev constab[] = {
+ {"kd", kdcnprobe, kdcninit, kdcngetc, kdcnputc},
+#if NCOM > 0 && RCLINE >= 0 && 1
+ {"com", comcnprobe, comcninit, comcngetc, comcnputc},
+#endif
+ {0}
+};
diff --git a/i386/i386at/cram.h b/i386/i386at/cram.h
new file mode 100644
index 00000000..8373ce03
--- /dev/null
+++ b/i386/i386at/cram.h
@@ -0,0 +1,75 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * cram.h
+ */
+
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * outb(CMOS_ADDR, addr);
+ * result = inb(CMOS_DATA);
+ *
+ * where "addr" tells what value you want to read (some are listed
+ * below). Interrupts should be disabled while you do this.
+ */
+
+/* I/O ports */
+
+#define CMOS_ADDR 0x70 /* port for CMOS ram address */
+#define CMOS_DATA 0x71 /* port for CMOS ram data */
+
+
+/* Addresses, related masks, and potential results */
+
+#define CMOS_EB 0x14 /* read Equipment Byte */
+#define CM_SCRMSK 0x30 /* mask for EB query to get screen */
+#define CM_EGA_VGA 0x00 /* "not CGA or MONO" */
+#define CM_CGA_40 0x10
+#define CM_CGA_80 0x20
+#define CM_MONO_80 0x30
+
diff --git a/i386/i386at/dev_hdr.h b/i386/i386at/dev_hdr.h
new file mode 100644
index 00000000..7af644b8
--- /dev/null
+++ b/i386/i386at/dev_hdr.h
@@ -0,0 +1,43 @@
+/*
+ * Mach device definitions (i386at version).
+ *
+ * Copyright (c) 1996 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+#ifndef _I386AT_DEV_HDR_H_
+#define _I386AT_DEV_HDR_H_
+
+struct device_emulation_ops;
+
+/* This structure is associated with each open device port.
+ The port representing the device points to this structure. */
+struct device
+{
+ struct device_emulation_ops *emul_ops;
+ void *emul_data;
+};
+
+typedef struct device *device_t;
+
+#define DEVICE_NULL ((device_t) 0)
+
+#endif /* _I386AT_DEV_HDR_H_ */
diff --git a/i386/i386at/device_emul.h b/i386/i386at/device_emul.h
new file mode 100644
index 00000000..957bd505
--- /dev/null
+++ b/i386/i386at/device_emul.h
@@ -0,0 +1,64 @@
+/*
+ * Mach device emulation definitions (i386at version).
+ *
+ * Copyright (c) 1996 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+#ifndef _I386AT_DEVICE_EMUL_H_
+#define _I386AT_DEVICE_EMUL_H_
+
+#include <mach/notify.h>
+#include <device/net_status.h>
+
+/* Each emulation layer provides these operations. */
+struct device_emulation_ops
+{
+ void (*reference) (void *);
+ void (*dealloc) (void *);
+ ipc_port_t (*dev_to_port) (void *);
+ io_return_t (*open) (ipc_port_t, mach_msg_type_name_t,
+ dev_mode_t, char *, device_t *);
+ io_return_t (*close) (void *);
+ io_return_t (*write) (void *, ipc_port_t, mach_msg_type_name_t,
+ dev_mode_t, recnum_t, io_buf_ptr_t, unsigned, int *);
+ io_return_t (*write_inband) (void *, ipc_port_t, mach_msg_type_name_t,
+ dev_mode_t, recnum_t, io_buf_ptr_inband_t,
+ unsigned, int *);
+ io_return_t (*read) (void *, ipc_port_t, mach_msg_type_name_t,
+ dev_mode_t, recnum_t, int, io_buf_ptr_t *, unsigned *);
+ io_return_t (*read_inband) (void *, ipc_port_t, mach_msg_type_name_t,
+ dev_mode_t, recnum_t, int, char *, unsigned *);
+ io_return_t (*set_status) (void *, dev_flavor_t, dev_status_t,
+ mach_msg_type_number_t);
+ io_return_t (*get_status) (void *, dev_flavor_t, dev_status_t,
+ mach_msg_type_number_t *);
+ io_return_t (*set_filter) (void *, ipc_port_t, int, filter_t [], unsigned);
+ io_return_t (*map) (void *, vm_prot_t, vm_offset_t,
+ vm_size_t, ipc_port_t *, boolean_t);
+ void (*no_senders) (mach_no_senders_notification_t *);
+ io_return_t (*write_trap) (void *, dev_mode_t,
+ recnum_t, vm_offset_t, vm_size_t);
+ io_return_t (*writev_trap) (void *, dev_mode_t,
+ recnum_t, io_buf_vec_t *, vm_size_t);
+};
+
+#endif /* _I386AT_DEVICE_EMUL_H_ */
diff --git a/i386/i386at/disk.h b/i386/i386at/disk.h
new file mode 100644
index 00000000..e1fe6b98
--- /dev/null
+++ b/i386/i386at/disk.h
@@ -0,0 +1,186 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * disk.h
+ */
+
+/* Grab the public part. */
+#include <mach/machine/disk.h>
+
+
+
+#define MAX_ALTENTS 253 /* Maximum # of slots for alts */
+ /* allowed for in the table. */
+
+#define ALT_SANITY 0xdeadbeef /* magic # to validate alt table */
+
+struct alt_table {
+ u_short alt_used; /* # of alternates already assigned */
+ u_short alt_reserved; /* # of alternates reserved on disk */
+ long alt_base; /* 1st sector (abs) of the alt area */
+ long alt_bad[MAX_ALTENTS]; /* list of bad sectors/tracks */
+};
+
+struct alt_info { /* table length should be multiple of 512 */
+ long alt_sanity; /* to validate correctness */
+ u_short alt_version; /* to corroborate vintage */
+ u_short alt_pad; /* padding for alignment */
+ struct alt_table alt_trk; /* bad track table */
+ struct alt_table alt_sec; /* bad sector table */
+};
+typedef struct alt_info altinfo_t;
+
+#define V_NUMPAR 16 /* maximum number of partitions */
+
+#define VTOC_SANE 0x600DDEEE /* Indicates a sane VTOC */
+#define PDLOCATION 29 /* location of VTOC */
+
+#define BAD_BLK 0x80 /* needed for V_VERIFY */
+/* BAD_BLK moved from old hdreg.h */
+
+
+#define HDPDLOC 29 /* location of pdinfo/vtoc */
+#define LBLLOC 1 /* label block for xxxbsd */
+
+/* Partition permission flags */
+#define V_OPEN 0x100 /* Partition open (for driver use) */
+#define V_VALID 0x200 /* Partition is valid to use */
+
+
+
+/* Sanity word for the physical description area */
+#define VALID_PD 0xCA5E600D
+
+struct localpartition {
+ u_int p_flag; /*permision flags*/
+ long p_start; /*physical start sector no of partition*/
+ long p_size; /*# of physical sectors in partition*/
+};
+typedef struct localpartition localpartition_t;
+
+struct evtoc {
+ u_int fill0[6];
+ u_int cyls; /*number of cylinders per drive*/
+ u_int tracks; /*number tracks per cylinder*/
+ u_int sectors; /*number sectors per track*/
+ u_int fill1[13];
+ u_int version; /*layout version*/
+ u_int alt_ptr; /*byte offset of alternates table*/
+ u_short alt_len; /*byte length of alternates table*/
+ u_int sanity; /*to verify vtoc sanity*/
+ u_int xcyls; /*number of cylinders per drive*/
+ u_int xtracks; /*number tracks per cylinder*/
+ u_int xsectors; /*number sectors per track*/
+ u_short nparts; /*number of partitions*/
+ u_short fill2; /*pad for 286 compiler*/
+ char label[40];
+ struct localpartition part[V_NUMPAR];/*partition headers*/
+ char fill[512-352];
+};
+
+union io_arg {
+ struct {
+ u_short ncyl; /* number of cylinders on drive */
+ u_char nhead; /* number of heads/cyl */
+ u_char nsec; /* number of sectors/track */
+ u_short secsiz; /* number of bytes/sector */
+ } ia_cd; /* used for Configure Drive cmd */
+ struct {
+ u_short flags; /* flags (see below) */
+ long bad_sector; /* absolute sector number */
+ long new_sector; /* RETURNED alternate sect assigned */
+ } ia_abs; /* used for Add Bad Sector cmd */
+ struct {
+ u_short start_trk; /* first track # */
+ u_short num_trks; /* number of tracks to format */
+ u_short intlv; /* interleave factor */
+ } ia_fmt; /* used for Format Tracks cmd */
+ struct {
+ u_short start_trk; /* first track */
+ char *intlv_tbl; /* interleave table */
+ } ia_xfmt; /* used for the V_XFORMAT ioctl */
+};
+
+
+#define BOOTSZ 446 /* size of boot code in master boot block */
+#define FD_NUMPART 4 /* number of 'partitions' in fdisk table */
+#define ACTIVE 128 /* indicator of active partition */
+#define BOOT_MAGIC 0xAA55 /* signature of the boot record */
+#define UNIXOS 99 /* UNIX partition */
+#define BSDOS 165
+#define LINUXSWAP 130
+#define LINUXOS 131
+extern int OS; /* what partition we came from */
+
+/*
+ * structure to hold the fdisk partition table
+ */
+struct ipart {
+ u_char bootid; /* bootable or not */
+ u_char beghead; /* beginning head, sector, cylinder */
+ u_char begsect; /* begcyl is a 10-bit number. High 2 bits */
+ u_char begcyl; /* are in begsect. */
+ u_char systid; /* OS type */
+ u_char endhead; /* ending head, sector, cylinder */
+ u_char endsect; /* endcyl is a 10-bit number. High 2 bits */
+ u_char endcyl; /* are in endsect. */
+ long relsect; /* first sector relative to start of disk */
+ long numsect; /* number of sectors in partition */
+};
+
+/*
+ * structure to hold master boot block in physical sector 0 of the disk.
+ * Note that partitions stuff can't be directly included in the structure
+ * because of lameo '386 compiler alignment design.
+ */
+struct mboot { /* master boot block */
+ char bootinst[BOOTSZ];
+ char parts[FD_NUMPART * sizeof(struct ipart)];
+ u_short signature;
+};
+
diff --git a/i386/i386at/ds8390.h b/i386/i386at/ds8390.h
new file mode 100644
index 00000000..a91e6427
--- /dev/null
+++ b/i386/i386at/ds8390.h
@@ -0,0 +1,166 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)ds8390.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * Nominal Semidestructor DS8390 Ethernet Chip
+ * Register and bit definitions
+ */
+
+/*
+ * Page register offset values
+ */
+#define ds_cmd 0x00 /* Command register: */
+#define DSCM_STOP 0x01 /* Stop controller */
+#define DSCM_START 0x02 /* Start controller */
+#define DSCM_TRANS 0x04 /* Transmit packet */
+#define DSCM_RREAD 0x08 /* Remote read */
+#define DSCM_RWRITE 0x10 /* Remote write */
+#define DSCM_NODMA 0x20 /* No Remote DMA present */
+#define DSCM_PG0 0x00 /* Select Page 0 */
+#define DSCM_PG1 0x40 /* Select Page 1 */
+#define DSCM_PG2 0x80 /* Select Page 2? */
+
+#define ds0_pstart 0x01 /* Page Start register */
+#define ds0_pstop 0x02 /* Page Stop register */
+#define ds0_bnry 0x03 /* Boundary Pointer */
+#define ds0_bndy ds0_bnry /* Boundary Pointer */
+
+#define ds0_tsr 0x04 /* Transmit Status (read-only) */
+#define DSTS_PTX 0x01 /* Successful packet transmit */
+#define DSTS_COLL 0x04 /* Packet transmit w/ collision*/
+#define DSTS_COLL16 0x04 /* Packet had >16 collisions & fail */
+#define DSTS_ABT 0x08 /* Transmit aborted */
+#define DSTS_CRS 0x10 /* Carrier sense lost/xmit !aborted */
+#define DSTS_UND 0x20 /* FIFO Underrun on transmission*/
+#define DSTS_CDH 0x40 /* CD heartbeat */
+#define DSTS_OWC 0x80 /* Out of window collision - */
+ /* transmit not aborted */
+
+#define ds0_tpsr ds0_tsr /* Transmit Page (write-only) */
+#define ds0_tbcr0 0x05 /* Transmit Byte count, low WO */
+#define ds0_tbcr1 0x06 /* Transmit Byte count, high WO */
+
+#define ds0_isr 0x07 /* Interrupt status register */
+#define DSIS_RX 0x01 /* Successful packet reception */
+#define DSIS_TX 0x02 /* Successful packet transmission */
+#define DSIS_RXE 0x04 /* Packet reception w/error */
+#define DSIS_TXE 0x08 /* Packet transmission w/error*/
+#define DSIS_ROVRN 0x10 /* Receiver overrun in the ring*/
+#define DSIS_CTRS 0x20 /* Diagnostic counters need attn */
+#define DSIS_RDC 0x40 /* Remote DMA Complete */
+#define DSIS_RESET 0x80 /* Reset Complete */
+
+#define ds0_rsar0 0x08 /* Remote start address low WO */
+#define ds0_rsar1 0x09 /* Remote start address high WO */
+#define ds0_rbcr0 0x0A /* Remote byte count low WO */
+#define ds0_rbcr1 0x0B /* Remote byte count high WO */
+
+#define ds0_rsr 0x0C /* Receive status RO */
+#define ds0_cntr0 0x0D /* Receive status RO */
+#define ds0_cntr1 0x0E /* Receive status RO */
+#define ds0_cntr2 0x0F /* Receive status RO */
+#define DSRS_RPC 0x01 /* Received Packet Complete */
+#define DSRS_CRC 0x02 /* CRC error */
+#define DSRS_FAE 0x04 /* Frame alignment error */
+#define DSRS_FO 0x08 /* FIFO Overrun */
+#define DSRS_MPA 0x10 /* Missed packet */
+#define DSRS_PHY 0x20 /* Physical/multicast address */
+#define DSRS_DIS 0x40 /* Receiver disable */
+#define DSRS_DFR 0x80 /* Deferring */
+
+#define ds0_rcr ds0_rsr /* Receive configuration WO */
+#define DSRC_SEP 0x01 /* Save error packets */
+#define DSRC_AR 0x02 /* Accept Runt packets */
+#define DSRC_AB 0x04 /* Accept Broadcast packets */
+#define DSRC_AM 0x08 /* Accept Multicast packets */
+#define DSRC_PRO 0x10 /* Promiscuous physical */
+#define DSRC_MON 0x20 /* Monitor mode */
+
+#define ds0_tcr 0x0D /* Transmit configuration WO */
+#define DSTC_CRC 0x01 /* Inhibit CRC */
+#define DSTC_LB0 0x02 /* Encoded Loopback Control */
+#define DSTC_LB1 0x04 /* Encoded Loopback Control */
+#define DSTC_ATD 0x08 /* Auto Transmit Disable */
+#define DSTC_OFST 0x10 /* Collision Offset Enable */
+
+#define ds0_rcvalctr ds0_tcr /* Receive alignment err ctr RO */
+
+#define ds0_dcr 0x0E /* Data configuration WO */
+#define DSDC_WTS 0x01 /* Word Transfer Select */
+#define DSDC_BOS 0x02 /* Byte Order Select */
+#define DSDC_LAS 0x04 /* Long Address Select */
+#define DSDC_BMS 0x08 /* Burst Mode Select */
+#define DSDC_AR 0x10 /* Autoinitialize Remote */
+#define DSDC_FT0 0x20 /* Fifo Threshold Select */
+#define DSDC_FT1 0x40 /* Fifo Threshold Select */
+
+#define ds0_rcvcrcctr ds0_dcr /* Receive CRC error counter RO */
+
+#define ds0_imr 0x0F /* Interrupt mask register WO */
+#define DSIM_PRXE 0x01 /* Packet received enable */
+#define DSIM_PTXE 0x02 /* Packet transmitted enable */
+#define DSIM_RXEE 0x04 /* Receive error enable */
+#define DSIM_TXEE 0x08 /* Transmit error enable */
+#define DSIM_OVWE 0x10 /* Overwrite warning enable */
+#define DSIM_CNTE 0x20 /* Counter overflow enable */
+#define DSIM_RDCE 0x40 /* Dma complete enable */
+
+
+/* We DON'T enable Counter Overflow and Remote DMA complete. */
+#define IMR_ENABLE (DSIM_PRXE|DSIM_PTXE|DSIM_RXEE|DSIM_TXEE|DSIM_OVWE)
+
+#define ds0_rcvfrmctr ds0_imr /* Receive Frame error cntr RO */
+
+
+#define ds1_par0 ds0_pstart /* Physical address register 0 */
+ /* Physical address registers 1-4 */
+#define ds1_par5 ds0_tbcr1 /* Physical address register 5 */
+#define ds1_curr ds0_isr /* Current page (receive unit) */
+#define ds1_mar0 ds0_rsar0 /* Multicast address register 0 */
+ /* Multicast address registers 1-6 */
+#define ds1_mar7 ds0_imr /* Multicast address register 7 */
+#define ds1_curr ds0_isr /* Current page (receive unit) */
+
+#define DS_PGSIZE 256 /* Size of RAM pages in bytes */
+
+/*
+ * Packet receive header, 1 per each buffer page used in receive packet
+ */
+struct prhdr {
+ u_char pr_status; /* is this a good packet, same as ds0_rsr */
+ u_char pr_nxtpg; /* next page of packet or next packet */
+ u_char pr_sz0;
+ u_char pr_sz1;
+};
diff --git a/i386/i386at/eisa.h b/i386/i386at/eisa.h
new file mode 100644
index 00000000..33629ca0
--- /dev/null
+++ b/i386/i386at/eisa.h
@@ -0,0 +1,110 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Copyright 1992 by Open Software Foundation,
+ * Grenoble, FRANCE
+ *
+ * All Rights Reserved
+ *
+ * Permission to use, copy, modify, and distribute this software and
+ * its documentation for any purpose and without fee is hereby granted,
+ * provided that the above copyright notice appears in all copies and
+ * that both the copyright notice and this permission notice appear in
+ * supporting documentation, and that the name of OSF or Open Software
+ * Foundation not be used in advertising or publicity pertaining to
+ * distribution of the software without specific, written prior
+ * permission.
+ *
+ * OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+ * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+ * IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+ * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+ * LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+ * NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+ * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+/*
+ * Eisa defs
+ */
+
+#ifndef _I386AT_EISA_H_
+#define _I386AT_EISA_H_
+
+#include <mach/boolean.h>
+
+#if EISA
+extern boolean_t is_eisa_bus;
+
+#define EISA_ID_REG(board, byte) (0xc80 | (byte) | ((board) << 12))
+
+#define EISA_ID_REG_0 0x0
+#define EISA_ID_REG_1 0x1
+#define EISA_ID_REG_2 0x2
+#define EISA_ID_REG_3 0x3
+
+#define EISA_SYSTEM_BOARD 0x0
+
+struct std_board_id {
+ unsigned revision: 8, /* Revision number */
+ product: 8; /* Product number */
+};
+
+struct sys_board_id {
+ unsigned bus_vers: 3, /* EISA bus version */
+ reserved: 13; /* Manufacturer reserved */
+};
+
+struct board_id {
+ union {
+ struct sys_board_id sys_id;
+ struct std_board_id std_id;
+ } bd_id;
+ unsigned name_char_2: 5, /* 3nd compressed char */
+ name_char_1: 5, /* 2nd compressed char */
+ name_char_0: 5, /* 1st compressed char */
+ not_eisa: 1; /* 0 if eisa board */
+};
+
+union eisa_board_id {
+ unsigned char byte[4];
+ struct board_id id;
+};
+
+typedef union eisa_board_id eisa_board_id_t;
+
+
+/* Additional DMA registers */
+
+#define DMA0HIPAGE 0x481 /* DMA 0 address: bits 24-31 */
+#define DMA0HICNT 0x405 /* DMA 0 count: bits 16-23 */
+
+
+#else /* EISA */
+#define is_eisa_bus FALSE
+#define probe_eisa()
+#endif /* EISA */
+
+#endif /* _I386AT_EISA_H_ */
diff --git a/i386/i386at/fd.c b/i386/i386at/fd.c
new file mode 100644
index 00000000..773411b4
--- /dev/null
+++ b/i386/i386at/fd.c
@@ -0,0 +1,1701 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* Copyright (c) 1987, 1988 TOSHIBA Corp. */
+/* All Rights Reserved */
+
+#if 0
+
+#include <fd.h>
+
+#ifdef MACH_KERNEL
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include <device/buf.h>
+#include <device/errno.h>
+#else MACH_KERNEL
+#include <sys/buf.h>
+#include <sys/errno.h>
+#include <sys/user.h>
+#include <sys/ioctl.h>
+#endif MACH_KERNEL
+#include <i386/pio.h>
+#include <i386/machspl.h>
+#include <chips/busses.h>
+#include <i386at/fdreg.h>
+#include <i386at/disk.h>
+#include <vm/vm_kern.h>
+
+#ifdef DEBUG
+#define D(x) x
+#define DD(x) x
+#else /* DEBUG */
+#define D(x)
+#define DD(x)
+#endif /* DEBUG */
+
+/*
+ * Floppy Device-Table Definitions (drtabs)
+ *
+ * Cyls,Sec,spc,part,Mtype,RWFpl,FGpl
+ */
+struct fddrtab m765f[] = { /* format table */
+ 80, 18, 1440, 9, 0x88, 0x2a, 0x50, /* [0] 3.50" 720 Kb */
+ 80, 36, 2880, 18, 0x08, 0x1b, 0x6c, /* [1] 3.50" 1.44 Meg */
+ 40, 18, 720, 9, 0xa8, 0x2a, 0x50, /* [2] 5.25" 360 Kb */
+ 80, 30, 2400, 15, 0x08, 0x1b, 0x54 /* [3] 5.25" 1.20 Meg */
+};
+
+/*
+ * The following are static initialization variables
+ * which are based on the configuration.
+ */
+struct ctrl_info ctrl_info[MAXUNIT>>1] = { /* device data table */
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 } ,
+ { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }
+};
+
+struct unit_info unit_info[MAXUNIT]; /* unit buffer headers */
+
+char *fderr = "FD Error on unit";
+char *fdmsg[] = {
+ "?",
+ "Missing data address mark",
+ "Write protected",
+ "Sector not found",
+ "Data Overrun", /* Over run error */
+ "Uncorrectable data read error", /* CRC Error */
+ "FDC Error",
+ "Illegal format type",
+ "Drive not ready",
+ "diskette not present - please insert",
+ "Illegal interrupt type"
+};
+
+struct buf fdrbuf[MAXUNIT]; /* data transfer buffer structures */
+
+int fdminphys();
+int fdintr(), fdprobe(), fdslave();
+void fdattach();
+int FdDmaEISA = 0;
+int FdDmaThreshold = 16 * 1024 * 1024;
+vm_offset_t FdDmaPage = (vm_offset_t) 0;
+vm_offset_t fd_std[NFD] = { 0 };
+struct bus_device *fd_dinfo[NFD*2];
+struct bus_ctlr *fd_minfo[NFD];
+struct bus_driver fddriver =
+ {fdprobe, fdslave, fdattach, 0, fd_std, "fd", fd_dinfo, "fdc", fd_minfo, 0};
+
+int m765verify[MAXUNIT] = {1,1,1,1}; /* write after read flag */
+ /* 0 != verify mode */
+ /* 0 == not verify mode */
+#ifdef MACH_KERNEL
+extern struct buf *geteblk();
+#endif MACH_KERNEL
+
+#define trfrate(uip, type) outb(VFOREG(uip->addr),(((type)&RATEMASK)>>6))
+#define rbskrate(uip, type) trfrate(uip,(type)&RAPID?RPSEEK:NMSEEK)
+#define getparm(type) ((type<0||type>3)?(struct fddrtab *)ERROR:&m765f[type])
+#define relative(s1,s2) ((s1)>(s2)?(s1)-(s2):(s2)-(s1))
+
+fdprobe(port, ctlr)
+struct bus_ctlr *ctlr;
+{
+ int spot = STSREG((int) ctlr->address);
+ struct ctrl_info *cip = &ctrl_info[ctlr->unit];
+ int i, in;
+
+ outb(spot, DATAOK);
+ for (i = 1000; i--;) {
+ in = inb(spot);
+ if ((in&DATAOK) == DATAOK && !(in&0x0f)) {
+ take_ctlr_irq(ctlr);
+ cip->b_cmd.c_rbmtr = 0; /* recalibrate/moter flag */
+ cip->b_cmd.c_intr = CMDRST; /* interrupt flag */
+ cip->b_unitf = 0;
+ cip->b_uip = 0;
+ cip->b_rwerr = cip->b_seekerr = cip->b_rberr = 0;
+ cip->usebuf = 0;
+ if (FdDmaPage) {
+ cip->b_pbuf = FdDmaPage + PAGE_SIZE * ctlr->unit;
+ if (kmem_alloc_pageable(kernel_map,
+ (vm_offset_t *)&cip->b_vbuf,
+ PAGE_SIZE) != KERN_SUCCESS) {
+ printf("%s%d: can not kmem_alloc_pageable.\n",
+ ctlr->name, ctlr->unit);
+ return 0;
+ }
+ (void)pmap_map(cip->b_vbuf,
+ (vm_offset_t)cip->b_pbuf,
+ (vm_offset_t)cip->b_pbuf+PAGE_SIZE,
+ VM_PROT_READ | VM_PROT_WRITE);
+ }
+ printf("%s%d: port = %x, spl = %d, pic = %d.\n", ctlr->name,
+ ctlr->unit, ctlr->address, ctlr->sysdep, ctlr->sysdep1);
+ return(1);
+ }
+ }
+ return(0);
+}
+
+fdslave(dev, xxxx)
+struct bus_device *dev;
+{
+ return(1); /* gross hack */
+}
+
+void fdattach(dev)
+struct bus_device *dev;
+{
+ struct unit_info *uip = &unit_info[dev->unit];
+ struct ctrl_info *cip = &ctrl_info[dev->ctlr];
+
+ uip->dev = dev;
+ dev->address = dev->mi->address;
+ uip->addr = dev->address;
+ uip->b_cmd = &cip->b_cmd;
+ uip->b_seekaddr = 0;
+ uip->av_forw = 0;
+ uip->wakeme = 0;
+ if (cip->b_unitf) {
+ uip->b_unitf=cip->b_unitf->b_unitf;
+ cip->b_unitf->b_unitf=uip;
+ } else {
+ uip->b_unitf=uip;
+ cip->b_unitf=uip;
+ }
+ uip->d_drtab.dr_type &= ~OKTYPE;
+
+ printf(", port = %x, spl = %d, pic = %d.",
+ dev->address, dev->sysdep, dev->sysdep1);
+
+ rstout(uip);
+ specify(uip);
+}
+/*****************************************************************************
+ *
+ * TITLE: fdopen
+ *
+ * ABSTRACT: Open a unit.
+ *
+ ****************************************************************************/
+fdopen(dev, flag, otyp)
+dev_t dev;
+int flag; /* not used */
+int otyp; /* not used */
+{
+ struct fddrtab *driv;
+ struct buf *wbp;
+ spl_t x = SPL();
+ int error = 0;
+ int unit = UNIT(dev);
+ struct unit_info *uip = &unit_info[unit];
+ int slave = uip->dev->slave;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+ if (unit < MAXUNIT){
+ /* Since all functions that use this are called from open, we only
+ set this once, right here. */
+ rstout(uip);
+ cip->b_wup = uip;
+ openchk(cmdp);
+ cmdp->c_devflag |= FDMCHK;
+ chkbusy(cmdp);
+ cmdp->c_stsflag |= MTRFLAG;
+ mtr_on(uip);
+ if(inb(VFOREG(uip->addr))&OPENBIT ||
+ !(uip->d_drtab.dr_type&OKTYPE)){
+ uip->d_drtab.dr_type &= ~OKTYPE;
+ if(!rbrate(RAPID, uip))
+ fdseek(RAPID, uip, 2);
+ if(inb(VFOREG(uip->addr))&OPENBIT)
+ error = ENXIO;
+ }
+ cmdp->c_stsflag &= ~MTRFLAG;
+ mtr_on(uip);
+ openfre(cmdp);
+ if(!error && !(uip->d_drtab.dr_type & OKTYPE)) {
+ if (MEDIATYPE(dev)>3)
+ goto endopen;
+ driv = &m765f[MEDIATYPE(dev)];
+ wbp = geteblk(BLKSIZE);
+ m765sweep(uip, driv);
+ cmdp->c_rbmtr &= ~(1<<(RBSHIFT+(slave)));
+ ++cip->b_rwerr;
+ wbp->b_dev = dev; wbp->b_error = 0; wbp->b_resid = 0;
+ wbp->b_flags = (B_READ|B_VERIFY); wbp->b_bcount = 512;
+ wbp->b_pfcent = 2*driv->dr_spc + driv->dr_nsec - 1;
+ setqueue(wbp, uip);
+ biowait(wbp);
+ brelse(wbp);
+ error = 0;
+ uip->d_drtab.dr_type |= OKTYPE;
+ }
+ } else
+ error = ENXIO;
+ endopen:
+ splx(x);
+ return(error);
+}
+/*****************************************************************************
+ *
+ * TITLE: fdclose
+ *
+ * ABSTRACT: Close a unit.
+ *
+ * Called on last close. mark the unit closed and not-ready.
+ *
+ * Unix doesn't actually "open" an inode for rootdev, swapdev or pipedev.
+ * If UNIT(swapdev) != UNIT(rootdev), then must add code in init() to
+ * "open" swapdev. These devices should never be closed.
+ *
+ *****************************************************************************/
+fdclose(dev, flag, otyp, offset)
+dev_t dev; /* major, minor numbers */
+int flag; /* not used */
+int otyp; /* not used */
+off_t offset; /* not used */
+{
+ extern dev_t rootdev, swapdev;
+ struct unit_info *uip = &unit_info[UNIT(dev)];
+ spl_t s;
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if ((dev == rootdev) || (dev == swapdev)) /* never close these */
+ return(0);
+#endif MACH_KERNEL
+
+ /* Clear the bit.
+ * If last close of drive insure drtab queue is empty before returning.
+ */
+ s = SPL();
+ while(uip->av_forw != 0) {
+ uip->wakeme = 1;
+ sleep(uip, PRIBIO);
+ }
+ splx(s);
+#ifdef MACH_KERNEL
+ return(0);
+#else MACH_KERNEL
+ close(0);
+#endif MACH_KERNEL
+}
+/*****************************************************************************
+ *
+ * TITLE: fdstrategy
+ *
+ * ABSTRACT: Queue an I/O Request, and start it if not busy already.
+ *
+ * Reject request if unit is not-ready.
+ *
+ * Note: check for not-ready done here ==> could get requests
+ * queued prior to unit going not-ready.
+ * not-ready status to those requests that are attempted
+ * before a new volume is inserted. Once a new volume is
+ * inserted, would get good I/O's to wrong volume.
+ *
+ * CALLS: iodone(),setqueue()
+ *
+ * CALLING ROUTINES: fdread (indirectly, thru physio)
+ * fdwrite (indirectly, thru physio)
+ *
+ ****************************************************************************/
+fdstrategy(bp)
+struct buf *bp; /* buffer header */
+{
+ unsigned bytes_left;
+ daddr_t secno;
+ struct unit_info *uip = &unit_info[UNIT(bp->b_dev)];
+ struct fddrtab *dr = &uip->d_drtab;
+ struct fddrtab *sdr;
+
+ bp->b_error = 0;
+ /* set b_resid to b_bcount because we haven't done anything yet */
+ bp->b_resid = bp->b_bcount;
+ if (!(dr->dr_type & OKTYPE) ||
+ ((sdr = getparm(MEDIATYPE(bp->b_dev)))==(struct fddrtab *)ERROR) ||
+ /* wrong parameters */
+ (sdr->dr_ncyl != dr->dr_ncyl) || (sdr->dr_nsec != dr->dr_nsec) ||
+ ((sdr->dr_type|OKTYPE) != dr->dr_type) ||
+ (sdr->dr_rwgpl != dr->dr_rwgpl) ||
+ (sdr->dr_fgpl != dr->dr_fgpl)) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ biodone(bp);
+ return(0);
+ }
+ /*
+ * Figure "secno" from b_blkno. Adjust sector # for partition.
+ *
+ * If reading just past the end of the device, it's
+ * End of File. If not reading, or if read starts further in
+ * than the first sector after the partition, it's an error.
+ *
+ * secno is logical blockno / # of logical blocks per sector */
+ secno = (bp->b_blkno * NBPSCTR) >> 9;
+ if (secno >= dr->p_nsec) {
+ if (!((bp->b_flags & B_READ) && (secno == dr->p_nsec))){
+ /* off the deep end */
+ bp->b_flags |= B_ERROR;
+ bp->b_error = ENXIO;
+ }
+ biodone(bp);
+ return(0);
+ }
+/* At this point, it is no longer possible to directly return from strategy.
+ We now set b_resid to the number of bytes we cannot transfer because
+ they lie beyond the end of the request's partition. This value is 0
+ if the entire request is within the partition. */
+ bytes_left = (dr->p_nsec - secno) << 9;
+ bp->b_resid = ((bp->b_bcount<=bytes_left)?0:(bp->b_bcount-bytes_left));
+ bp->b_pfcent = secno;
+ setqueue(bp, uip);
+ return(0);
+}
+
+/***************************************************************************
+ *
+ * set queue to buffer
+ *
+ ***************************************************************************/
+setqueue(bp, uip)
+struct buf *bp;
+struct unit_info *uip;
+{
+ spl_t x = SPL();
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ openchk(cmdp); /* openning check */
+ cmdp->c_devflag |= STRCHK;
+ fd_disksort(uip, bp); /* queue the request */
+ /*
+ * If no requests are in progress, start this one up. Else
+ * leave it on the queue, and fdintr will call m765io later.
+ */
+ if(!cip->b_uip)
+ m765io(uip);
+ splx(x);
+}
+/***************************************************************************
+ *
+ * check io_busy routine
+ *
+ ***************************************************************************/
+chkbusy(cmdp)
+struct fdcmd *cmdp;
+{
+ while(cmdp->c_devflag & STRCHK){
+ cmdp->c_devflag |= STRWAIT;
+ sleep(&cmdp->c_devflag,PZERO);
+ }
+}
+/***************************************************************************
+ *
+ * check fdopen() routine
+ *
+ ***************************************************************************/
+openchk(cmdp)
+struct fdcmd *cmdp;
+{
+ while(cmdp->c_devflag & FDMCHK ){
+ cmdp->c_devflag |= FDWAIT;
+ sleep(&cmdp->c_devflag,PZERO);
+ }
+}
+/***************************************************************************
+ *
+ * free fdopen() routine
+ *
+ ***************************************************************************/
+openfre(cmdp)
+struct fdcmd *cmdp;
+{
+ cmdp->c_devflag &= ~FDMCHK;
+ if(cmdp->c_devflag & FDWAIT){
+ cmdp->c_devflag &= ~FDWAIT;
+ wakeup(&cmdp->c_devflag);
+ }
+}
+/*****************************************************************************
+ *
+ * TITLE: m765io
+ *
+ * ABSTRACT: Start handling an I/O request.
+ *
+ ****************************************************************************/
+m765io(uip)
+struct unit_info *uip;
+{
+ extern int(m765iosub)();
+ register struct buf *bp;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+
+ bp = uip->av_forw; /*move bp to ctrl_info[ctrl].b_buf*/
+ cip->b_buf = bp;
+ cip->b_uip = uip;
+ cip->b_xferaddr = bp->b_un.b_addr;
+ cip->b_xfercount = bp->b_bcount - bp->b_resid;
+ cip->b_sector = bp->b_pfcent;
+ uip->b_cmd->c_stsflag |= MTRFLAG;
+ if(!mtr_start(uip))
+ timeout(m765iosub, uip, HZ);
+ else
+ m765iosub(uip);
+}
+/****************************************************************************
+ *
+ * m765io subroutine
+ *
+ ****************************************************************************/
+m765iosub(uip)
+struct unit_info *uip;
+{
+ struct fddrtab *dr = &uip->d_drtab;
+ int startsec;
+ int slave = uip->dev->slave;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ rwcmdset(uip);
+ if(cip->b_buf->b_flags&B_FORMAT)
+ goto skipchk;
+ startsec = (cmdp->c_rwdata[3] * dr->dr_nsec) + cmdp->c_rwdata[4];
+ if(startsec+(cip->b_xfercount>>9)-1 > dr->dr_spc)
+ cip->b_xferdma = (dr->dr_spc-startsec+1) << 9;
+ else
+skipchk: cip->b_xferdma = cip->b_xfercount;
+ if(!(cmdp->c_rbmtr & (1<<(RBSHIFT+slave))))
+ cip->b_status = rbirate(uip);
+ else if(uip->b_seekaddr != cmdp->c_saddr)
+ cip->b_status = fdiseek(uip,cmdp->c_saddr);
+ else
+ cip->b_status = outicmd(uip);
+ if(cip->b_status)
+ intrerr0(uip);
+ return;
+}
+/***************************************************************************
+ *
+ * read / write / format / verify command set to command table
+ *
+ ***************************************************************************/
+rwcmdset(uip)
+struct unit_info *uip;
+{
+ short resid;
+ int slave = uip->dev->slave;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ switch(cip->b_buf->b_flags&(B_FORMAT|B_VERIFY|B_READ|B_WRITE)){
+ case B_VERIFY|B_WRITE: /* VERIFY after WRITE */
+ cmdp->c_rwdata[0] = RDMV;
+ break;
+ case B_FORMAT:
+ cmdp->c_dcount = FMTCNT;
+ cmdp->c_rwdata[0] = FMTM;
+ cmdp->c_saddr = cip->b_sector / uip->d_drtab.dr_spc;
+ resid = cip->b_sector % uip->d_drtab.dr_spc;
+ cmdp->c_rwdata[1] = slave|((resid/uip->d_drtab.dr_nsec)<<2);
+ cmdp->c_rwdata[2] =
+ ((struct fmttbl *)cip->b_buf->b_un.b_addr)->s_type;
+ cmdp->c_rwdata[3] = uip->d_drtab.dr_nsec;
+ cmdp->c_rwdata[4] = uip->d_drtab.dr_fgpl;
+ cmdp->c_rwdata[5] = FMTDATA;
+ break;
+ case B_WRITE:
+ case B_READ:
+ case B_READ|B_VERIFY:
+ cmdp->c_dcount = RWCNT;
+ if(cip->b_buf->b_flags&B_READ)
+ if(cip->b_buf->b_flags&B_VERIFY)
+ cmdp->c_rwdata[0] = RDMV;
+ else
+ cmdp->c_rwdata[0] = RDM;
+ else
+ cmdp->c_rwdata[0] = WTM; /* format or write */
+ resid = cip->b_sector % uip->d_drtab.dr_spc;
+ cmdp->c_rwdata[3] = resid / uip->d_drtab.dr_nsec;
+ cmdp->c_rwdata[1] = slave|(cmdp->c_rwdata[3]<<2);
+ cmdp->c_rwdata[2] = cmdp->c_saddr =
+ cip->b_sector / uip->d_drtab.dr_spc;
+ cmdp->c_rwdata[4] = (resid % uip->d_drtab.dr_nsec) + 1;
+ cmdp->c_rwdata[5] = 2;
+ cmdp->c_rwdata[6] = uip->d_drtab.dr_nsec;
+ cmdp->c_rwdata[7] = uip->d_drtab.dr_rwgpl;
+ cmdp->c_rwdata[8] = DTL;
+ D(printf("SET %x %x C%x H%x S%x %x %x %x %x ",
+ cmdp->c_rwdata[0], cmdp->c_rwdata[1],
+ cmdp->c_rwdata[2], cmdp->c_rwdata[3],
+ cmdp->c_rwdata[4], cmdp->c_rwdata[5],
+ cmdp->c_rwdata[6], cmdp->c_rwdata[7],
+ cmdp->c_rwdata[8]));
+ break;
+ }
+}
+/*****************************************************************************
+ *
+ * TITLE: fdread
+ *
+ * ABSTRACT: "Raw" read. Use physio().
+ *
+ * CALLS: m765breakup (indirectly, thru physio)
+ *
+ ****************************************************************************/
+fdread(dev, uio)
+register dev_t dev;
+struct uio *uio;
+{
+#ifdef MACH_KERNEL
+ /* no need for page-size restriction */
+ return (block_io(fdstrategy, minphys, uio));
+#else MACH_KERNEL
+ return(physio(fdstrategy,&fdrbuf[UNIT(dev)],dev,B_READ,fdminphys,uio));
+#endif MACH_KERNEL
+}
+/*****************************************************************************
+ *
+ * TITLE: fdwrite
+ *
+ * ABSTRACT: "Raw" write. Use physio().
+ *
+ * CALLS: m765breakup (indirectly, thru physio)
+ *
+ ****************************************************************************/
+fdwrite(dev, uio)
+register dev_t dev;
+struct uio *uio;
+{
+#ifdef MACH_KERNEL
+ /* no need for page-size restriction */
+ return (block_io(fdstrategy, minphys, uio));
+#else MACH_KERNEL
+ return(physio(fdstrategy,&fdrbuf[UNIT(dev)],dev,B_WRITE,fdminphys,uio));
+#endif MACH_KERNEL
+}
+/*****************************************************************************
+ *
+ * TITLE: fdminphys
+ *
+ * ABSTRACT: Trim buffer length if buffer-size is bigger than page size
+ *
+ * CALLS: physio
+ *
+ ****************************************************************************/
+fdminphys(bp)
+struct buf *bp;
+{
+ if (bp->b_bcount > PAGESIZ)
+ bp->b_bcount = PAGESIZ;
+}
+#ifdef MACH_KERNEL
+/* IOC_OUT only and not IOC_INOUT */
+io_return_t fdgetstat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int * data; /* pointer to OUT array */
+ unsigned int *count; /* OUT */
+{
+ switch (flavor) {
+
+ /* Mandatory flavors */
+
+ case DEV_GET_SIZE: {
+ int ret;
+ struct disk_parms p;
+
+ ret = fd_getparms(dev, &p);
+ if (ret) return ret;
+ data[DEV_GET_SIZE_DEVICE_SIZE] = p.dp_pnumsec * NBPSCTR;
+ data[DEV_GET_SIZE_RECORD_SIZE] = NBPSCTR;
+ *count = DEV_GET_SIZE_COUNT;
+ break;
+ }
+
+ /* Extra flavors */
+
+ case V_GETPARMS:
+ if (*count < sizeof (struct disk_parms)/sizeof (int))
+ return (D_INVALID_OPERATION);
+ *count = sizeof (struct disk_parms)/sizeof(int);
+ return (fd_getparms(dev, data));
+ default:
+ return (D_INVALID_OPERATION);
+ }
+}
+/* IOC_VOID or IOC_IN or IOC_INOUT */
+/*ARGSUSED*/
+io_return_t fdsetstat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int * data;
+ unsigned int count;
+{
+ int unit = UNIT(dev);
+ switch (flavor) {
+ case V_SETPARMS: /* Caller wants reset_parameters */
+ return(fd_setparms(unit,*(int *)data));
+ case V_FORMAT:
+ return(fd_format(dev,data));
+ case V_VERIFY: /* cmdarg : 0 == no verify, 0 != verify */
+ m765verify[unit] = *(int *)data;
+ return(D_SUCCESS);
+ default:
+ return(D_INVALID_OPERATION);
+ }
+}
+
+/*
+ * Get block size
+ */
+int
+fddevinfo(dev, flavor, info)
+dev_t dev;
+int flavor;
+char *info;
+{
+ register struct fddrtab *dr;
+ register struct fdpart *p;
+ register int result = D_SUCCESS;
+
+ switch (flavor) {
+ case D_INFO_BLOCK_SIZE:
+ dr = &unit_info[UNIT(dev)].d_drtab;
+
+ if(dr->dr_type & OKTYPE)
+ *((int *) info) = 512;
+ else
+ result = D_INVALID_OPERATION;
+
+ break;
+ default:
+ result = D_INVALID_OPERATION;
+ }
+
+ return(result);
+}
+#else MACH_KERNEL
+/*****************************************************************************
+ *
+ * TITLE: fdioctl
+ *
+ * ABSTRACT: m765 driver special functions.
+ *
+ * CALLING ROUTINES: kernel
+ *
+ ****************************************************************************/
+int
+fdioctl(dev, cmd, cmdarg, flag)
+dev_t dev; /* major, minor numbers */
+int cmd; /* command code */
+int *cmdarg; /* user structure with parameters */
+int flag; /* not used */
+{
+ register unsigned unit = UNIT(dev);
+ switch (cmd) {
+ case V_SETPARMS: /* Caller wants reset_parameters */
+ return(fd_setparms(unit,*cmdarg));
+ case V_GETPARMS: /* Caller wants device parameters */
+ return(fd_getparms(dev,cmdarg));
+ case V_FORMAT:
+ return(fd_format(dev,cmdarg));
+ case V_VERIFY: /* cmdarg : 0 == no verify, 0 != verify */
+ m765verify[unit] = *cmdarg;
+ return(0);
+ }
+ return(EINVAL);
+}
+#endif MACH_KERNEL
+/****************************************************************************
+ *
+ * set fd parameters
+ *
+ ****************************************************************************/
+int
+fd_setparms(unit, cmdarg)
+register unsigned int unit;
+long cmdarg;
+{
+ struct fddrtab *fdparm;
+ spl_t x;
+ struct unit_info *uip = &unit_info[unit];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ cmdp->c_rbmtr &= ~(1<<(RBSHIFT+uip->dev->slave));
+ if ((fdparm = getparm(MEDIATYPE(cmdarg))) == (struct fddrtab *)ERROR)
+ return(EINVAL);
+ x = SPL();
+ openchk(cmdp);
+ cmdp->c_devflag |= FDMCHK;
+ chkbusy(cmdp);
+ m765sweep(uip, fdparm);
+ uip->d_drtab.dr_type |= OKTYPE;
+ openfre(cmdp);
+ splx(x);
+ return(0);
+}
+/****************************************************************************
+ *
+ * get fd parameters
+ *
+ ****************************************************************************/
+int
+fd_getparms(dev,cmdarg)
+dev_t dev; /* major, minor numbers */
+int *cmdarg;
+{
+ struct disk_parms *diskp = (struct disk_parms *)cmdarg;
+ register struct fddrtab *dr = &unit_info[UNIT(dev)].d_drtab;
+
+ if(dr->dr_type & OKTYPE){
+ diskp->dp_type = DPT_FLOPPY;
+ diskp->dp_heads = 2;
+ diskp->dp_sectors = dr->dr_nsec;
+ diskp->dp_pstartsec = 0;
+ diskp->dp_cyls = dr->dr_ncyl;
+ diskp->dp_pnumsec = dr->p_nsec;
+ return(0);
+ }
+ return(ENXIO);
+}
+/****************************************************************************
+ *
+ * format command
+ *
+ ****************************************************************************/
+fd_format(dev,cmdarg)
+dev_t dev; /* major, minor numbers */
+int *cmdarg;
+
+{
+ register struct buf *bp;
+ register daddr_t track;
+ union io_arg *varg;
+ u_short num_trks;
+ register struct fddrtab *dr = &unit_info[UNIT(dev)].d_drtab;
+
+ if(!(dr->dr_type & OKTYPE))
+ return(EINVAL);
+ varg = (union io_arg *)cmdarg;
+ num_trks = varg->ia_fmt.num_trks;
+ track = (daddr_t)(varg->ia_fmt.start_trk*dr->dr_nsec);
+ if((track + (num_trks*dr->dr_nsec))>dr->p_nsec)
+ return(EINVAL);
+ bp = geteblk(BLKSIZE); /* get struct buf area */
+ while (num_trks>0) {
+ bp->b_flags &= ~B_DONE;
+ bp->b_dev = dev;
+ bp->b_error = 0; bp->b_resid = 0;
+ bp->b_flags = B_FORMAT;
+ bp->b_bcount = dr->dr_nsec * FMTID;
+ bp->b_blkno = (daddr_t)((track << 9) / NBPSCTR);
+ if(makeidtbl(bp->b_un.b_addr,dr,
+ varg->ia_fmt.start_trk++,varg->ia_fmt.intlv))
+ return(EINVAL);
+ fdstrategy(bp);
+ biowait(bp);
+ if(bp->b_error)
+ if((bp->b_error == (char)EBBHARD) ||
+ (bp->b_error == (char)EBBSOFT))
+ return(EIO);
+ else
+ return(bp->b_error);
+ num_trks--;
+ track += dr->dr_nsec;
+ }
+ brelse(bp);
+ return(0);
+}
+/****************************************************************************
+ *
+ * make id table for format
+ *
+ ****************************************************************************/
+makeidtbl(tblpt,dr,track,intlv)
+struct fmttbl *tblpt;
+struct fddrtab *dr;
+unsigned short track;
+unsigned short intlv;
+{
+ register int i,j,secno;
+
+ if(intlv >= dr->dr_nsec)
+ return(1);
+ for(i=0; i<dr->dr_nsec; i++)
+ tblpt[i].sector = 0;
+ for(i=0,j=0,secno=1; i<dr->dr_nsec; i++){
+ tblpt[j].cyl = track >> 1;
+ tblpt[j].head = track & 1;
+ tblpt[j].sector = secno++;
+ tblpt[j].s_type = 2;
+ if((j+=intlv) < dr->dr_nsec)
+ continue;
+ for(j-=dr->dr_nsec; j < dr->dr_nsec ; j++)
+ if(!tblpt[j].sector)
+ break;
+ }
+ return(0);
+}
+/*****************************************************************************
+ *
+ * TITLE: fdintr
+ *
+ * ABSTRACT: Handle interrupt.
+ *
+ * Interrupt procedure for m765 driver. Gets status of last
+ * operation and performs service function according to the
+ * type of interrupt. If it was an operation complete interrupt,
+ * switches on the current driver state and either declares the
+ * operation done, or starts the next operation
+ *
+ ****************************************************************************/
+fdintr(ctrl)
+int ctrl;
+{
+ extern int(m765intrsub)();
+ struct unit_info *uip = ctrl_info[ctrl].b_uip;
+ struct unit_info *wup = ctrl_info[ctrl].b_wup;
+ struct fdcmd *cmdp = &ctrl_info[ctrl].b_cmd;
+ if(cmdp->c_stsflag & INTROUT)
+ untimeout(fdintr, ctrl);
+ cmdp->c_stsflag &= ~INTROUT;
+ switch(cmdp->c_intr){
+ case RWFLAG:
+ rwintr(uip);
+ break;
+ case SKFLAG:
+ case SKEFLAG|SKFLAG:
+ case RBFLAG:
+ timeout(m765intrsub, uip, SEEKWAIT);
+ break;
+ case WUPFLAG:
+ cmdp->c_intr &= ~WUPFLAG;
+ wakeup(wup);
+ }
+ return(0);
+}
+/*****************************************************************************
+ *
+ * interrup subroutine (seek recalibrate)
+ *
+ *****************************************************************************/
+m765intrsub(uip)
+struct unit_info *uip;
+{
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+
+ if((cip->b_status = sis(uip))!= ST0OK)
+ switch(uip->b_cmd->c_intr){
+ case SKFLAG:
+ seekintr(uip);
+ break;
+ case SKEFLAG|SKFLAG:
+ seekintre(uip);
+ break;
+ case RBFLAG:
+ rbintr(uip);
+ }
+}
+/*****************************************************************************
+ *
+ * read / write / format / verify interrupt routine
+ *
+ *****************************************************************************/
+rwintr(uip)
+struct unit_info *uip;
+{
+ int rsult[7];
+ register int rtn, count;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ cmdp->c_intr &= ~RWFLAG;
+ if((cip->b_buf->b_flags&(B_READ|B_VERIFY))!=(B_READ|B_VERIFY))
+ if(inb(VFOREG(uip->addr))&OPENBIT){
+ if(cip->b_buf->b_flags&B_FORMAT){
+ cip->b_status = TIMEOUT;
+ intrerr0(uip);
+ } else {
+ if((inb(STSREG(uip->addr))&ST0OK)!=ST0OK)
+ printf("%s %d : %s\n",
+ fderr,
+ uip-unit_info,
+ fdmsg[DOORERR]);
+ rstout(uip);
+ specify(uip);
+ cmdp->c_rbmtr &= RBRST;
+ cmdp->c_intr |= SKEFLAG;
+ if(cmdp->c_saddr > 2)
+ fdiseek(uip, cmdp->c_saddr-2);
+ else
+ fdiseek(uip, cmdp->c_saddr+2);
+ }
+ return;
+ }
+ for( count = 0 ; count < 7 ; count++ ){
+ if(rtn = fdc_sts(FD_ISTS, uip)) /* status check */
+ goto rwend;
+ rsult[count] = inb(DATAREG(uip->addr));
+ }
+ rtn = 0;
+ if(rsult[0]&0xc0){
+ rtn = cmdp->c_rwdata[0]<<8;
+ if(rsult[0]&0x80){ rtn |= FDCERR; goto rwend; }
+ if(rsult[1]&0x80){ rtn |= NOREC; goto rwend; }
+ if(rsult[1]&0x20){ rtn |= CRCERR; goto rwend; }
+ if(rsult[1]&0x10){ rtn |= OVERRUN; goto rwend; }
+ if(rsult[1]&0x04){ rtn |= NOREC; goto rwend; }
+ if(rsult[1]&0x02){ rtn |= WTPRT; goto rwend; }
+ if(rsult[1]&0x01){ rtn |= ADDRERR; goto rwend; }
+ rtn |= FDCERR;
+rwend: outb(0x0a, 0x06);
+ }
+ if(cip->b_status = rtn) {
+ D(printf("\n->rwierr %x ", rtn));
+ rwierr(uip);
+ } else { /* write command */
+ if(((cip->b_buf->b_flags&(B_FORMAT|B_READ|B_WRITE))==B_WRITE)
+ && !(cip->b_buf->b_flags & B_VERIFY)) {
+ D(printf("->w/v "));
+ cip->b_buf->b_flags |= B_VERIFY;
+ rwcmdset(uip);
+ if(cip->b_status = outicmd(uip))
+ intrerr0(uip);
+ return;
+ }
+ /* clear retry count */
+ if (cip->usebuf) {
+ bcopy(cip->b_vbuf, cip->b_xferaddr, cip->b_xferdma);
+ DD(printf("R(%x, %x, %x)\n",
+ cip->b_vbuf, cip->b_xferaddr, cip->b_xferdma));
+ }
+ cip->b_buf->b_flags &= ~B_VERIFY;
+ cip->b_rwerr = cip->b_seekerr = cip->b_rberr = 0;
+ cip->b_xfercount -= cip->b_xferdma;
+ cip->b_xferaddr += cip->b_xferdma;
+ cip->b_sector = cip->b_sector+(cip->b_xferdma>>9);
+ D(printf("->done%s\n", cip->b_xfercount?"":"." ));
+ /* next address (cyl,head,sec) */
+ if((int)cip->b_xfercount>0)
+ m765iosub(uip);
+ else
+ quechk(uip);
+ }
+}
+/*****************************************************************************
+ *
+ * read / write / format / verify error routine
+ *
+ *****************************************************************************/
+rwierr(uip)
+struct unit_info *uip;
+{
+ short status;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ D(printf("%x-%x-%x ", cip->b_rwerr&SRMASK, cip->b_rwerr&MRMASK, cip->b_rwerr&LRMASK));
+ if((cip->b_buf->b_flags&(B_READ|B_VERIFY))==(B_READ|B_VERIFY)){
+ if((cip->b_rwerr&SRMASK)<MEDIARD)
+ goto rwrtry;
+ if((cip->b_rwerr&MRMASK)<MEDIASEEK)
+ goto rwseek;
+ goto rwexit;
+ } else
+ if(cip->b_buf->b_flags&B_VERIFY){
+ cip->b_buf->b_flags &= ~B_VERIFY;
+ rwcmdset(uip);
+ }
+rwrtry: status = cip->b_status;
+ if((++cip->b_rwerr&SRMASK)<SRETRY)
+ cip->b_status = outicmd(uip);
+ else {
+rwseek: cip->b_rwerr = (cip->b_rwerr&RMRMASK)+MINC;
+ if((cip->b_rwerr&MRMASK)<MRETRY){
+ cmdp->c_intr |= SKEFLAG;
+ if(cmdp->c_saddr > 2)
+ cip->b_status=fdiseek(uip,cmdp->c_saddr-2);
+ else
+ cip->b_status=fdiseek(uip,cmdp->c_saddr+2);
+ } else {
+ cip->b_rwerr = (cip->b_rwerr&LRMASK)+LINC;
+ if((cip->b_rwerr&LRMASK)<LRETRY)
+ cip->b_status=rbirate(uip);
+ }
+ }
+ if(cip->b_status){
+ D(printf("ERR->intrerr0 "));
+ cip->b_status = status;
+rwexit: intrerr0(uip);
+ }
+}
+/*****************************************************************************
+ *
+ * recalibrate interrupt routine
+ *
+ *****************************************************************************/
+rbintr(uip)
+struct unit_info *uip;
+{
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ cmdp->c_intr &= ~RBFLAG;
+ if(cip->b_status) {
+ if(++cip->b_rberr<SRETRY)
+ cip->b_status = rbirate(uip);
+ } else {
+ cmdp->c_rbmtr |= 1<<(RBSHIFT+uip->dev->slave);
+ uip->b_seekaddr = 0;
+ cip->b_rberr = 0;
+ cip->b_status=fdiseek(uip, cmdp->c_saddr);
+ }
+ if(cip->b_status)
+ intrerr0(uip);
+}
+/******************************************************************************
+ *
+ * seek interrupt routine
+ *
+ *****************************************************************************/
+seekintr(uip)
+struct unit_info *uip;
+{
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ cmdp->c_intr &= ~SKFLAG;
+ if(cip->b_status)
+ seekierr(uip, cmdp->c_saddr);
+ else {
+ uip->b_seekaddr = cmdp->c_saddr;
+ cip->b_status = outicmd(uip);
+ }
+ if(cip->b_status)
+ intrerr0(uip);
+ else
+ cip->b_seekerr = 0;
+}
+/*****************************************************************************
+ *
+ * seek error retry interrupt routine
+ *
+ *****************************************************************************/
+seekintre(uip)
+struct unit_info *uip;
+{
+ register char seekpoint;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ cmdp->c_intr &= ~(SKEFLAG|SKFLAG);
+ if(cmdp->c_saddr > 2)
+ seekpoint = cmdp->c_saddr-2;
+ else
+ seekpoint = cmdp->c_saddr+2;
+ if(cip->b_status)
+ seekierr(uip, seekpoint);
+ else {
+ uip->b_seekaddr = seekpoint;
+ cip->b_status = fdiseek(uip, cmdp->c_saddr);
+ }
+ if(cip->b_status)
+ intrerr0(uip);
+ else
+ cip->b_seekerr = 0;
+}
+/*****************************************************************************
+ *
+ * seek error routine
+ *
+ *****************************************************************************/
+seekierr(uip, seekpoint)
+struct unit_info *uip;
+register char seekpoint;
+{
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+
+ if((++cip->b_seekerr&SRMASK)<SRETRY)
+ cip->b_status=fdiseek(uip, seekpoint);
+ else {
+ cip->b_seekerr = (cip->b_seekerr&MRMASK) + MINC;
+ if((cip->b_seekerr&MRMASK)<MRETRY)
+ cip->b_status=rbirate(uip);
+ }
+ if(cip->b_status)
+ intrerr0(uip);
+}
+/*****************************************************************************
+ *
+ * TITLE: m765sweep
+ *
+ * ABSTRACT: Perform an initialization sweep.
+ *
+ **************************************************************************/
+m765sweep(uip, cdr)
+struct unit_info *uip;
+register struct fddrtab *cdr; /* device initialization data */
+{
+ register struct fddrtab *dr = &uip->d_drtab;
+
+ dr->dr_ncyl = cdr->dr_ncyl;
+ dr->dr_nsec = cdr->dr_nsec;
+ dr->dr_spc = cdr->dr_spc;
+ dr->p_nsec = cdr->p_nsec;
+ dr->dr_type = cdr->dr_type;
+ dr->dr_rwgpl= cdr->dr_rwgpl;
+ dr->dr_fgpl = cdr->dr_fgpl;
+}
+/*****************************************************************************
+ *
+ * TITLE: m765disksort
+ *
+ *****************************************************************************/
+fd_disksort(uip, bp)
+struct unit_info *uip; /* Pointer to head of active queue */
+register struct buf *bp; /* Pointer to buffer to be inserted */
+{
+ register struct buf *bp2; /* Pointer to next buffer in queue */
+ register struct buf *bp1; /* Pointer where to insert buffer */
+
+ if (!(bp1 = uip->av_forw)) {
+ /* No other buffers to compare against */
+ uip->av_forw = bp;
+ bp->av_forw = 0;
+ return;
+ }
+ bp2 = bp1->av_forw;
+ while(bp2 && (relative(bp1->b_pfcent,bp->b_pfcent) >=
+ relative(bp1->b_pfcent,bp2->b_pfcent))) {
+ bp1 = bp2;
+ bp2 = bp1->av_forw;
+ }
+ bp1->av_forw = bp;
+ bp->av_forw = bp2;
+}
+/*****************************************************************************
+ *
+ * Set Interrupt error and FDC reset
+ *
+ *****************************************************************************/
+intrerr0(uip)
+struct unit_info *uip;
+{
+ struct buf *bp; /* Pointer to next buffer in queue */
+ int resid;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+ register struct fddrtab *dr = &uip->d_drtab;
+
+ if((cip->b_buf->b_flags&(B_READ|B_VERIFY))!=(B_READ|B_VERIFY)){
+ resid = cip->b_xfercount = cip->b_xferdma-1-inb(DMACNT)*0x101;
+ resid = (cip->b_sector + (resid>>9)) % dr->dr_spc;
+ printf("%s %d : %s\n",
+ fderr,
+ uip->dev->slave,
+ fdmsg[cip->b_status&BYTEMASK]);
+ printf("cylinder = %d ",cmdp->c_saddr);
+ printf("head = %d sector = %d byte/sec = %d\n",
+ resid / dr->dr_nsec , (resid % dr->dr_nsec)+1 , 512);
+ }
+ cip->b_rwerr = cip->b_seekerr = cip->b_rberr = 0;
+ cmdp->c_intr = CMDRST;
+ if(((cip->b_buf->b_flags&(B_READ|B_VERIFY))!=(B_READ|B_VERIFY)) &&
+ uip->dev->slave)
+ dr->dr_type &= ~OKTYPE;
+ bp = cip->b_buf;
+ bp->b_flags |= B_ERROR;
+ switch(cip->b_status&BYTEMASK){
+ case ADDRERR:
+ case OVERRUN:
+ case FDCERR:
+ case TIMEOUT:
+ bp->b_error = EIO;
+ break;
+ case WTPRT:
+#ifdef MACH_KERNEL
+ bp->b_error = ENXIO;
+#else
+ bp->b_error = ENODEV;
+#endif
+ break;
+ case NOREC:
+ bp->b_error = EBBHARD;
+ break;
+ case CRCERR:
+ bp->b_error = EBBSOFT;
+ }
+ rstout(uip);
+ specify(uip);
+ cmdp->c_rbmtr &= RBRST;
+ quechk(uip);
+}
+/*****************************************************************************
+ *
+ * Next queue check routine
+ *
+ *****************************************************************************/
+quechk(uip)
+struct unit_info *uip;
+{
+ register struct buf *bp = uip->av_forw;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct unit_info *loop;
+ struct fdcmd *cmdp = uip->b_cmd;
+ /* clear retry count */
+ cip->b_rwerr = cip->b_seekerr = cip->b_rberr = 0;
+ bp->b_resid = bp->b_resid + cip->b_xfercount;
+ uip->av_forw=bp->av_forw;
+ if (!uip->av_forw && uip->wakeme) {
+ uip->wakeme = 0;
+ wakeup(uip);
+ }
+ biodone(bp);
+ loop = uip;
+ do {
+ loop=loop->b_unitf;
+ if (loop->av_forw) {
+ m765io(loop);
+ return;
+ }
+ } while (loop!=uip);
+ cip->b_uip = 0;
+ cmdp->c_stsflag &= ~MTRFLAG;
+ mtr_on(uip);
+ cmdp->c_devflag &= ~STRCHK;
+ if(cmdp->c_devflag & STRWAIT){
+ cmdp->c_devflag &= ~STRWAIT;
+ wakeup(&cmdp->c_devflag);
+ }
+}
+fdprint(dev,str)
+dev_t dev;
+char *str;
+{
+ printf("floppy disk driver: %s on bad dev %d, partition %d\n",
+ str, UNIT(dev), 0);
+}
+fdsize()
+{
+ printf("fdsize() -- not implemented\n");
+}
+fddump()
+{
+ printf("fddump() -- not implemented\n");
+}
+/*****************************************************************************
+ *
+ * fdc reset routine
+ *
+ *****************************************************************************/
+rstout(uip)
+struct unit_info *uip;
+{
+ register int outd;
+
+ outd = ((uip->b_cmd->c_rbmtr&MTRMASK)<<MTR_ON)|uip->dev->slave;
+ outb(CTRLREG(uip->addr), outd);
+ outd |= FDC_RST;
+ outb(CTRLREG(uip->addr), outd);
+ outd |= DMAREQ;
+ outb(CTRLREG(uip->addr), outd);
+}
+/*****************************************************************************
+ *
+ * specify command routine
+ *
+ *****************************************************************************/
+specify(uip)
+struct unit_info *uip;
+{
+ /* status check */
+ if(fdc_sts(FD_OSTS, uip))
+ return;
+ /* Specify command */
+ outb(DATAREG(uip->addr), SPCCMD);
+ /* status check */
+ if(fdc_sts(FD_OSTS, uip))
+ return;
+ /* Step rate,Head unload time */
+ outb(DATAREG(uip->addr), SRTHUT);
+ /* status check */
+ if(fdc_sts(FD_OSTS, uip))
+ return;
+ /* Head load time,Non DMA Mode*/
+ outb(DATAREG(uip->addr), HLTND);
+ return;
+}
+/****************************************************************************
+ *
+ * recalibrate command routine
+ *
+ ****************************************************************************/
+rbrate(mtype,uip)
+char mtype;
+struct unit_info *uip;
+{
+ register int rtn = 1, rty_flg=2;
+ spl_t x;
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ rbskrate(uip, mtype); /* set transfer rate */
+ while((rty_flg--)&&rtn){
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ break;
+ /*recalibrate command*/
+ outb(DATAREG(uip->addr), RBCMD);
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ break;
+ /* Device to wake up specified in open */
+ cmdp->c_intr |= WUPFLAG;
+ x = SPL();
+ outb(DATAREG(uip->addr), uip->dev->slave);
+ rtn = ERROR;
+ while(rtn) {
+ uip->wakeme = 1;
+ sleep(uip, PZERO);
+ if((rtn = sis(uip)) == ST0OK)
+ /* Device to wake up specified in open */
+ cmdp->c_intr |= WUPFLAG;
+ else
+ break;
+ }
+ splx(x);
+ }
+ return(rtn);
+}
+/*****************************************************************************
+ *
+ * seek command routine
+ *
+ ****************************************************************************/
+fdseek(mtype, uip, cylno)
+register char mtype;
+struct unit_info *uip;
+register int cylno;
+{
+ spl_t x;
+ int rtn;
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ rbskrate(uip, mtype);
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ return(rtn);
+ outb(DATAREG(uip->addr), SEEKCMD); /* seek command */
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ return(rtn);
+ outb(DATAREG(uip->addr), uip->dev->slave); /* drive number */
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ return(rtn);
+ x = SPL();
+ /* Device to wake up specified in open */
+ cmdp->c_intr |= WUPFLAG;
+ outb(DATAREG(uip->addr), cylno); /* seek count */
+ rtn = ERROR;
+ while(rtn){
+ uip->wakeme = 1;
+ sleep(uip, PZERO);
+ if((rtn = sis(uip)) == ST0OK)
+ /* Device to wake up specified in open */
+ cmdp->c_intr |= WUPFLAG;
+ else
+ break;
+ }
+ splx(x);
+ return(rtn);
+}
+/*****************************************************************************
+ *
+ * seek commnd routine(use interrupt)
+ *
+ *****************************************************************************/
+fdiseek(uip, cylno)
+struct unit_info *uip;
+int cylno;
+{
+ register int rtn;
+
+ D(printf("SK %x ", cylno));
+ rbskrate(uip, uip->d_drtab.dr_type);/* set transfer rate */
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ goto fdiend;
+ outb(DATAREG(uip->addr), SEEKCMD); /* seek command */
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ goto fdiend;
+ outb(DATAREG(uip->addr), uip->dev->slave); /* drive number */
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ goto fdiend;
+ uip->b_seekaddr = cylno;
+ if(uip->d_drtab.dr_type&DOUBLE)
+ cylno = cylno * 2;
+ uip->b_cmd->c_intr |= SKFLAG;
+ outb(DATAREG(uip->addr), cylno); /* seek count */
+fdiend:
+ if(rtn)
+ rtn |= SEEKCMD<<8;
+ return(rtn);
+}
+/*****************************************************************************
+ *
+ * recalibrate command routine(use interrupt)
+ *
+ *****************************************************************************/
+rbirate(uip)
+struct unit_info *uip;
+{
+ register int rtn;
+
+ rbskrate(uip, uip->d_drtab.dr_type);/* set transfer rate */
+ if(!(rtn = fdc_sts(FD_OSTS, uip))) { /* status check */
+ /* recalibrate command */
+ outb(DATAREG(uip->addr), RBCMD);
+ if(!(rtn = fdc_sts(FD_OSTS, uip))) { /* status check */
+ uip->b_cmd->c_intr |= RBFLAG;
+ outb(DATAREG(uip->addr), uip->dev->slave);
+ }
+ }
+ return(rtn ? rtn|RBCMD<<8 : 0);
+}
+/*****************************************************************************
+ *
+ * read / write / format / verify command out routine(use interrupt)
+ *
+ *****************************************************************************/
+outicmd(uip)
+struct unit_info *uip;
+{
+ int rtn;
+ register int *data,cnt0,dmalen;
+ register long address;
+ struct ctrl_info *cip = &ctrl_info[uip->dev->ctlr];
+ struct fdcmd *cmdp = uip->b_cmd;
+ spl_t x = splhi();
+
+ outb(DMACMD1,DMADATA0); /* DMA #1 command register */
+ outb(DMAMSK1,DMADATA1); /* DMA #1 all mask register */
+ /* Perhaps outb(0x0a,0x02); might work better on line above? */
+ switch(cmdp->c_rwdata[0]){
+ case RDM:
+ D(printf("RDM"));
+ outb(DMABPFF,DMARD);
+ outb(DMAMODE,DMARD);
+ break;
+ case WTM:
+ case FMTM:
+ D(printf("W"));
+ outb(DMABPFF,DMAWT);
+ outb(DMAMODE,DMAWT);
+ break;
+ case RDMV:
+ D(printf("RDMV"));
+ outb(DMABPFF,DMAVRF);
+ outb(DMAMODE,DMAVRF);
+ }
+ /* get work buffer physical address */
+ address = kvtophys(cip->b_xferaddr);
+ dmalen = i386_trunc_page(address) + I386_PGBYTES - address;
+ if ( (cip->b_rwerr&MRMASK) >= 0x10)
+ dmalen = 0x200;
+ if (dmalen<=cip->b_xferdma)
+ cip->b_xferdma = dmalen;
+ else
+ dmalen = cip->b_xferdma;
+ if (address >= FdDmaThreshold) {
+ DD(printf(">(%x[%x], %x[%x] L%x\n",
+ address, cip->b_pbuf,
+ cip->b_xferaddr, cip->b_vbuf, dmalen));
+ if (!FdDmaEISA) {
+ cip->usebuf = 1;
+ address = (long)cip->b_pbuf;
+ if (cmdp->c_rwdata[0] == WTM || cmdp->c_rwdata[0] == FMTM) {
+ bcopy(cip->b_xferaddr, cip->b_vbuf, dmalen);
+ DD(printf("W(%x, %x, %x)\n",
+ cip->b_xferaddr, cip->b_vbuf, dmalen));
+ }
+ } else
+ cip->usebuf = 0;
+ } else
+ cip->usebuf = 0;
+ D(printf(" %x L%x ", address, dmalen));
+ /* set buffer address */
+ outb(DMAADDR,(int)address&BYTEMASK);
+ outb(DMAADDR,(((int)address>>8)&BYTEMASK));
+ outb(DMAPAGE,(((int)address>>16)&BYTEMASK));
+ if (FdDmaEISA)
+ outb(FdDmaEISA+DMAPAGE-0x80,(((int)address>>24)&BYTEMASK));
+ /* set transfer count */
+ outb(DMACNT,(--dmalen)&BYTEMASK);
+ outb(DMACNT,((dmalen>>8)&BYTEMASK));
+ outb(DMAMSK,CHANNEL2);
+ splx(x);
+ trfrate(uip, uip->d_drtab.dr_type); /* set transfer rate */
+ data = &cmdp->c_rwdata[0];
+ for(cnt0 = 0; cnt0<cmdp->c_dcount; cnt0++,data++){
+ if(rtn = fdc_sts(FD_OSTS, uip)) /*status check*/
+ break;
+ outb(DATAREG(uip->addr), *data);
+ }
+ if(!rtn){
+ cmdp->c_intr |= RWFLAG;
+ cmdp->c_stsflag |= INTROUT;
+ cnt0 = ((cip->b_buf->b_flags&(B_READ|B_VERIFY)) ==
+ (B_READ|B_VERIFY))?TOUT:ITOUT;
+#ifdef MACH_KERNEL
+ timeout(fdintr,uip->dev->ctlr,cnt0);
+#else MACH_KERNEL
+ cmdp->c_timeid = timeout(fdintr,uip->dev->ctlr,cnt0);
+#endif MACH_KERNEL
+ }
+ return(rtn);
+}
+/*****************************************************************************
+ *
+ * sense interrupt status routine
+ *
+ *****************************************************************************/
+sis(uip)
+struct unit_info *uip;
+{
+ register int rtn, st0;
+
+ if(rtn = fdc_sts(FD_OSTS, uip)) /* status check */
+ return(rtn);
+ outb(DATAREG(uip->addr), SISCMD);
+ if(rtn = fdc_sts(FD_ISTS, uip)) /* status check */
+ return(rtn);
+ st0 = inb(DATAREG(uip->addr)) & ST0OK; /* get st0 */
+ if(rtn = fdc_sts(FD_ISTS, uip)) /* status check */
+ return(rtn);
+ inb(DATAREG(uip->addr)); /* get pcn */
+ if (st0&(ST0AT|ST0IC))
+ st0 = FDCERR;
+ return(st0);
+}
+
+/*****************************************************************************
+ *
+ * fdc status get routine
+ *
+ *****************************************************************************/
+fdc_sts(mode, uip)
+register int mode;
+struct unit_info *uip;
+{
+ register int ind;
+ int cnt0 = STSCHKCNT;
+
+ while(cnt0--)
+ if(((ind=inb(STSREG(uip->addr))) & DATAOK) &&
+ ((ind & DTOCPU) == mode))
+ return(0);
+ return(TIMEOUT);
+}
+/*****************************************************************************
+ *
+ * motor on routine
+ *
+ *****************************************************************************/
+mtr_on(uip)
+struct unit_info *uip;
+{
+ extern int(mtr_off)();
+ extern int(wakeup)();
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ if(!(mtr_start(uip))){
+ timeout(wakeup,&cmdp->c_stsflag,HZ);
+ sleep(&cmdp->c_stsflag,PZERO);
+ }
+ cmdp->c_stsflag |= MTROFF;
+#ifdef MACH_KERNEL
+ timeout(mtr_off,uip,MTRSTOP);
+#else MACH_KERNEL
+ cmdp->c_mtrid = timeout(mtr_off,uip,MTRSTOP);
+#endif MACH_KERNEL
+}
+/*****************************************************************************
+ *
+ * motor start routine
+ *
+ *****************************************************************************/
+mtr_start(uip)
+struct unit_info *uip;
+{
+ int status;
+ int (mtr_off)();
+ struct fdcmd *cmdp = uip->b_cmd;
+ int slave = uip->dev->slave;
+ if(cmdp->c_stsflag & MTROFF){
+ untimeout(mtr_off, uip);
+ cmdp->c_stsflag &= ~MTROFF;
+ }
+ status = cmdp->c_rbmtr&(1<<slave);
+ cmdp->c_rbmtr |= (1<<slave);
+ outb(CTRLREG(uip->addr), ((cmdp->c_rbmtr&MTRMASK)<<MTR_ON)|
+ FDC_RST|slave|DMAREQ);
+ return(status);
+}
+/*****************************************************************************
+ *
+ * motor off routine
+ *
+ *****************************************************************************/
+mtr_off(uip)
+struct unit_info *uip;
+{
+ struct fdcmd *cmdp = uip->b_cmd;
+
+ cmdp->c_stsflag &= ~MTROFF;
+ if(!(cmdp->c_stsflag&MTRFLAG)){
+ cmdp->c_rbmtr &= MTRRST;
+ outb(CTRLREG(uip->addr), FDC_RST | DMAREQ);
+ }
+}
+
+#endif
diff --git a/i386/i386at/fdreg.h b/i386/i386at/fdreg.h
new file mode 100644
index 00000000..98d8d007
--- /dev/null
+++ b/i386/i386at/fdreg.h
@@ -0,0 +1,368 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* Copyright (c) 1987, 1988 TOSHIBA Corp. */
+/* All Rights Reserved */
+
+#ident "@(#)m765.h 1.13 - 88/02/17"
+
+/*******************************************************************
+ *
+ * Toshiba Floppy Driver for UNIX System V R3
+ *
+ * June 21, 1988
+ *
+ * Intended Drive Units:
+ * Worldwide - Model No. ND-356 3.5" unformatted 2MB/1MB
+ * UNIX Media Type Name: 2HD512/2DD512/2D512/1D512.
+ *
+ * In Japan Only - Model No. ND-355 3.5" unformatted 1.6MB/1MB
+ * UNIX Media Type Name: 2HC1024/2HC512/2HC256/2DD512/2D512/1D512.
+ *
+ * Worldwide - Model No. ND-04DT-A 5.25" unformatted 500 KB
+ * UNIX Media Type Name: 2D512/1D512.
+ *
+ * In Japan Only - Model No. ND-08DE 5.25" unformatted 1.6MB/1MB
+ * UNIX Media Type Name: 2HC1024/2HC512/2HC256/2DD512/2D512/1D512.
+ *
+ * Use with other devices may require modification.
+ *
+ * Notes:
+ * For further detail regarding drive units contact
+ * Toshiba America,Inc. Disk Products Division,
+ * Irvine, CA (714) 583-3000.
+ *
+ *******************************************************************/
+
+/*
+ * fdcmd.c_rbmtr
+ *
+ * |--+--+--+--+--+--+--+--|
+ * | | | | | | | | |
+ * |--+--+--+--+--+--+--+--|
+ * ^ ^ ^ ^
+ * | | | |--- unit0 motor on flag
+ * | | |------ unit1 motor on flag
+ * | |--------------- unit0 recalibrate flag
+ * |------------------ unit1 recalibrate flag
+ */
+#define MTRMASK 0x003 /* mask motor_flag for get status */
+#define MTRRST 0x0fc /* reset motor_flag data */
+#define RBSHIFT 0x004 /* shift count for recalibrate data */
+#define RBRST 0x0cf /* reset recalibrate data */
+
+/*
+ * fdcmd.c_intr
+ *
+ * |--+--+--+--+--+--+--+--|
+ * | | | | | | | | |
+ * |--+--+--+--+--+--+--+--|
+ * ^ ^ ^ ^ ^ ^ ^ ^
+ * reserved --+ | | | | | | +--- read/write flag
+ * reserved -----+ | | | | +------ seek flag
+ * reserved --------+ | | +------ seek flag for retry
+ * recalibrate/seek flag(for open) ----------+ +--------- recalibrate flag
+ */
+#define RWFLAG 0x001
+#define SKFLAG 0x002
+#define SKEFLAG 0x004
+#define RBFLAG 0x008
+#define WUPFLAG 0x010
+#define CMDRST 0x000
+
+/*
+ * fddrtab.dr_type
+ *
+ * +---+---+---+---+---+---+---+---+
+ * | | | | | | | | |
+ * +---+---+---+---+---+---+---+---+
+ * ^ ^ ^ ^ ^
+ * | | | | |----------- rapid seek flag
+ * |---| | | 0: normal seek
+ * | | | 1: rapid seek
+ * | | |--------------- detect format
+ * | | 0: no detect
+ * | | 1: format type OK
+ * | |------------------- 40 or 80 cylinder(for 2hc/2dd drive)
+ * | 0: 80 cylinder
+ * | 1: 40 cylinder
+ * |------------------------- transfer rate(for read/write/format)
+ * 00: 500kbps 10: 250kbps
+ * 01: 300kbps 11: reserved
+ */
+#define RPSEEK 0x00 /* rapid seek */
+#define RAPID 0x08 /* rapid seek flag */
+#define OKTYPE 0x10 /* media change flag */
+#define DOUBLE 0x20 /* double/single step change */
+#define NMSEEK 0x80 /* normal seek */
+#define RATEMASK 0xc0 /* transfer parameter mask data */
+
+/*
+ * device number
+ *
+ * 15 10 9 8 7 0
+ * +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
+ * | 0 0 0 0 0 0 0 1| | 0| 0| 0| 0| |
+ * +-----------+-----+-----+--+--+--+--+-----------+
+ * ^ ^ ^ ^ ^ ^
+ * |____________________| |__| |__|
+ * | | |
+ * | | |- media type
+ * major number | 0: 3.50" 720 KB
+ * |- unit number 1: 3.50" 1.44 Meg
+ * 2: 5.25" 360 KB
+ * 3: 5.25" 1.20 Meg
+ */
+#define UNIT(dev) ((dev & 0xc0)>>6) /* get unit number */
+#define MEDIATYPE(dev) (dev & 0x03) /* get media type */
+/*****************************************************************************
+
+ wait time / timeout count
+
+ *****************************************************************************/
+#define STSCHKCNT 0x2800 /* For check status */
+#define ITOUT HZ*5 /* interrupt timeout count */
+#define TOUT HZ/4 /* media type check timeout count */
+#define MTRSTOP HZ*2 /* motor off time */
+#define SEEKWAIT HZ/100*3 /* head_lock time */
+
+/******************************************************************************
+
+ define for FDC
+
+ ******************************************************************************/
+/* FDC register */
+#define CTRLREG(ADDR) (ADDR) /* controle register */
+#define STSREG(ADDR) ((ADDR)+2) /* status register */
+#define DATAREG(ADDR) ((ADDR)+3) /* data register */
+#define VFOREG(ADDR) ((ADDR)+5) /* vfo register */
+
+/* CTRLREG flags */
+#define FDC_RST 0x04
+#define MTR_ON 0x04
+#define DMAREQ 0x08
+#define RDY 0x40
+#define BSY 0x80
+
+/* status for command_out */
+#define FD_OSTS 0x00 /* For output check */
+#define FD_ISTS 0x40 /* For input check */
+#define DTOCPU 0x40
+#define DATAOK 0x80
+
+/* Command for FDC */
+#define SPCCMD 0x03 /* Specify command */
+#define RBCMD 0x07 /* Recalibrate command */
+#define SISCMD 0x08 /* Sense interrupt status command */
+#define SEEKCMD 0x0f /* seek command */
+#define RDM 0xe6 /* FDC READ command */
+#define RDMV 0x42e6 /* VERIFY READ command */
+#define WTM 0xc5 /* FDC WRITE command */
+#define FMTM 0x4d /* FDC FORMAT command */
+#define FMTDATA 0x5e /* format data */
+
+/* check value */
+#define OPENBIT 0x80 /* VFO check define */
+#define BYTEMASK 0xff
+
+/* FDC error code define */
+#define ERROR 0xff
+#define EBBHARD 128
+#define EBBSOFT 129
+#define ST0AT 0x40
+#define ST0IC 0x80
+#define ST0OK 0xc0
+#define ADDRERR 0x01
+#define WTPRT 0x02
+#define NOREC 0x03
+#define OVERRUN 0x04
+#define CRCERR 0x05
+#define FDCERR 0x06
+#define TIMEOUT 0x08
+#define DOORERR 0x09
+
+/******************************************************************************
+
+ define for DMA
+
+ *****************************************************************************/
+/* DMA register */
+#define DMACMD1 0x08 /* DMA #1 command register */
+#define DMAMSK1 0x0f /* DMA #1 all mask register */
+#define DMABPFF 0x0c
+#define DMAMODE 0x0b
+#define DMAADDR 0x04
+#define DMAPAGE 0x81
+#define DMACNT 0x05
+#define DMAMSK 0x0a
+
+/* dma set data */
+#define DMARD 0x46 /* DMA read mode */
+#define DMAWT 0x4a /* DMA write mode */
+#define DMAVRF 0x42 /* DMA verify mode */
+
+#define DMADATA0 0x00 /* DMA #2 all mask data */
+#define DMADATA1 0x0b /* DMA #1 all mask data */
+#define CHANNEL2 0x02
+
+#define SRTHUT 0xdf
+#define HLTND 0x02
+#define DTL 0xff
+
+/******************************************************************************
+
+ etc. define
+
+ *****************************************************************************/
+#define SPL spl5 /* Same as in i386at/autoconf.c */
+#define MAXUNIT 4 /* Max unit number */
+#define BLKSIZE 512 /* block size */
+
+/* fdcmd.c_stsflag */
+#define MTRFLAG 0x01
+#define MTROFF 0x02
+#define INTROUT 0x04
+
+/* fdcmd.c_devflag (media check flag . etc.) */
+#define FDMCHK 0x01
+#define FDWAIT 0x02
+#define STRCHK 0x04
+#define STRWAIT 0x08
+
+/* fdcmd.c_dcount */
+#define FDCCNT 9 /* Command table for read/write/format (FDC) */
+#define RWCNT 9 /* Read/Write command count */
+#define FMTCNT 6 /* format command count */
+
+struct fdcmd {
+ int c_rbmtr; /* moter & rcalibrate flag */
+ int c_intr; /* intr flag */
+ int c_stsflag; /* moter flag */
+ int c_mtrid; /* motor off queue id */
+ int c_timeid; /* interrupt timeout id */
+ int c_devflag; /* device status */
+ int c_dcount; /* Read/Write/Format data count */
+ int c_rwdata[FDCCNT]; /* Read/Write/Format cmd (FDC) */
+ int c_saddr; /* cmd seek address */
+};
+
+/* fdmbuf.b_rberr/fdmbuf.b_seekerr/fdmbuf.b_rwerr */
+#define MEDIARD 0x01
+#define MEDIASEEK 0x01
+#define SRETRY 0x03
+#define MRETRY 0x30
+#define LRETRY 0x300
+#define SRMASK 0x0f
+#define MRMASK 0xf0
+#define RMRMASK 0xff0
+#define LRMASK 0xf00
+#define MINC 0x10
+#define LINC 0x100
+
+struct ctrl_info {
+ struct unit_info *b_unitf; /* first buffer for this dev */
+ struct unit_info *b_uip; /* like b_unit */
+ struct unit_info *b_wup; /* unit to wake up when WUPFLAG */
+ short b_rberr; /* rb error count (for recovery) */
+ short b_seekerr; /* seek error count (for recovery) */
+ short b_rwerr; /* r/w error count (for recovery) */
+ short b_status; /* error status */
+ struct buf *b_buf; /* set bp address */
+ caddr_t b_xferaddr; /* trasfer address */
+ unsigned int b_xfercount; /* total transfer count */
+ unsigned int b_xferdma; /* dma transfer count */
+ int usebuf; /* use private dma page */
+ caddr_t b_vbuf; /* virtual address for dma page */
+ vm_offset_t b_pbuf; /* physical dma page (model_dep.c) */
+ daddr_t b_sector; /* read/write sector */
+ struct fdcmd b_cmd; /* set command table address */
+};
+
+#define FMTID 4
+struct fmttbl {
+ unsigned char cyl;
+ unsigned char head;
+ unsigned char sector;
+ unsigned char s_type;
+};
+
+struct fddrtab {
+ u_short dr_ncyl; /* cylinder count */
+ u_short dr_spc; /* actual sectors/cylinder */
+ daddr_t p_nsec; /* disk length (sector count) */
+ char dr_nsec; /* sector per track */
+ char dr_type; /* media type */
+ char dr_rwgpl; /* Read / Write Gap length */
+ char dr_fgpl; /* Format Gap length */
+};
+
+struct unit_info {
+ struct unit_info *b_unitf; /* next slave */
+ struct buf *av_forw; /* head of I/O queue (b_forw) */
+ int b_seekaddr; /* cylinder address */
+ u_short addr;
+ struct fddrtab d_drtab; /* floppy disk parameter */
+ struct bus_device *dev;
+ struct fdcmd *b_cmd; /* set command table address */
+ char wakeme; /* set if someone wants to be woken */
+};
+
+#define HZ 100 /* 100 ticks/second of the clock */
+#define NBPSCTR 512 /* Bytes per LOGICAL disk sector */
+ /* These should be added to
+ "sys/param.h". */
+#define PAGESIZ 4096
+#ifdef MACH_KERNEL
+#define PZERO 25
+#define PRIBIO 20
+
+#define B_VERIFY IO_SPARE_START
+#define B_FORMAT (IO_SPARE_START << 1)
+
+#define b_pfcent io_mode
+
+#endif MACH_KERNEL
diff --git a/i386/i386at/gpl/if_hpp.c b/i386/i386at/gpl/if_hpp.c
new file mode 100644
index 00000000..c1770301
--- /dev/null
+++ b/i386/i386at/gpl/if_hpp.c
@@ -0,0 +1,690 @@
+/*
+ Written 1994 by Donald Becker.
+
+ This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+ These cards are sold under several model numbers, usually 2724*.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ As is often the case, a great deal of credit is owed to Russ Nelson.
+ The Crynwr packet driver was my primary source of HP-specific
+ programming information.
+*/
+
+/*
+ * Ported to mach by Stephen Clawson, sclawson@cs.utah.edu
+ * University of Utah CSL.
+ *
+ * Derived from the Linux driver by Donald Becker.
+ *
+ * Also uses code Shantanu Goel adapted from Donald Becker
+ * for ns8930 support.
+ *
+ */
+
+#include <hpp.h>
+#if NHPP > 0
+
+#include <sys/types.h>
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <chips/busses.h>
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <i386at/gpl/if_nsreg.h>
+
+
+/*
+ * XXX - This is some gross glue garbage. The io instructions really
+ * should be integrated into pio.h...
+ */
+#define IO_DELAY __asm__ __volatile__("outb %al,$0x80")
+#define outb_p(p, v) { outb(p, v); IO_DELAY; }
+#define inb_p(p) ({ unsigned char _v; _v = inb(p); IO_DELAY; _v; })
+
+
+static __inline void
+insw(u_short port, void *addr, int cnt)
+{
+ __asm __volatile("cld\n\trepne\n\tinsw" :
+ : "d" (port), "D" (addr), "c" (cnt) : "%edi", "%ecx");
+}
+
+static __inline void
+outsw(u_short port, void *addr, int cnt)
+{
+ __asm __volatile("cld\n\trepne\n\toutsw" :
+ : "d" (port), "S" (addr), "c" (cnt) : "%esi", "%ecx");
+}
+
+
+/*
+ The HP EtherTwist chip implementation is a fairly routine DP8390
+ implementation. It allows both shared memory and programmed-I/O buffer
+ access, using a custom interface for both. The programmed-I/O mode is
+ entirely implemented in the HP EtherTwist chip, bypassing the problem
+ ridden built-in 8390 facilities used on NE2000 designs. The shared
+ memory mode is likewise special, with an offset register used to make
+ packets appear at the shared memory base. Both modes use a base and bounds
+ page register to hide the Rx ring buffer wrap -- a packet that spans the
+ end of physical buffer memory appears continuous to the driver. (c.f. the
+ 3c503 and Cabletron E2100)
+
+ A special note: the internal buffer of the board is only 8 bits wide.
+ This lays several nasty traps for the unaware:
+ - the 8390 must be programmed for byte-wide operations
+ - all I/O and memory operations must work on whole words (the access
+ latches are serially preloaded and have no byte-swapping ability).
+
+ This board is laid out in I/O space much like the earlier HP boards:
+ the first 16 locations are for the board registers, and the second 16 are
+ for the 8390. The board is easy to identify, with both a dedicated 16 bit
+ ID register and a constant 0x530* value in the upper bits of the paging
+ register.
+*/
+
+#define HP_ID 0x00 /* ID register, always 0x4850. */
+#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
+#define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option.*/
+#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page.*/
+#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page.*/
+#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page.*/
+#define HPP_NIC_OFFSET 0x10 /* Offset to the 8390 registers.*/
+#define HP_IO_EXTENT 32
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
+/*#define HP_STOP_PG 0x1f
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+ Perf_Page = 0, /* Normal operation. */
+ MAC_Page = 1, /* The ethernet address (+checksum). */
+ HW_Page = 2, /* EEPROM-loaded hw parameters. */
+ LAN_Page = 4, /* Transciever type, testing, etc. */
+ ID_Page = 6 };
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+ NICReset = 1, /* Active low, really UNreset. */
+ ChipReset = 2,
+ EnableIRQ = 4,
+ FakeIntr = 8,
+ BootROMEnb = 0x10,
+ IOEnb = 0x20,
+ MemEnable = 0x40,
+ ZeroWait = 0x80,
+ MemDisable = 0x1000, };
+
+
+void hpp_reset_8390(struct nssoftc *ns);
+
+void hpp_mem_block_input(struct nssoftc *ns, int, char *, int);
+int hpp_mem_block_output(struct nssoftc *ns, int, char *, int);
+void hpp_io_block_input(struct nssoftc *ns, int, char *, int);
+int hpp_io_block_output(struct nssoftc *ns, int,char *, int);
+
+
+/*
+ * Watchdog timer.
+ */
+int hppwstart = 0;
+void hppwatch(void);
+
+
+/*
+ * Autoconfig structures.
+ */
+int hpp_std[] = { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0 };
+struct bus_device *hpp_info[NHPP];
+int hpp_probe();
+void hpp_attach();
+struct bus_driver hppdriver = {
+ hpp_probe, 0, hpp_attach, 0, hpp_std, "hpp", hpp_info, 0, 0, 0
+};
+
+
+/*
+ * ns8390 state.
+ */
+struct nssoftc hppnssoftc[NHPP];
+
+
+/*
+ * hpp state.
+ */
+struct hppsoftc {
+ unsigned long rmem_start; /* shmem "recv" start */
+ unsigned long rmem_end; /* shmem "recv" end */
+ unsigned long mem_start; /* shared mem start */
+ unsigned long mem_end; /* shared mem end */
+} hppsoftc[NHPP];
+
+
+/*
+ * Probe a list of addresses for the card.
+ *
+ */
+int hpp_probe(port, dev)
+ int port;
+ struct bus_device *dev;
+{
+ int unit = dev->unit;
+ char *str = "hp-plus ethernet board %d out of range.\n";
+ caddr_t base = (caddr_t) (dev ? dev->address : 0);
+ int i;
+
+ if ((unit < 0) || (unit >= NHPP)) {
+ printf(str, unit);
+ return(0);
+ }
+
+ /* Check a single specified location. */
+ if (base > (caddr_t) 0x1ff)
+ return hpp_probe1(dev, base);
+ else if (base != 0) /* Don't probe at all. */
+ return 0;
+
+ for (i = 0; hpp_std[i]; i++) {
+ int ioaddr = hpp_std[i];
+
+ if ( ioaddr > 0 && hpp_probe1(dev, ioaddr) ) {
+ dev->address = ioaddr;
+ hpp_std[i] = -1; /* Mark address used */
+ return(1);
+ }
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Do the interesting part of the probe at a single address.
+ *
+ */
+int hpp_probe1(dev, ioaddr)
+ struct bus_device *dev;
+ int ioaddr;
+{
+ int i;
+ u_char checksum = 0;
+ int mem_start;
+
+ struct hppsoftc *hpp = &hppsoftc[dev->unit];
+ struct nssoftc *ns = &hppnssoftc[dev->unit];
+ struct ifnet *ifp = &ns->sc_if;
+
+ /* Check for the HP+ signature, 50 48 0x 53. */
+ if (inw(ioaddr + HP_ID) != 0x4850
+ || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
+ return 0;
+
+
+ printf("%s%d: HP PClan plus at %#3x,", dev->name, dev->unit, ioaddr);
+ /* Retrieve and checksum the station address. */
+ outw(ioaddr + HP_PAGING, MAC_Page);
+
+ printf("MAC_Page = %d, ioaddr = %x\n", MAC_Page, ioaddr);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ u_char inval = inb(ioaddr + 8 + i);
+ ns->sc_addr[i] = inval;
+ checksum += inval;
+ printf(" %2.2x", inval);
+ }
+ checksum += inb(ioaddr + 14);
+
+ if (checksum != 0xff) {
+ printf(" bad checksum %2.2x.\n", checksum);
+ return 0;
+ } else {
+ /* Point at the Software Configuration Flags. */
+ outw(ioaddr + HP_PAGING, ID_Page);
+ printf(" ID %4.4x", inw(ioaddr + 12));
+ }
+
+
+ /* Read the IRQ line. */
+ outw(ioaddr + HP_PAGING, HW_Page);
+ {
+ int irq = inb(ioaddr + 13) & 0x0f;
+ int option = inw(ioaddr + HPP_OPTION);
+
+ dev->sysdep1 = irq;
+ take_dev_irq(dev);
+
+ if (option & MemEnable) {
+ mem_start = inw(ioaddr + 9) << 8;
+ printf(", IRQ %d, memory address %#x.\n", irq, mem_start);
+ } else {
+ mem_start = 0;
+ printf(", IRQ %d, programmed-I/O mode.\n", irq);
+ }
+ }
+
+ /* Set the wrap registers for string I/O reads. */
+ outw( ioaddr + 14, (HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8));
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ ns->sc_port = ioaddr + HPP_NIC_OFFSET;
+
+ ns->sc_name = dev->name;
+ ns->sc_unit = dev->unit;
+ ns->sc_pingpong = 0; /* turn off pingpong mode */
+ ns->sc_word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
+ ns->sc_txstrtpg = HP_START_PG;
+ ns->sc_rxstrtpg = HP_START_PG + TX_2X_PAGES;
+ ns->sc_stoppg = HP_STOP_PG;
+
+
+ ns->sc_reset = hpp_reset_8390;
+ ns->sc_input = hpp_io_block_input;
+ ns->sc_output = hpp_io_block_output;
+
+ /* Check if the memory_enable flag is set in the option register. */
+ if (mem_start) {
+ ns->sc_input = hpp_mem_block_input;
+ ns->sc_output = hpp_mem_block_output;
+ hpp->mem_start = mem_start;
+ hpp->rmem_start = hpp->mem_start + TX_2X_PAGES * 256;
+ hpp->mem_end = hpp->rmem_end
+ = hpp->mem_start + (HP_STOP_PG - HP_START_PG) * 256;
+ }
+
+ outw(ioaddr + HP_PAGING, Perf_Page);
+
+ /* Leave the 8390 and HP chip reset. */
+ outw( ioaddr + HPP_OPTION, inw(ioaddr + HPP_OPTION) & ~EnableIRQ );
+
+ /*
+ * Initialize interface header.
+ */
+ ifp->if_unit = dev->unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = ETHER_ADDR_LEN;
+ ifp->if_address = ns->sc_addr;
+ if_init_queues(ifp);
+
+ return (1);
+}
+
+/*
+ * XXX
+ *
+ * this routine really should do the invasive part of the setup.
+ */
+void
+hpp_attach(dev)
+ struct bus_device *dev;
+{
+ /* NULL */
+}
+
+
+
+int
+hppopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ int s, unit = minor(dev);
+ struct bus_device *bd;
+ struct hppsoftc *hpp;
+ struct nssoftc *ns = &hppnssoftc[unit];
+
+ int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
+ int option_reg;
+
+ if (unit < 0 || unit >= NHPP ||
+ (bd = hpp_info[unit]) == 0 || !(bd->alive))
+ return ENXIO;
+
+ /*
+ * Start watchdog.
+ */
+ if (!hppwstart) {
+ hppwstart++;
+ timeout(hppwatch, 0, hz);
+ }
+ hpp = &hppsoftc[unit];
+ ns->sc_if.if_flags |= IFF_UP;
+
+ s = splimp();
+
+ /* Reset the 8390 and HP chip. */
+ option_reg = inw(ioaddr + HPP_OPTION);
+ outw( ioaddr + HPP_OPTION, option_reg & ~(NICReset + ChipReset) );
+ IO_DELAY; IO_DELAY;
+
+ /* Unreset the board and enable interrupts. */
+ outw( ioaddr + HPP_OPTION, option_reg | (EnableIRQ + NICReset + ChipReset));
+
+ /* Set the wrap registers for programmed-I/O operation. */
+ outw( ioaddr + HP_PAGING, HW_Page );
+ outw( ioaddr + 14, (HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8) );
+
+ /* Select the operational page. */
+ outw( ioaddr + HP_PAGING, Perf_Page );
+ nsinit(ns);
+
+ splx(s);
+
+ return (0);
+}
+
+/*
+ * needs to be called at splimp()?
+ *
+ */
+void
+hpp_reset_8390(ns)
+ struct nssoftc *ns;
+{
+ int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw( ioaddr + HPP_OPTION, option_reg & ~(NICReset + ChipReset) );
+ /* Pause a few cycles for the hardware reset to take place. */
+ IO_DELAY;
+ IO_DELAY;
+ ns->sc_txing = 0;
+ outw( ioaddr + HPP_OPTION, option_reg | (EnableIRQ + NICReset + ChipReset) );
+
+ /*
+ * XXX - I'm not sure there needs to be this many IO_DELAY's...
+ */
+ IO_DELAY; IO_DELAY;
+ IO_DELAY; IO_DELAY;
+
+ if ((inb_p(ioaddr + HPP_NIC_OFFSET + EN0_ISR) & ENISR_RESET) == 0)
+ printf("%s: hp_reset_8390() did not complete.\n", ns->sc_name);
+
+ return;
+}
+
+
+/*
+ * Block input and output, similar to the Crynwr packet driver.
+ * Note that transfer with the EtherTwist+ must be on word boundaries.
+ */
+void
+hpp_io_block_input(ns, count, buf, ring_offset)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int ring_offset;
+{
+ int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
+
+ outw(ioaddr + HPP_IN_ADDR, ring_offset);
+
+ insw(ioaddr + HP_DATAPORT, buf, count >> 1 );
+
+ if (count & 0x01)
+ buf[count-1] = (char) inw(ioaddr + HP_DATAPORT);
+
+}
+
+void
+hpp_mem_block_input(ns, count, buf, ring_offset)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int ring_offset;
+{
+ int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+ char *mem_start = (char *)phystokv(hppsoftc[ns->sc_unit].mem_start);
+
+ outw(ioaddr + HPP_IN_ADDR, ring_offset);
+ outw(ioaddr + HPP_OPTION, option_reg & ~(MemDisable + BootROMEnb));
+
+ /* copy as much as we can straight through */
+ bcopy16(mem_start, buf, count & ~1);
+
+ /* Now we copy that last byte. */
+ if (count & 0x01) {
+ u_short savebyte[2];
+
+ bcopy16(mem_start + (count & ~1), savebyte, 2);
+ buf[count-1] = savebyte[0];
+ }
+
+ outw(ioaddr + HPP_OPTION, option_reg);
+}
+
+
+/*
+ * output data into NIC buffers.
+ *
+ * NOTE: All transfers must be on word boundaries.
+ */
+int
+hpp_io_block_output(ns, count, buf, start_page)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int start_page;
+{
+ int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
+
+ outw(ioaddr + HPP_OUT_ADDR, start_page << 8) ;
+
+ if (count > 1) {
+ outsw(ioaddr + HP_DATAPORT, buf, count >> 1);
+ }
+
+ if ( (count & 1) == 1 ) {
+ u_char savebyte[2];
+
+ savebyte[1] = 0;
+ savebyte[0] = buf[count - 1];
+ outw(ioaddr + HP_DATAPORT, *(u_short *)savebyte);
+ }
+
+ if (count < (ETHERMIN + sizeof( struct ether_header )))
+ count = ETHERMIN + sizeof( struct ether_header );
+
+
+ return (count) ;
+}
+
+
+/* XXX
+ *
+ * I take great pains to not try and bcopy past the end of the buffer,
+ * does this matter? Are the io request buffers the exact byte size?
+ */
+int
+hpp_mem_block_output(ns, count, buf, start_page )
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int start_page;
+{
+ int ioaddr = ns->sc_port - HPP_NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+ struct hppsoftc *hpp = &hppsoftc[ns->sc_unit];
+ char *shmem;
+
+ outw(ioaddr + HPP_OUT_ADDR, start_page << 8);
+ outw(ioaddr + HPP_OPTION, option_reg & ~(MemDisable + BootROMEnb));
+
+ shmem = (char *)phystokv(hpp->mem_start);
+ bcopy16(buf, shmem, count & ~1);
+
+ if ( (count & 1) == 1 ) {
+ u_char savebyte[2];
+
+ savebyte[1] = 0;
+ savebyte[0] = buf[count - 1];
+ bcopy16(savebyte, shmem + (count & ~1), 2);
+ }
+
+ while (count < ETHERMIN + sizeof(struct ether_header)) {
+ *(shmem + count) = 0;
+ count++;
+ }
+
+ outw(ioaddr + HPP_OPTION, option_reg);
+
+ return count;
+}
+
+
+int
+hppintr(unit)
+ int unit;
+{
+ nsintr(&hppnssoftc[unit]);
+
+ return(0);
+}
+
+void
+hppstart(unit)
+ int unit;
+{
+ nsstart(&hppnssoftc[unit]);
+}
+
+int hppoutput();
+
+int
+hppoutput(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ int unit = minor(dev);
+ struct bus_device *ui;
+
+ if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_write(&hppnssoftc[unit].sc_if, hppstart, ior));
+}
+
+
+int
+hppsetinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t *filter;
+ unsigned filter_count;
+{
+ int unit = minor(dev);
+ struct bus_device *ui;
+
+ if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&hppnssoftc[unit].sc_if, receive_port,
+ priority, filter, filter_count));
+}
+
+
+int
+hppgetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned *count;
+{
+ int unit = minor(dev);
+ struct bus_device *ui;
+
+ if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_getstat(&hppnssoftc[unit].sc_if, flavor, status, count));
+}
+
+
+int
+hppsetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned count;
+{
+ int unit = minor(dev), oflags, s;
+ struct bus_device *ui;
+ struct ifnet *ifp;
+ struct net_status *ns;
+
+ if (unit >= NHPP || (ui = hpp_info[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ ifp = &hppnssoftc[unit].sc_if;
+
+ switch (flavor) {
+
+ case NET_STATUS:
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+ ns = (struct net_status *)status;
+ oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
+ if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
+ s = splimp();
+ nsinit(&hppnssoftc[unit]);
+ splx(s);
+ }
+ break;
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+/*
+ * Watchdog.
+ * Check for hung transmissions.
+ */
+void
+hppwatch()
+{
+ int unit, s;
+ struct nssoftc *ns;
+
+ timeout(hppwatch, 0, hz);
+
+ s = splimp();
+ for (unit = 0; unit < NHPP; unit++) {
+ if (hpp_info[unit] == 0 || hpp_info[unit]->alive == 0)
+ continue;
+ ns = &hppnssoftc[unit];
+ if (ns->sc_timer && --ns->sc_timer == 0) {
+ printf("hpp%d: transmission timeout\n", unit);
+ (*ns->sc_reset)(ns);
+ nsinit(ns);
+ }
+ }
+ splx(s);
+}
+
+
+#endif /* NHPP > 0 */
+
+
diff --git a/i386/i386at/gpl/if_ns.c b/i386/i386at/gpl/if_ns.c
new file mode 100644
index 00000000..da629cb3
--- /dev/null
+++ b/i386/i386at/gpl/if_ns.c
@@ -0,0 +1,642 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Written 1992,1993 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU Public License,
+ * incorporated herein by reference.
+ *
+ * The Author may be reached as becker@super.org or
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+
+#include <ul.h>
+#include <wd.h>
+#include <hpp.h>
+#if NUL > 0 || NWD > 0 || NHPP > 0
+/*
+ * Generic NS8390 routines.
+ * Derived from the Linux driver by Donald Becker.
+ *
+ * Shantanu Goel (goel@cs.columbia.edu)
+ */
+#include <sys/types.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <chips/busses.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/gpl/if_nsreg.h>
+
+#define IO_DELAY __asm__ __volatile__ ("outb %al,$0x80")
+#define outb_p(p, v) { outb(p, v); IO_DELAY; }
+#define inb_p(p) ({ unsigned char _v; _v = inb(p); IO_DELAY; _v; })
+
+#define NSDEBUG
+#ifdef NSDEBUG
+int nsdebug = 0;
+#define DEBUGF(stmt) { if (nsdebug) stmt; }
+#else
+#define DEBUGF(stmt)
+#endif
+
+void nsxint(struct nssoftc *);
+void nsrint(struct nssoftc *);
+void nsxmit(struct nssoftc *, unsigned, int);
+void nsrxoverrun(struct nssoftc *);
+
+/*
+ * Initialize the NIC.
+ * Must be called at splimp().
+ */
+void
+nsinit(sc)
+ struct nssoftc *sc;
+{
+ int port = sc->sc_port, i, rxconfig;
+ int endcfg = sc->sc_word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+ struct ifnet *ifp = &sc->sc_if;
+
+ /*
+ * Reset the board.
+ */
+ (*sc->sc_reset)(sc);
+
+ sc->sc_oactive = 0;
+ sc->sc_txing = 0;
+ sc->sc_timer = 0;
+ sc->sc_tx1 = sc->sc_tx2 = 0;
+ sc->sc_curpg = sc->sc_rxstrtpg;
+
+ /*
+ * Follow National Semiconductor's recommendations for
+ * initializing the DP83902.
+ */
+ outb_p(port, E8390_NODMA+E8390_PAGE0+E8390_STOP); /* 0x21 */
+ outb_p(port + EN0_DCFG, endcfg); /* 0x48 or 0x49 */
+
+ /*
+ * Clear remote byte count registers.
+ */
+ outb_p(port + EN0_RCNTLO, 0);
+ outb_p(port + EN0_RCNTHI, 0);
+
+ /*
+ * Set to monitor and loopback mode -- this is vital!
+ */
+ outb_p(port + EN0_RXCR, E8390_RXOFF); /* 0x20 */
+ outb_p(port + EN0_TXCR, E8390_TXOFF); /* 0x02 */
+
+ /*
+ * Set transmit page and receive ring.
+ */
+ outb_p(port + EN0_TPSR, sc->sc_txstrtpg);
+ outb_p(port + EN0_STARTPG, sc->sc_rxstrtpg);
+ outb_p(port + EN0_BOUNDARY, sc->sc_stoppg - 1);
+ outb_p(port + EN0_STOPPG, sc->sc_stoppg);
+
+ /*
+ * Clear pending interrupts and mask.
+ */
+ outb_p(port + EN0_ISR, 0xff);
+
+ /*
+ * Enable the following interrupts: receive/transmit complete,
+ * receive/transmit error, and Receiver OverWrite.
+ *
+ * Counter overflow and Remote DMA complete are *not* enabled.
+ */
+ outb_p(port + EN0_IMR, ENISR_RX | ENISR_TX | ENISR_RX_ERR |
+ ENISR_TX_ERR | ENISR_OVER );
+
+ /*
+ * Copy station address into 8390 registers.
+ */
+ outb_p(port, E8390_NODMA + E8390_PAGE1 + E8390_STOP); /* 0x61 */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ outb_p(port + EN1_PHYS + i, sc->sc_addr[i]);
+
+ /*
+ * Set up to accept all multicast packets.
+ */
+ for (i = 0; i < 8; i++)
+ outb_p(port + EN1_MULT + i, 0xff);
+
+ /*
+ * Initialize CURRent pointer
+ */
+ outb_p(port + EN1_CURPAG, sc->sc_rxstrtpg);
+
+ /*
+ * Program command register for page 0.
+ */
+ outb_p(port, E8390_NODMA + E8390_PAGE0 + E8390_STOP);
+
+#if 0
+ outb_p(port + EN0_ISR, 0xff);
+ outb_p(port + EN0_IMR, ENISR_ALL);
+#endif
+
+ outb_p(port + E8390_CMD, E8390_NODMA + E8390_PAGE0 + E8390_START);
+ outb_p(port + EN0_TXCR, E8390_TXCONFIG); /* xmit on */
+
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ rxconfig = E8390_RXCONFIG;
+ if (ifp->if_flags & IFF_ALLMULTI)
+ rxconfig |= 0x08;
+ if (ifp->if_flags & IFF_PROMISC)
+ rxconfig |= 0x10;
+ outb_p(port + EN0_RXCR, rxconfig); /* rx on */
+
+ /*
+ * Mark interface as up and start output.
+ */
+ ifp->if_flags |= IFF_RUNNING;
+ nsstart(sc);
+}
+
+/*
+ * Start output on interface.
+ * Must be called at splimp().
+ */
+void
+nsstart(sc)
+ struct nssoftc *sc;
+{
+ io_req_t ior;
+ struct ifnet *ifp = &sc->sc_if;
+
+ /*
+ * Drop packets if interface is down.
+ */
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ while (1) {
+ IF_DEQUEUE(&ifp->if_snd, ior);
+ if (ior == 0)
+ return;
+ iodone(ior);
+ }
+ }
+ /*
+ * If transmitter is busy, bail out.
+ */
+ if (sc->sc_oactive)
+ return;
+
+ /*
+ * Dequeue a packet.
+ */
+ IF_DEQUEUE(&ifp->if_snd, ior);
+ if (ior == 0)
+ return;
+
+ /* Mask interrupts from the ethercard. */
+ outb( sc->sc_port + EN0_IMR, 0x00);
+
+ if (sc->sc_pingpong) {
+ int count, output_page;
+
+ if (sc->sc_tx1 == 0) {
+ output_page = sc->sc_txstrtpg;
+ sc->sc_tx1 = count = (*sc->sc_output)(sc,
+ ior->io_count,
+ ior->io_data,
+ sc->sc_txstrtpg);
+ } else if (sc->sc_tx2 == 0) {
+ output_page = sc->sc_txstrtpg + 6;
+ sc->sc_tx2 = count = (*sc->sc_output)(sc,
+ ior->io_count,
+ ior->io_data,
+ output_page);
+ } else {
+ sc->sc_oactive = 1;
+ IF_PREPEND(&ifp->if_snd, ior);
+ return;
+ }
+
+ DEBUGF({
+ struct ether_header *eh;
+
+ eh = (struct ether_header *)ior->io_data;
+ printf("send: %s%d: %x:%x:%x:%x:%x:%x, "
+ "olen %d, len %d\n",
+ sc->sc_name, sc->sc_unit,
+ eh->ether_dhost[0], eh->ether_dhost[1],
+ eh->ether_dhost[2], eh->ether_dhost[3],
+ eh->ether_dhost[4], eh->ether_dhost[5],
+ ior->io_count, count);
+ });
+
+ if (!sc->sc_txing) {
+ nsxmit(sc, count, output_page);
+ if (output_page == sc->sc_txstrtpg)
+ sc->sc_tx1 = -1, sc->sc_lasttx = -1;
+ else
+ sc->sc_tx2 = -1, sc->sc_lasttx = -2;
+ }
+ sc->sc_oactive = (sc->sc_tx1 && sc->sc_tx2);
+ } else {
+ int count;
+
+ count = (*sc->sc_output)(sc, ior->io_count,
+ ior->io_data, sc->sc_txstrtpg);
+
+ DEBUGF({
+ struct ether_header *eh;
+
+ eh = (struct ether_header *)ior->io_data;
+ printf("send: %s%d: %x:%x:%x:%x:%x:%x, "
+ "olen %d, len %d\n",
+ sc->sc_name, sc->sc_unit,
+ eh->ether_dhost[0], eh->ether_dhost[1],
+ eh->ether_dhost[2], eh->ether_dhost[3],
+ eh->ether_dhost[4], eh->ether_dhost[5],
+ ior->io_count, count);
+ });
+
+ nsxmit(sc, count, sc->sc_txstrtpg);
+ sc->sc_oactive = 1;
+ }
+
+ /* reenable 8390 interrupts. */
+ outb_p(sc->sc_port + EN0_IMR, ENISR_ALL);
+
+ iodone(ior);
+}
+
+/*
+ * Interrupt routine.
+ * Called by board level driver.
+ */
+void
+nsintr(sc)
+ struct nssoftc *sc;
+{
+ int port = sc->sc_port;
+ int interrupts, boguscount = 0;
+ struct ifnet *ifp = &sc->sc_if;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ DEBUGF(printf("nsintr: %s%d: interface down\n",
+ sc->sc_name, sc->sc_unit));
+ return;
+ }
+
+ /*
+ * Change to page 0 and read intr status reg.
+ */
+ outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0);
+
+ while ((interrupts = inb_p(port + EN0_ISR)) != 0 && ++boguscount < 9) {
+ if (interrupts & ENISR_RDC) {
+ /*
+ * Ack meaningless DMA complete.
+ */
+ outb_p(port + EN0_ISR, ENISR_RDC);
+ }
+
+ if (interrupts & ENISR_OVER)
+ nsrxoverrun(sc);
+ else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+ nsrint(sc);
+ }
+
+ if (interrupts & ENISR_TX) {
+ nsxint(sc);
+ }
+ else if (interrupts & ENISR_COUNTERS) {
+ /*
+ * XXX - We really should be storing statistics
+ * about the interface. For now we just drop them.
+ */
+
+ /* reading resets the counters! */
+ (void) inb_p(port + EN0_COUNTER0); /* frame */
+ (void) inb_p(port + EN0_COUNTER1); /* crc */
+ (void) inb_p(port + EN0_COUNTER2); /* miss */
+
+ DEBUGF(printf("%s%d: acked counter interrupt.\n",
+ sc->sc_name, sc->sc_unit));
+
+ outb_p(port + EN0_ISR, ENISR_COUNTERS); /* ack intr */
+ }
+
+ if (interrupts & ENISR_TX_ERR) {
+ DEBUGF(printf("acking transmit error\n"));
+ outb_p(port + EN0_ISR, ENISR_TX_ERR); /* ack intr */
+ }
+
+ outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0+E8390_START);
+ }
+
+ DEBUGF({
+ if (interrupts) {
+ printf("%s%d: unknown interrupt 0x%x",
+ sc->sc_name, sc->sc_unit, interrupts);
+ outb_p(port + E8390_CMD,
+ E8390_NODMA+E8390_PAGE0+E8390_START);
+ outb_p(port + EN0_ISR, 0xff); /* ack all intrs */
+ }
+ })
+}
+
+/*
+ * Process a transmit interrupt.
+ */
+void
+nsxint(sc)
+ struct nssoftc *sc;
+{
+ int port = sc->sc_port, status;
+ struct ifnet *ifp = &sc->sc_if;
+
+ status = inb(port + EN0_TSR);
+ outb_p(port + EN0_ISR, ENISR_TX); /* ack intr */
+
+ sc->sc_txing = 0;
+ sc->sc_timer = 0;
+ sc->sc_oactive = 0;
+
+ if (sc->sc_pingpong) {
+ if (sc->sc_tx1 < 0) {
+ if (sc->sc_lasttx != 1 && sc->sc_lasttx != -1)
+ printf("%s%d: bogus last_tx_buffer %d,"
+ "tx1 = %d\n",
+ sc->sc_name, sc->sc_unit,
+ sc->sc_lasttx, sc->sc_tx1);
+ sc->sc_tx1 = 0;
+ if (sc->sc_tx2 > 0) {
+ nsxmit(sc, sc->sc_tx2, sc->sc_txstrtpg + 6);
+ sc->sc_tx2 = -1;
+ sc->sc_lasttx = 2;
+ } else
+ sc->sc_lasttx = 20;
+ } else if (sc->sc_tx2 < 0) {
+ if (sc->sc_lasttx != 2 && sc->sc_lasttx != -2)
+ printf("%s%d: bogus last_tx_buffer %d,"
+ "tx2 = %d\n",
+ sc->sc_name, sc->sc_unit,
+ sc->sc_lasttx, sc->sc_tx2);
+ sc->sc_tx2 = 0;
+ if (sc->sc_tx1 > 0) {
+ nsxmit(sc, sc->sc_tx1, sc->sc_txstrtpg);
+ sc->sc_tx1 = -1;
+ sc->sc_lasttx = 1;
+ } else
+ sc->sc_lasttx = 10;
+ } else
+ printf("%s%d: unexpected TX-done interrupt, "
+ "lasttx = %d\n",
+ sc->sc_name, sc->sc_unit, sc->sc_lasttx);
+ }
+ /*
+ * Update stats.
+ */
+ if (status & ENTSR_COL) {
+ if (status & ENTSR_ABT)
+ ifp->if_collisions += 16;
+ else
+ ifp->if_collisions += inb(port + EN0_NCR);
+ }
+ if (status & ENTSR_PTX) {
+ DEBUGF(printf("sent: %s%d\n", sc->sc_name, sc->sc_unit));
+ ifp->if_opackets++;
+ } else
+ ifp->if_oerrors++;
+
+ /*
+ * Start output on interface.
+ */
+ nsstart(sc);
+}
+
+/*
+ * Process a receive interrupt.
+ */
+void
+nsrint(sc)
+ struct nssoftc *sc;
+{
+ int port = sc->sc_port;
+ int rxing_page, this_frame, next_frame, current_offset;
+ int rx_pkt_count = 0;
+ int num_rx_pages = sc->sc_stoppg - sc->sc_rxstrtpg;
+ struct nspkthdr rx_frame;
+ struct ifnet *ifp = &sc->sc_if;
+
+ while (++rx_pkt_count < 10) {
+ int pkt_len;
+
+ /*
+ * Get the rx page (incoming packet pointer).
+ */
+ outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE1);
+ rxing_page = inb_p(port + EN1_CURPAG);
+ outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0);
+
+ /*
+ * Remove one frame from the ring.
+ * Boundary is always a page behind.
+ */
+ this_frame = inb_p(port + EN0_BOUNDARY) + 1;
+ if (this_frame >= sc->sc_stoppg)
+ this_frame = sc->sc_rxstrtpg;
+
+ DEBUGF({
+ if (this_frame != sc->sc_curpg)
+ printf("%s%d: mismatched read page pointers "
+ "%x vs %x\n",
+ sc->sc_name, sc->sc_unit,
+ this_frame, sc->sc_curpg);
+ });
+
+ if (this_frame == rxing_page) {
+ DEBUGF(printf("this_frame = rxing_page!\n"));
+ break;
+ }
+
+ current_offset = this_frame << 8;
+ (*sc->sc_input)(sc, sizeof(rx_frame), (char *)&rx_frame,
+ current_offset);
+
+ pkt_len = rx_frame.count - sizeof(rx_frame);
+
+ next_frame = this_frame + 1 + ((pkt_len + 4) >> 8);
+
+ if (rx_frame.next != next_frame
+ && rx_frame.next != next_frame + 1
+ && rx_frame.next != next_frame - num_rx_pages
+ && rx_frame.next != next_frame + 1 - num_rx_pages) {
+ sc->sc_curpg = rxing_page;
+ outb(port + EN0_BOUNDARY, sc->sc_curpg - 1);
+ ifp->if_ierrors++;
+ DEBUGF(printf("INPUT ERROR?\n"));
+ continue;
+ }
+ if (pkt_len < 60 || pkt_len > 1518) {
+ ifp->if_ierrors++;
+ DEBUGF(printf("%s%d: bad packet length %d\n",
+ sc->sc_name, sc->sc_unit, pkt_len));
+ } else if ((rx_frame.status & 0x0f) == ENRSR_RXOK) {
+ ipc_kmsg_t kmsg;
+
+ kmsg = net_kmsg_get();
+ if (kmsg == 0) {
+ DEBUGF(printf("%s%d: dropped packet\n",
+ sc->sc_name, sc->sc_unit));
+ ifp->if_rcvdrops++;
+ } else {
+ int len, off;
+ struct ether_header *eh;
+ struct packet_header *pkt;
+
+ ifp->if_ipackets++;
+ off = current_offset + sizeof(rx_frame);
+ eh = ((struct ether_header *)
+ (&net_kmsg(kmsg)->header[0]));
+ (*sc->sc_input)(sc,
+ sizeof(struct ether_header),
+ (char *)eh, off);
+ off += sizeof(struct ether_header);
+ len = pkt_len - sizeof(struct ether_header);
+
+ DEBUGF(printf("rcv: %s%d: %x:%x:%x:%x:%x:%x, "
+ "len %d, type 0x%x\n",
+ sc->sc_name, sc->sc_unit,
+ eh->ether_shost[0],
+ eh->ether_shost[1],
+ eh->ether_shost[2],
+ eh->ether_shost[3],
+ eh->ether_shost[4],
+ eh->ether_shost[5],
+ len, eh->ether_type));
+
+ pkt = ((struct packet_header *)
+ (&net_kmsg(kmsg)->packet[0]));
+ (*sc->sc_input)(sc, len, (char *)(pkt+1), off);
+ pkt->type = eh->ether_type;
+ pkt->length = len+sizeof(struct packet_header);
+ net_packet(ifp, kmsg, pkt->length,
+ ethernet_priority(kmsg));
+ }
+ } else {
+ DEBUGF(printf("%s%d: bogus packet: "
+ "status=0x%x nxpg=0x%x size=%d\n",
+ sc->sc_name, sc->sc_unit,
+ rx_frame.status, rx_frame.next,
+ rx_frame.count));
+ ifp->if_ierrors++;
+ }
+ next_frame = rx_frame.next;
+ if (next_frame >= sc->sc_stoppg) {
+ DEBUGF(printf("%s%d: next frame inconsistency, 0x%x\n",
+ sc->sc_name, sc->sc_unit, next_frame));
+ next_frame = sc->sc_rxstrtpg;
+ }
+ sc->sc_curpg = next_frame;
+ outb(port + EN0_BOUNDARY, next_frame - 1);
+ }
+
+ /*
+ * Bug alert! Reset ENISR_OVER to avoid spurious overruns!
+ */
+ outb_p(port + EN0_ISR, ENISR_RX+ENISR_RX_ERR+ENISR_OVER);
+}
+
+/*
+ * Handle a receive overrun condition.
+ *
+ * XXX - this needs to be gone over in light of the NS documentation.
+ */
+void
+nsrxoverrun(sc)
+ struct nssoftc *sc;
+{
+ int port = sc->sc_port, i;
+ extern unsigned delaycount;
+
+ printf("%s%d: receive overrun\n", sc->sc_name, sc->sc_unit);
+
+ /*
+ * We should already be stopped and in page0, but just to be sure...
+ */
+ outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0+E8390_STOP);
+
+ /*
+ * Clear remote byte counter registers.
+ */
+ outb_p(port + EN0_RCNTLO, 0);
+ outb_p(port + EN0_RCNTHI, 0);
+
+ /*
+ * Wait for reset to complete.
+ */
+ for (i = delaycount*2; i && !(inb_p(port+EN0_ISR) & ENISR_RESET); i--)
+ ;
+ if (i == 0) {
+ printf("%s%d: reset did not complete at overrun\n",
+ sc->sc_name, sc->sc_unit);
+ nsinit(sc);
+ return;
+ }
+ /*
+ * Disable transmitter.
+ */
+ outb_p(port + EN0_TXCR, E8390_TXOFF);
+
+ /*
+ * Remove packets.
+ */
+ nsrint(sc);
+
+ outb_p(port + EN0_ISR, 0xff);
+ outb_p(port + E8390_CMD, E8390_NODMA+E8390_PAGE0+E8390_START);
+ outb_p(port + EN0_TXCR, E8390_TXCONFIG);
+}
+
+/*
+ * Trigger a transmit start.
+ */
+void
+nsxmit(sc, length, start_page)
+ struct nssoftc *sc;
+ unsigned length;
+ int start_page;
+{
+ int port = sc->sc_port;
+
+ sc->sc_txing = 1;
+ outb_p(port, E8390_NODMA+E8390_PAGE0);
+ if (inb_p(port) & E8390_TRANS) {
+ printf("%s%d: nsxmit() called with the transmitter busy\n",
+ sc->sc_name, sc->sc_unit);
+ return;
+ }
+ outb_p(port + EN0_TCNTLO, length & 0xff);
+ outb_p(port + EN0_TCNTHI, (length >> 8) & 0xff);
+ outb_p(port + EN0_TPSR, start_page);
+ outb_p(port, E8390_NODMA+E8390_TRANS+E8390_START);
+ sc->sc_timer = 4;
+}
+
+#endif /* NUL > 0 || NWD > 0 */
diff --git a/i386/i386at/gpl/if_nsreg.h b/i386/i386at/gpl/if_nsreg.h
new file mode 100644
index 00000000..89447976
--- /dev/null
+++ b/i386/i386at/gpl/if_nsreg.h
@@ -0,0 +1,169 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Written 1992,1993 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU Public License,
+ * incorporated herein by reference.
+ *
+ * The Author may be reached as becker@super.org or
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+
+/*
+ * Generic NS8390 definitions.
+ * Derived from the Linux driver by Donald Becker.
+ */
+
+#define ETHER_ADDR_LEN 6
+
+/*
+ * 8390 state.
+ */
+struct nssoftc {
+ struct ifnet sc_if; /* interface header */
+ u_char sc_addr[ETHER_ADDR_LEN]; /* station address */
+ /*
+ * The following are board specific.
+ * reset() - resets the NIC and board
+ * input() - read data into buffer from supplied offset
+ * output() - write data from buffer into supplied page
+ * the data is padded if necessary and the actual
+ * count is returned.
+ */
+ void (*sc_reset)(struct nssoftc *);
+ void (*sc_input)(struct nssoftc *, int, char *, int);
+ int (*sc_output)(struct nssoftc *, int, char *, int);
+ int sc_word16:1; /* 16 bit (vs 8 bit) board */
+ int sc_txing:1; /* transmit active */
+ int sc_pingpong:1; /* using ping-pong driver */
+ int sc_oactive:1; /* transmitter is active */
+ u_char sc_txstrtpg; /* starting transmit page */
+ u_char sc_rxstrtpg; /* starting receive page */
+ u_char sc_stoppg; /* stop page */
+ u_char sc_curpg; /* current page */
+ short sc_tx1; /* packet lengths for ping-pong transmit */
+ short sc_tx2;
+ short sc_lasttx;
+ u_char sc_timer; /* watchdog */
+ int sc_port; /* I/O port of 8390 */
+ char *sc_name; /* name of board */
+ int sc_unit; /* unit in driver */
+};
+
+#define TX_2X_PAGES 12
+#define TX_1X_PAGES 6
+#define TX_PAGES(ns) ((ns)->sc_pingpong ? TX_2X_PAGES : TX_1X_PAGES)
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK 0x5
+#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
+#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+#define E8390_CMD 0x00 /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS 0x01 /* word transfer mode selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
+#define EN1_CURPAG 0x07 /* Current memory page RD WR */
+#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicase address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occured during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
+
+/* The per-packet-header format. */
+struct nspkthdr {
+ u_char status; /* status */
+ u_char next; /* pointer to next packet. */
+ u_short count; /* header + packet length in bytes */
+};
+
+void nsinit(struct nssoftc *);
+void nsstart(struct nssoftc *);
+void nsintr(struct nssoftc *);
diff --git a/i386/i386at/gpl/if_ul.c b/i386/i386at/gpl/if_ul.c
new file mode 100644
index 00000000..59e74f55
--- /dev/null
+++ b/i386/i386at/gpl/if_ul.c
@@ -0,0 +1,489 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Written 1993 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU Public License,
+ * incorporated herein by reference.
+ *
+ * The Author may be reached as becker@super.org or
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+
+#include <wd.h>
+#include <ul.h>
+#if NUL > 0
+/*
+ * Driver for SMC Ultra ethernet adaptor.
+ * Derived from the Linux driver by Donald Becker.
+ *
+ * Shantanu Goel (goel@cs.columbia.edu)
+ */
+#include <mach/sa/sys/types.h>
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <chips/busses.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/gpl/if_nsreg.h>
+
+#define START_PG 0x00 /* first page of TX buffer */
+#define ULTRA_CMDREG 0 /* offset of ASIC command register */
+#define ULTRA_RESET 0x80 /* board reset in ULTRA_CMDREG */
+#define ULTRA_MEMEN 0x40 /* enable shared memory */
+#define ULTRA_NIC_OFF 16 /* NIC register offset */
+
+#define ulunit(dev) minor(dev)
+
+/*
+ * Autoconfiguration stuff.
+ */
+int ulprobe();
+void ulattach();
+int ulstd[] = { 0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0 };
+struct bus_device *ulinfo[NUL];
+struct bus_driver uldriver = {
+ ulprobe, 0, ulattach, 0, ulstd, "ul", ulinfo, 0, 0, 0
+};
+
+/*
+ * NS8390 state.
+ */
+struct nssoftc ulnssoftc[NUL];
+
+/*
+ * Ultra state.
+ */
+struct ulsoftc {
+ int sc_mstart; /* start of board's RAM */
+ int sc_mend; /* end of board's RAM */
+ int sc_rmstart; /* start of receive RAM */
+ int sc_rmend; /* end of receive RAM */
+} ulsoftc[NUL];
+
+void ulstart(int);
+void ul_reset(struct nssoftc *sc);
+void ul_input(struct nssoftc *sc, int, char *, int);
+int ul_output(struct nssoftc *sc, int, char *, int);
+
+/*
+ * Watchdog.
+ */
+int ulwstart = 0;
+void ulwatch(void);
+
+#define ULDEBUG
+#ifdef ULDEBUG
+int uldebug = 0;
+#define DEBUGF(stmt) { if (uldebug) stmt; }
+#else
+#define DEBUGF(stmt)
+#endif
+
+/*
+ * Probe for the Ultra.
+ * This looks like an 8013 with the station address PROM
+ * at I/O ports <base>+8 to <base>+13, with a checksum following.
+ */
+int
+ulprobe(xxx, ui)
+ int xxx;
+ struct bus_device *ui;
+{
+ int *port;
+
+ if (ui->unit >= NUL) {
+ printf("ul%d: not configured\n", ui->unit);
+ return (0);
+ }
+ for (port = ulstd; *port; port++) {
+ if (*port < 0)
+ continue;
+ /*
+ * Check chip ID nibble.
+ */
+ if ((inb(*port + 7) & 0xf0) != 0x20)
+ continue;
+ if (ulprobe1(*port, ui)) {
+ ui->address = *port;
+ *port = -1;
+#if NWD > 0
+ /*
+ * XXX: The Western Digital/SMC driver can sometimes
+ * probe the Ultra incorrectly. Remove the Ultra's
+ * port from it's list to avoid the problem.
+ */
+ {
+ int i;
+ extern int wdstd[];
+
+ for (i = 0; wdstd[i]; i++) {
+ if (wdstd[i] == ui->address) {
+ wdstd[i] = -1;
+ break;
+ }
+ }
+ }
+#endif
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+ulprobe1(port, ui)
+ int port;
+ struct bus_device *ui;
+{
+ u_char num_pages, irqreg, addr, reg4;
+ u_char irqmap[] = { 0, 9, 3, 5, 7, 10, 11, 15 };
+ short num_pages_tbl[4] = { 0x20, 0x40, 0x80, 0xff };
+ int i, irq, checksum = 0;
+ int addr_tbl[4] = { 0x0c0000, 0x0e0000, 0xfc0000, 0xfe0000 };
+ struct ulsoftc *ul = &ulsoftc[ui->unit];
+ struct nssoftc *ns = &ulnssoftc[ui->unit];
+ struct ifnet *ifp = &ns->sc_if;
+
+ /*
+ * Select the station address register set.
+ */
+ reg4 = inb(port + 4) & 0x7f;
+ outb(port + 4, reg4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(port + 8 + i);
+ if ((checksum & 0xff) != 0xff)
+ return (0);
+
+ /*
+ * Use 2 transmit buffers.
+ */
+ ns->sc_pingpong = 1;
+
+ printf("ul%d: SMC Ultra at 0x%03x, ", ui->unit, port);
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ if (i == 0)
+ printf("%02x", ns->sc_addr[i] = inb(port + 8 + i));
+ else
+ printf(":%02x", ns->sc_addr[i] = inb(port + 8 + i));
+ }
+ /*
+ * Switch from station address to alternate register set
+ * and read useful registers there.
+ */
+ outb(port + 4, 0x80 | reg4);
+
+ /*
+ * Enable FINE16 mode to avoid BIOS ROM width mismatches
+ * during reboot.
+ */
+ outb(port + 0x0c, 0x80 | inb(port + 0x0c));
+ irqreg = inb(port + 0x0d);
+ addr = inb(port + 0x0b);
+
+ /*
+ * Switch back to station address register set so the MSDOG
+ * driver can find the card after a warm boot.
+ */
+ outb(port + 4, reg4);
+
+ /*
+ * Determine IRQ. The IRQ bits are split.
+ */
+ irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+ if (irq == 0) {
+ printf(", failed to detect IRQ line.\n");
+ return (0);
+ }
+ ui->sysdep1 = irq;
+ take_dev_irq(ui);
+ printf(", irq %d", irq);
+
+ /*
+ * Determine board's RAM location.
+ */
+ ul->sc_mstart = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3];
+ num_pages = num_pages_tbl[(addr >> 4) & 3];
+ ul->sc_rmstart = ul->sc_mstart + TX_PAGES(ns) * 256;
+ ul->sc_mend = ul->sc_rmend
+ = ul->sc_mstart + (num_pages - START_PG) * 256;
+ printf(", memory 0x%05x-0x%05x\n", ul->sc_mstart, ul->sc_mend);
+
+ /*
+ * Initialize 8390 state.
+ */
+ ns->sc_name = ui->name;
+ ns->sc_unit = ui->unit;
+ ns->sc_port = port + ULTRA_NIC_OFF;
+ ns->sc_word16 = 1;
+ ns->sc_txstrtpg = START_PG;
+ ns->sc_rxstrtpg = START_PG + TX_PAGES(ns);
+ ns->sc_stoppg = num_pages;
+ ns->sc_reset = ul_reset;
+ ns->sc_input = ul_input;
+ ns->sc_output = ul_output;
+
+ DEBUGF(printf("ul%d: txstrtpg %d rxstrtpg %d num_pages %d\n",
+ ui->unit, ns->sc_txstrtpg, ns->sc_rxstrtpg, num_pages));
+
+ /*
+ * Initialize interface header.
+ */
+ ifp->if_unit = ui->unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = ETHER_ADDR_LEN;
+ ifp->if_address = ns->sc_addr;
+ if_init_queues(ifp);
+
+ return (1);
+}
+
+void
+ulattach(ui)
+ struct bus_device *ui;
+{
+ /*
+ * void
+ */
+}
+
+int
+ulopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ int unit = ulunit(dev), s;
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ /*
+ * Start watchdog.
+ */
+ if (!ulwstart) {
+ ulwstart++;
+ timeout(ulwatch, 0, hz);
+ }
+ ulnssoftc[unit].sc_if.if_flags |= IFF_UP;
+ s = splimp();
+ outb(ui->address, ULTRA_MEMEN); /* enable memory, 16 bit mode */
+ outb(ui->address + 5, 0x80);
+ outb(ui->address + 6, 0x01); /* enable interrupts and memory */
+ nsinit(&ulnssoftc[unit]);
+ splx(s);
+ return (0);
+}
+
+int
+uloutput(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ int unit = ulunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_write(&ulnssoftc[unit].sc_if, ulstart, ior));
+}
+
+int
+ulsetinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t *filter;
+ unsigned filter_count;
+{
+ int unit = ulunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&ulnssoftc[unit].sc_if, receive_port,
+ priority, filter, filter_count));
+}
+
+int
+ulgetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned *count;
+{
+ int unit = ulunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_getstat(&ulnssoftc[unit].sc_if, flavor, status, count));
+}
+
+int
+ulsetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned count;
+{
+ int unit = ulunit(dev), oflags, s;
+ struct bus_device *ui;
+ struct ifnet *ifp;
+ struct net_status *ns;
+
+ if (unit >= NUL || (ui = ulinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ ifp = &ulnssoftc[unit].sc_if;
+
+ switch (flavor) {
+
+ case NET_STATUS:
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+ ns = (struct net_status *)status;
+ oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
+ if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
+ s = splimp();
+ nsinit(&ulnssoftc[unit]);
+ splx(s);
+ }
+ break;
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+void
+ulintr(unit)
+ int unit;
+{
+ nsintr(&ulnssoftc[unit]);
+}
+
+void
+ulstart(unit)
+ int unit;
+{
+ nsstart(&ulnssoftc[unit]);
+}
+
+void
+ul_reset(ns)
+ struct nssoftc *ns;
+{
+ int port = ns->sc_port - ULTRA_NIC_OFF; /* ASIC base address */
+
+ outb(port, ULTRA_RESET);
+ outb(0x80, 0); /* I/O delay */
+ outb(port, ULTRA_MEMEN);
+}
+
+void
+ul_input(ns, count, buf, ring_offset)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int ring_offset;
+{
+ int xfer_start;
+ struct ulsoftc *ul = &ulsoftc[ns->sc_unit];
+
+ DEBUGF(printf("ul%d: ring_offset = %d\n", ns->sc_unit, ring_offset));
+
+ xfer_start = ul->sc_mstart + ring_offset - (START_PG << 8);
+ if (xfer_start + count > ul->sc_rmend) {
+ int semi_count = ul->sc_rmend - xfer_start;
+
+ /*
+ * Input move must be wrapped.
+ */
+ bcopy((char *)phystokv(xfer_start), buf, semi_count);
+ count -= semi_count;
+ bcopy((char *)phystokv(ul->sc_rmstart), buf+semi_count, count);
+ } else
+ bcopy((char *)phystokv(xfer_start), buf, count);
+}
+
+int
+ul_output(ns, count, buf, start_page)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int start_page;
+{
+ char *shmem;
+ int i;
+ struct ulsoftc *ul = &ulsoftc[ns->sc_unit];
+
+ DEBUGF(printf("ul%d: start_page = %d\n", ns->sc_unit, start_page));
+
+ shmem = (char *)phystokv(ul->sc_mstart + ((start_page-START_PG) << 8));
+ bcopy(buf, shmem, count);
+ while (count < ETHERMIN + sizeof(struct ether_header)) {
+ *(shmem + count) = 0;
+ count++;
+ }
+ return (count);
+}
+
+/*
+ * Watchdog.
+ * Check for hung transmissions.
+ */
+void
+ulwatch()
+{
+ int unit, s;
+ struct nssoftc *ns;
+
+ timeout(ulwatch, 0, hz);
+
+ s = splimp();
+ for (unit = 0; unit < NUL; unit++) {
+ if (ulinfo[unit] == 0 || ulinfo[unit]->alive == 0)
+ continue;
+ ns = &ulnssoftc[unit];
+ if (ns->sc_timer && --ns->sc_timer == 0) {
+ printf("ul%d: transmission timeout\n", unit);
+ nsinit(ns);
+ }
+ }
+ splx(s);
+}
+
+#endif /* NUL > 0 */
diff --git a/i386/i386at/gpl/if_wd.c b/i386/i386at/gpl/if_wd.c
new file mode 100644
index 00000000..c569a3e1
--- /dev/null
+++ b/i386/i386at/gpl/if_wd.c
@@ -0,0 +1,581 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Written 1993 by Donald Becker.
+ *
+ * Copyright 1993 United States Government as represented by the
+ * Director, National Security Agency. This software may be used and
+ * distributed according to the terms of the GNU Public License,
+ * incorporated herein by reference.
+ *
+ * The Author may be reached as becker@super.org or
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ */
+
+#include <wd.h>
+#if NWD > 0
+/*
+ * Driver for SMC/Western Digital Ethernet adaptors.
+ * Derived from the Linux driver by Donald Becker.
+ *
+ * Shantanu Goel (goel@cs.columbia.edu)
+ */
+#include <mach/sa/sys/types.h>
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <chips/busses.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/gpl/if_nsreg.h>
+
+#define WD_START_PG 0x00 /* first page of TX buffer */
+#define WD03_STOP_PG 0x20 /* last page +1 of RX ring */
+#define WD13_STOP_PG 0x40 /* last page +1 of RX ring */
+
+#define WD_CMDREG 0 /* offset of ASIC command register */
+#define WD_RESET 0x80 /* board reset in WDTRA_CMDREG */
+#define WD_MEMEN 0x40 /* enable shared memory */
+#define WD_CMDREG5 5 /* offset of 16-bit-only ASIC register 5 */
+#define ISA16 0x80 /* enable 16 bit access from the ISA bus */
+#define NIC16 0x40 /* enable 16 bit access from the 8390 */
+#define WD_NIC_OFF 16 /* NIC register offset */
+
+#define wdunit(dev) minor(dev)
+
+/*
+ * Autoconfiguration stuff.
+ */
+int wdprobe();
+void wdattach();
+int wdstd[] = { 0x300, 0x280, 0x380, 0x240, 0 };
+struct bus_device *wdinfo[NWD];
+struct bus_driver wddriver = {
+ wdprobe, 0, wdattach, 0, wdstd, "wd", wdinfo, 0, 0, 0
+};
+
+/*
+ * NS8390 state.
+ */
+struct nssoftc wdnssoftc[NWD];
+
+/*
+ * Board state.
+ */
+struct wdsoftc {
+ int sc_mstart; /* start of board's RAM */
+ int sc_mend; /* end of board's RAM */
+ int sc_rmstart; /* start of receive RAM */
+ int sc_rmend; /* end of receive RAM */
+ int sc_reg0; /* copy of register 0 of ASIC */
+ int sc_reg5; /* copy of register 5 of ASIC */
+} wdsoftc[NWD];
+
+void wdstart(int);
+void wd_reset(struct nssoftc *sc);
+void wd_input(struct nssoftc *sc, int, char *, int);
+int wd_output(struct nssoftc *sc, int, char *, int);
+
+/*
+ * Watchdog.
+ */
+int wdwstart = 0;
+void wdwatch(void);
+
+#define WDDEBUG
+#ifdef WDDEBUG
+int wddebug = 0;
+#define DEBUGF(stmt) { if (wddebug) stmt; }
+#else
+#define DEBUGF(stmt)
+#endif
+
+/*
+ * Probe for the WD8003 and WD8013.
+ * These cards have the station address PROM at I/O ports <base>+8
+ * to <base>+13, with a checksum following. A Soundblaster can have
+ * the same checksum as a WD ethercard, so we have an extra exclusionary
+ * check for it.
+ */
+int
+wdprobe(xxx, ui)
+ int xxx;
+ struct bus_device *ui;
+{
+ int *port;
+
+ if (ui->unit >= NWD) {
+ printf("wd%d: not configured\n", ui->unit);
+ return (0);
+ }
+ for (port = wdstd; *port; port++) {
+ if (*port < 0)
+ continue;
+ if (inb(*port + 8) != 0xff
+ && inb(*port + 9) != 0xff
+ && wdprobe1(*port, ui)) {
+ ui->address = *port;
+ *port = -1;
+ return (1);
+ }
+ }
+ return (0);
+}
+
+int
+wdprobe1(port, ui)
+ int port;
+ struct bus_device *ui;
+{
+
+ int i, irq = 0, checksum = 0, ancient = 0, word16 = 0;
+ struct wdsoftc *wd = &wdsoftc[ui->unit];
+ struct nssoftc *ns = &wdnssoftc[ui->unit];
+ struct ifnet *ifp = &ns->sc_if;
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(port + 8 + i);
+ if ((checksum & 0xff) != 0xff)
+ return (0);
+
+ printf("wd%d: WD80x3 at 0x%03x, ", ui->unit, port);
+ for (i = 0; i < ETHER_ADDR_LEN; i++) {
+ if (i == 0)
+ printf("%02x", ns->sc_addr[i] = inb(port + 8 + i));
+ else
+ printf(":%02x", ns->sc_addr[i] = inb(port + 8 + i));
+ }
+ /*
+ * Check for PureData.
+ */
+ if (inb(port) == 'P' && inb(port + 1) == 'D') {
+ u_char reg5 = inb(port + 5);
+
+ switch (inb(port + 2)) {
+
+ case 0x03:
+ case 0x05:
+ word16 = 0;
+ break;
+
+ case 0x0a:
+ word16 = 1;
+ break;
+
+ default:
+ word16 = 0;
+ break;
+ }
+ wd->sc_mstart = ((reg5 & 0x1c) + 0xc0) << 12;
+ irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+ } else {
+ /*
+ * Check for 8 bit vs 16 bit card.
+ */
+ for (i = 0; i < ETHER_ADDR_LEN; i++)
+ if (inb(port + i) != inb(port + 8 + i))
+ break;
+ if (i >= ETHER_ADDR_LEN) {
+ ancient = 1;
+ word16 = 0;
+ } else {
+ int tmp = inb(port + 1);
+
+ /*
+ * Attempt to clear 16bit bit.
+ */
+ outb(port + 1, tmp ^ 0x01);
+ if (((inb(port + 1) & 0x01) == 0x01) /* 16 bit */
+ && (tmp & 0x01) == 0x01) { /* in 16 bit slot */
+ int asic_reg5 = inb(port + WD_CMDREG5);
+
+ /*
+ * Magic to set ASIC to word-wide mode.
+ */
+ outb(port+WD_CMDREG5, NIC16|(asic_reg5&0x1f));
+ outb(port + 1, tmp);
+ word16 = 1;
+ } else
+ word16 = 0;
+ outb(port + 1, tmp);
+ }
+ if (!ancient && (inb(port + 1) & 0x01) != (word16 & 0x01))
+ printf("\nwd%d: bus width conflict, "
+ "%d (probe) != %d (reg report)", ui->unit,
+ word16 ? 16 : 8, (inb(port+1) & 0x01) ? 16 : 8);
+ }
+ /*
+ * Determine board's RAM location.
+ */
+ if (wd->sc_mstart == 0) {
+ int reg0 = inb(port);
+
+ if (reg0 == 0xff || reg0 == 0)
+ wd->sc_mstart = 0xd0000;
+ else {
+ int high_addr_bits = inb(port + WD_CMDREG5) & 0x1f;
+
+ if (high_addr_bits == 0x1f || word16 == 0)
+ high_addr_bits = 0x01;
+ wd->sc_mstart = ((reg0&0x3f)<<13)+(high_addr_bits<<19);
+ }
+ }
+ /*
+ * Determine irq.
+ */
+ if (irq == 0) {
+ int irqmap[] = { 9, 3, 5, 7, 10, 11, 15, 4 };
+ int reg1 = inb(port + 1);
+ int reg4 = inb(port + 4);
+
+ /*
+ * For old card, irq must be supplied.
+ */
+ if (ancient || reg1 == 0xff) {
+ if (ui->sysdep1 == 0) {
+ printf("\nwd%d: must specify IRQ for card\n",
+ ui->unit);
+ return (0);
+ }
+ irq = ui->sysdep1;
+ } else {
+ DEBUGF({
+ int i = ((reg4 >> 5) & 0x03) + (reg1 & 0x04);
+
+ printf("\nwd%d: irq index %d\n", ui->unit, i);
+ printf("wd%d:", ui->unit);
+ })
+ irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+ }
+ } else if (irq == 2)
+ irq = 9;
+ ui->sysdep1 = irq;
+ take_dev_irq(ui);
+ printf(", irq %d", irq);
+
+ /*
+ * Initialize 8390 state.
+ */
+ ns->sc_name = ui->name;
+ ns->sc_unit = ui->unit;
+ ns->sc_port = port + WD_NIC_OFF;
+ ns->sc_reset = wd_reset;
+ ns->sc_input = wd_input;
+ ns->sc_output = wd_output;
+ ns->sc_pingpong = 1;
+ ns->sc_word16 = word16;
+ ns->sc_txstrtpg = WD_START_PG;
+ ns->sc_rxstrtpg = WD_START_PG + TX_PAGES(ns);
+ ns->sc_stoppg = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+
+ wd->sc_rmstart = wd->sc_mstart + TX_PAGES(ns) * 256;
+ wd->sc_mend = wd->sc_rmend
+ = wd->sc_mstart + (ns->sc_stoppg - WD_START_PG) * 256;
+ printf(", memory 0x%05x-0x%05x", wd->sc_mstart, wd->sc_mend);
+
+ if (word16)
+ printf(", 16 bit");
+ printf("\n");
+
+ DEBUGF(printf("wd%d: txstrtpg %d rxstrtpg %d num_pages %d\n",
+ ui->unit, ns->sc_txstrtpg, ns->sc_rxstrtpg,
+ (wd->sc_mend - wd->sc_mstart) / 256));
+
+ /*
+ * Initialize interface header.
+ */
+ ifp->if_unit = ui->unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = ETHER_ADDR_LEN;
+ ifp->if_address = ns->sc_addr;
+ if_init_queues(ifp);
+
+ return (1);
+}
+
+void
+wdattach(ui)
+ struct bus_device *ui;
+{
+ /*
+ * void
+ */
+}
+
+int
+wdopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ int unit = wdunit(dev), s;
+ struct bus_device *ui;
+ struct wdsoftc *wd;
+ struct nssoftc *ns;
+
+ if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ /*
+ * Start watchdog.
+ */
+ if (!wdwstart) {
+ wdwstart++;
+ timeout(wdwatch, 0, hz);
+ }
+ wd = &wdsoftc[unit];
+ ns = &wdnssoftc[unit];
+ ns->sc_if.if_flags |= IFF_UP;
+ s = splimp();
+ wd->sc_reg0 = ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN;
+ wd->sc_reg5 = ((wd->sc_mstart >> 19) & 0x1f) | NIC16;
+ if (ns->sc_word16)
+ outb(ui->address + WD_CMDREG5, wd->sc_reg5);
+ outb(ui->address, wd->sc_reg0);
+ nsinit(ns);
+ splx(s);
+ return (0);
+}
+
+int
+wdoutput(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ int unit = wdunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_write(&wdnssoftc[unit].sc_if, wdstart, ior));
+}
+
+int
+wdsetinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t *filter;
+ unsigned filter_count;
+{
+ int unit = wdunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&wdnssoftc[unit].sc_if, receive_port,
+ priority, filter, filter_count));
+}
+
+int
+wdgetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned *count;
+{
+ int unit = wdunit(dev);
+ struct bus_device *ui;
+
+ if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ return (net_getstat(&wdnssoftc[unit].sc_if, flavor, status, count));
+}
+
+int
+wdsetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned count;
+{
+ int unit = wdunit(dev), oflags, s;
+ struct bus_device *ui;
+ struct ifnet *ifp;
+ struct net_status *ns;
+
+ if (unit >= NWD || (ui = wdinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ ifp = &wdnssoftc[unit].sc_if;
+
+ switch (flavor) {
+
+ case NET_STATUS:
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+ ns = (struct net_status *)status;
+ oflags = ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags &= ~(IFF_ALLMULTI|IFF_PROMISC);
+ ifp->if_flags |= ns->flags & (IFF_ALLMULTI|IFF_PROMISC);
+ if ((ifp->if_flags & (IFF_ALLMULTI|IFF_PROMISC)) != oflags) {
+ s = splimp();
+ nsinit(&wdnssoftc[unit]);
+ splx(s);
+ }
+ break;
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+void
+wdintr(unit)
+ int unit;
+{
+ nsintr(&wdnssoftc[unit]);
+}
+
+void
+wdstart(unit)
+ int unit;
+{
+ nsstart(&wdnssoftc[unit]);
+}
+
+void
+wd_reset(ns)
+ struct nssoftc *ns;
+{
+ int port = ns->sc_port - WD_NIC_OFF; /* ASIC base address */
+ struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
+
+ outb(port, WD_RESET);
+ outb(0x80, 0); /* I/O delay */
+ /*
+ * Set up the ASIC registers, just in case something changed them.
+ */
+ outb(port, ((wd->sc_mstart >> 13) & 0x3f) | WD_MEMEN);
+ if (ns->sc_word16)
+ outb(port + WD_CMDREG5, NIC16 | ((wd->sc_mstart>>19) & 0x1f));
+}
+
+void
+wd_input(ns, count, buf, ring_offset)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int ring_offset;
+{
+ int port = ns->sc_port - WD_NIC_OFF;
+ int xfer_start;
+ struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
+
+ DEBUGF(printf("wd%d: ring_offset = %d\n", ns->sc_unit, ring_offset));
+
+ xfer_start = wd->sc_mstart + ring_offset - (WD_START_PG << 8);
+
+ /*
+ * The NIC driver calls us 3 times. Once to read the NIC 4 byte
+ * header, next to read the Ethernet header and finally to read
+ * the actual data. We enable 16 bit mode before the NIC header
+ * and disable it after the packet body.
+ */
+ if (count == 4) {
+ if (ns->sc_word16)
+ outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5);
+ ((int *)buf)[0] = ((int *)phystokv(xfer_start))[0];
+ return;
+ }
+ if (count == sizeof(struct ether_header)) {
+ xfer_start = (int)phystokv(xfer_start);
+ ((int *)buf)[0] = ((int *)xfer_start)[0];
+ ((int *)buf)[1] = ((int *)xfer_start)[1];
+ ((int *)buf)[2] = ((int *)xfer_start)[2];
+ ((short *)(buf + 12))[0] = ((short *)(xfer_start + 12))[0];
+ return;
+ }
+ if (xfer_start + count > wd->sc_rmend) {
+ int semi_count = wd->sc_rmend - xfer_start;
+
+ /*
+ * Input move must be wrapped.
+ */
+ bcopy((char *)phystokv(xfer_start), buf, semi_count);
+ count -= semi_count;
+ bcopy((char *)phystokv(wd->sc_rmstart),buf+semi_count,count);
+ } else
+ bcopy((char *)phystokv(xfer_start), buf, count);
+ if (ns->sc_word16)
+ outb(port + WD_CMDREG5, wd->sc_reg5);
+}
+
+int
+wd_output(ns, count, buf, start_page)
+ struct nssoftc *ns;
+ int count;
+ char *buf;
+ int start_page;
+{
+ char *shmem;
+ int i, port = ns->sc_port - WD_NIC_OFF;
+ struct wdsoftc *wd = &wdsoftc[ns->sc_unit];
+
+ DEBUGF(printf("wd%d: start_page = %d\n", ns->sc_unit, start_page));
+
+ shmem = (char *)phystokv(wd->sc_mstart+((start_page-WD_START_PG)<<8));
+ if (ns->sc_word16) {
+ outb(port + WD_CMDREG5, ISA16 | wd->sc_reg5);
+ bcopy(buf, shmem, count);
+ outb(port + WD_CMDREG5, wd->sc_reg5);
+ } else
+ bcopy(buf, shmem, count);
+ while (count < ETHERMIN + sizeof(struct ether_header)) {
+ *(shmem + count) = 0;
+ count++;
+ }
+ return (count);
+}
+
+/*
+ * Watchdog.
+ * Check for hung transmissions.
+ */
+void
+wdwatch()
+{
+ int unit, s;
+ struct nssoftc *ns;
+
+ timeout(wdwatch, 0, hz);
+
+ s = splimp();
+ for (unit = 0; unit < NWD; unit++) {
+ if (wdinfo[unit] == 0 || wdinfo[unit]->alive == 0)
+ continue;
+ ns = &wdnssoftc[unit];
+ if (ns->sc_timer && --ns->sc_timer == 0) {
+ printf("wd%d: transmission timeout\n", unit);
+ nsinit(ns);
+ }
+ }
+ splx(s);
+}
+
+#endif /* NWD > 0 */
diff --git a/i386/i386at/gpl/linux/block/cmd640.c b/i386/i386at/gpl/linux/block/cmd640.c
new file mode 100644
index 00000000..99a139dc
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/cmd640.c
@@ -0,0 +1,738 @@
+/*
+ * linux/drivers/block/cmd640.c Version 0.07 Jan 27, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: abramov@cecmow.enet.dec.com (Igor Abramov)
+ *
+ * This file provides support for the advanced features and bugs
+ * of IDE interfaces using the CMD Technologies 0640 IDE interface chip.
+ *
+ * Version 0.01 Initial version, hacked out of ide.c,
+ * and #include'd rather than compiled separately.
+ * This will get cleaned up in a subsequent release.
+ *
+ * Version 0.02 Fixes for vlb initialization code, enable
+ * read-ahead for versions 'B' and 'C' of chip by
+ * default, some code cleanup.
+ *
+ * Version 0.03 Added reset of secondary interface,
+ * and black list for devices which are not compatible
+ * with read ahead mode. Separate function for setting
+ * readahead is added, possibly it will be called some
+ * day from ioctl processing code.
+ *
+ * Version 0.04 Now configs/compiles separate from ide.c -ml
+ *
+ * Version 0.05 Major rewrite of interface timing code.
+ * Added new function cmd640_set_mode to set PIO mode
+ * from ioctl call. New drives added to black list.
+ *
+ * Version 0.06 More code cleanup. Readahead is enabled only for
+ * detected hard drives, not included in readahed
+ * black list.
+ *
+ * Version 0.07 Changed to more conservative drive tuning policy.
+ * Unknown drives, which report PIO < 4 are set to
+ * (reported_PIO - 1) if it is supported, or to PIO0.
+ * List of known drives extended by info provided by
+ * CMD at their ftp site.
+ *
+ * Version 0.08 Added autotune/noautotune support. -ml
+ *
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include "ide.h"
+#include "ide_modes.h"
+
+int cmd640_vlb = 0;
+
+/*
+ * CMD640 specific registers definition.
+ */
+
+#define VID 0x00
+#define DID 0x02
+#define PCMD 0x04
+#define PSTTS 0x06
+#define REVID 0x08
+#define PROGIF 0x09
+#define SUBCL 0x0a
+#define BASCL 0x0b
+#define BaseA0 0x10
+#define BaseA1 0x14
+#define BaseA2 0x18
+#define BaseA3 0x1c
+#define INTLINE 0x3c
+#define INPINE 0x3d
+
+#define CFR 0x50
+#define CFR_DEVREV 0x03
+#define CFR_IDE01INTR 0x04
+#define CFR_DEVID 0x18
+#define CFR_AT_VESA_078h 0x20
+#define CFR_DSA1 0x40
+#define CFR_DSA0 0x80
+
+#define CNTRL 0x51
+#define CNTRL_DIS_RA0 0x40
+#define CNTRL_DIS_RA1 0x80
+#define CNTRL_ENA_2ND 0x08
+
+#define CMDTIM 0x52
+#define ARTTIM0 0x53
+#define DRWTIM0 0x54
+#define ARTTIM1 0x55
+#define DRWTIM1 0x56
+#define ARTTIM23 0x57
+#define DIS_RA2 0x04
+#define DIS_RA3 0x08
+#define DRWTIM23 0x58
+#define BRST 0x59
+
+static ide_tuneproc_t cmd640_tune_drive;
+
+/* Interface to access cmd640x registers */
+static void (*put_cmd640_reg)(int reg_no, int val);
+static byte (*get_cmd640_reg)(int reg_no);
+
+enum { none, vlb, pci1, pci2 };
+static int bus_type = none;
+static int cmd640_chip_version;
+static int cmd640_key;
+static int bus_speed; /* MHz */
+
+/*
+ * For some unknown reasons pcibios functions which read and write registers
+ * do not always work with cmd640. We use direct IO instead.
+ */
+
+/* PCI method 1 access */
+
+static void put_cmd640_reg_pci1(int reg_no, int val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
+ outb_p(val, (reg_no & 3) + 0xcfc);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci1(int reg_no)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outl_p((reg_no & 0xfc) | cmd640_key, 0xcf8);
+ b = inb_p(0xcfc + (reg_no & 3));
+ restore_flags(flags);
+ return b;
+}
+
+/* PCI method 2 access (from CMD datasheet) */
+
+static void put_cmd640_reg_pci2(int reg_no, int val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ outb_p(val, cmd640_key + reg_no);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_pci2(int reg_no)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(0x10, 0xcf8);
+ b = inb_p(cmd640_key + reg_no);
+ outb_p(0, 0xcf8);
+ restore_flags(flags);
+ return b;
+}
+
+/* VLB access */
+
+static void put_cmd640_reg_vlb(int reg_no, int val)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg_no, cmd640_key + 8);
+ outb_p(val, cmd640_key + 0xc);
+ restore_flags(flags);
+}
+
+static byte get_cmd640_reg_vlb(int reg_no)
+{
+ byte b;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ outb_p(reg_no, cmd640_key + 8);
+ b = inb_p(cmd640_key + 0xc);
+ restore_flags(flags);
+ return b;
+}
+
+/*
+ * Probe for CMD640x -- pci method 1
+ */
+
+static int probe_for_cmd640_pci1(void)
+{
+ long id;
+ int k;
+
+ for (k = 0x80000000; k <= 0x8000f800; k += 0x800) {
+ outl(k, 0xcf8);
+ id = inl(0xcfc);
+ if (id != 0x06401095)
+ continue;
+ put_cmd640_reg = put_cmd640_reg_pci1;
+ get_cmd640_reg = get_cmd640_reg_pci1;
+ cmd640_key = k;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- pci method 2
+ */
+
+static int probe_for_cmd640_pci2(void)
+{
+ int i;
+ int v_id;
+ int d_id;
+
+ for (i = 0xc000; i <= 0xcf00; i += 0x100) {
+ outb(0x10, 0xcf8);
+ v_id = inw(i);
+ d_id = inw(i + 2);
+ outb(0, 0xcf8);
+ if (v_id != 0x1095 || d_id != 0x640)
+ continue;
+ put_cmd640_reg = put_cmd640_reg_pci2;
+ get_cmd640_reg = get_cmd640_reg_pci2;
+ cmd640_key = i;
+ return 1;
+ }
+ return 0;
+}
+
+/*
+ * Probe for CMD640x -- vlb
+ */
+
+static int probe_for_cmd640_vlb(void) {
+ byte b;
+
+ outb(CFR, 0x178);
+ b = inb(0x17c);
+ if (b == 0xff || b == 0 || (b & CFR_AT_VESA_078h)) {
+ outb(CFR, 0x78);
+ b = inb(0x7c);
+ if (b == 0xff || b == 0 || !(b & CFR_AT_VESA_078h))
+ return 0;
+ cmd640_key = 0x70;
+ } else {
+ cmd640_key = 0x170;
+ }
+ put_cmd640_reg = put_cmd640_reg_vlb;
+ get_cmd640_reg = get_cmd640_reg_vlb;
+ return 1;
+}
+
+/*
+ * Low level reset for controller, actually it has nothing specific for
+ * CMD640, but I don't know how to use standard reset routine before
+ * we recognized any drives.
+ */
+
+static void cmd640_reset_controller(int iface_no)
+{
+ int retry_count = 600;
+ int base_port = iface_no ? 0x170 : 0x1f0;
+
+ outb_p(4, base_port + 7);
+ udelay(5);
+ outb_p(0, base_port + 7);
+
+ do {
+ udelay(5);
+ retry_count -= 1;
+ } while ((inb_p(base_port + 7) & 0x80) && retry_count);
+
+ if (retry_count == 0)
+ printk("cmd640: failed to reset controller %d\n", iface_no);
+#if 0
+ else
+ printk("cmd640: controller %d reset [%d]\n",
+ iface_no, retry_count);
+#endif
+}
+
+/*
+ * Probe for Cmd640x and initialize it if found
+ */
+
+int ide_probe_for_cmd640x(void)
+{
+ int second_port;
+ byte b;
+
+ if (probe_for_cmd640_pci1()) {
+ bus_type = pci1;
+ } else if (probe_for_cmd640_pci2()) {
+ bus_type = pci2;
+ } else if (cmd640_vlb && probe_for_cmd640_vlb()) {
+ /* May be remove cmd640_vlb at all, and probe in any case */
+ bus_type = vlb;
+ } else {
+ return 0;
+ }
+
+ ide_hwifs[0].serialized = 1; /* ensure this *always* gets set */
+
+#if 0
+ /* Dump initial state of chip registers */
+ for (b = 0; b != 0xff; b++) {
+ printk(" %2x%c", get_cmd640_reg(b),
+ ((b&0xf) == 0xf) ? '\n' : ',');
+ }
+
+#endif
+
+ /*
+ * Undocumented magic. (There is no 0x5b port in specs)
+ */
+
+ put_cmd640_reg(0x5b, 0xbd);
+ if (get_cmd640_reg(0x5b) != 0xbd) {
+ printk("ide: can't initialize cmd640 -- wrong value in 0x5b\n");
+ return 0;
+ }
+ put_cmd640_reg(0x5b, 0);
+
+ /*
+ * Documented magic.
+ */
+
+ cmd640_chip_version = get_cmd640_reg(CFR) & CFR_DEVREV;
+ if (cmd640_chip_version == 0) {
+ printk ("ide: wrong CMD640 version -- 0\n");
+ return 0;
+ }
+
+ /*
+ * Setup the most conservative timings for all drives,
+ */
+ put_cmd640_reg(ARTTIM0, 0xc0);
+ put_cmd640_reg(ARTTIM1, 0xc0);
+ put_cmd640_reg(ARTTIM23, 0xcc); /* 0xc0? */
+
+ /*
+ * Do not initialize secondary controller for vlbus
+ */
+ second_port = (bus_type != vlb);
+
+ /*
+ * Set the maximum allowed bus speed (it is safest until we
+ * find how to detect bus speed)
+ * Normally PCI bus runs at 33MHz, but often works overclocked to 40
+ */
+ bus_speed = (bus_type == vlb) ? 50 : 40;
+
+ /*
+ * Setup Control Register
+ */
+ b = get_cmd640_reg(CNTRL);
+
+ if (second_port)
+ b |= CNTRL_ENA_2ND;
+ else
+ b &= ~CNTRL_ENA_2ND;
+
+ /*
+ * Disable readahead for drives at primary interface
+ */
+ b |= (CNTRL_DIS_RA0 | CNTRL_DIS_RA1);
+
+ put_cmd640_reg(CNTRL, b);
+
+ /*
+ * Note that we assume that the first interface is at 0x1f0,
+ * and that the second interface, if enabled, is at 0x170.
+ */
+ ide_hwifs[0].chipset = ide_cmd640;
+ ide_hwifs[0].tuneproc = &cmd640_tune_drive;
+ if (ide_hwifs[0].drives[0].autotune == 0)
+ ide_hwifs[0].drives[0].autotune = 1;
+ if (ide_hwifs[0].drives[1].autotune == 0)
+ ide_hwifs[0].drives[1].autotune = 1;
+
+ /*
+ * Initialize 2nd IDE port, if required
+ */
+ if (second_port) {
+ ide_hwifs[1].chipset = ide_cmd640;
+ ide_hwifs[1].tuneproc = &cmd640_tune_drive;
+ if (ide_hwifs[1].drives[0].autotune == 0)
+ ide_hwifs[1].drives[0].autotune = 1;
+ if (ide_hwifs[1].drives[1].autotune == 0)
+ ide_hwifs[1].drives[1].autotune = 1;
+ /* We reset timings, and disable read-ahead */
+ put_cmd640_reg(ARTTIM23, (DIS_RA2 | DIS_RA3));
+ put_cmd640_reg(DRWTIM23, 0);
+
+ cmd640_reset_controller(1);
+ }
+
+ printk("ide: buggy CMD640%c interface at ",
+ 'A' - 1 + cmd640_chip_version);
+ switch (bus_type) {
+ case vlb :
+ printk("local bus, port 0x%x", cmd640_key);
+ break;
+ case pci1:
+ printk("pci, (0x%x)", cmd640_key);
+ break;
+ case pci2:
+ printk("pci,(access method 2) (0x%x)", cmd640_key);
+ break;
+ }
+
+ /*
+ * Reset interface timings
+ */
+ put_cmd640_reg(CMDTIM, 0);
+
+ printk("\n ... serialized, secondary interface %s\n",
+ second_port ? "enabled" : "disabled");
+
+ return 1;
+}
+
+int cmd640_off(void) {
+ static int a = 0;
+ byte b;
+
+ if (bus_type == none || a == 1)
+ return 0;
+ a = 1;
+ b = get_cmd640_reg(CNTRL);
+ b &= ~CNTRL_ENA_2ND;
+ put_cmd640_reg(CNTRL, b);
+ return 1;
+}
+
+/*
+ * Sets readahead mode for specific drive
+ * in the future it could be called from ioctl
+ */
+
+static void set_readahead_mode(int mode, int if_num, int dr_num)
+{
+ static int masks[2][2] =
+ {
+ {CNTRL_DIS_RA0, CNTRL_DIS_RA1},
+ {DIS_RA2, DIS_RA3}
+ };
+
+ int port = (if_num == 0) ? CNTRL : ARTTIM23;
+ int mask = masks[if_num][dr_num];
+ byte b;
+
+ b = get_cmd640_reg(port);
+ if (mode)
+ b &= ~mask; /* Enable readahead for specific drive */
+ else
+ b |= mask; /* Disable readahed for specific drive */
+ put_cmd640_reg(port, b);
+}
+
+static struct readahead_black_list {
+ const char* name;
+ int mode;
+} drives_ra[] = {
+ { "ST3655A", 0 },
+ { "SAMSUNG", 0 }, /* Be conservative */
+ { NULL, 0 }
+};
+
+static int strmatch(const char* pattern, const char* name) {
+ char c1, c2;
+
+ while (1) {
+ c1 = *pattern++;
+ c2 = *name++;
+ if (c1 == 0) {
+ return 0;
+ }
+ if (c1 != c2)
+ return 1;
+ }
+}
+
+static int known_drive_readahead(char* name) {
+ int i;
+
+ for (i = 0; drives_ra[i].name != NULL; i++) {
+ if (strmatch(drives_ra[i].name, name) == 0) {
+ return drives_ra[i].mode;
+ }
+ }
+ return -1;
+}
+
+static int arttim[4] = {2, 2, 2, 2}; /* Address setup count (in clocks) */
+static int a_count[4] = {1, 1, 1, 1}; /* Active count (encoded) */
+static int r_count[4] = {1, 1, 1, 1}; /* Recovery count (encoded) */
+
+/*
+ * Convert address setup count from number of clocks
+ * to representation used by controller
+ */
+
+inline static int pack_arttim(int clocks)
+{
+ if (clocks <= 2) return 0x40;
+ else if (clocks == 3) return 0x80;
+ else if (clocks == 4) return 0x00;
+ else return 0xc0;
+}
+
+/*
+ * Pack active and recovery counts into single byte representation
+ * used by controller
+ */
+
+inline static int pack_counts(int act_count, int rec_count)
+{
+ return ((act_count & 0x0f)<<4) | (rec_count & 0x0f);
+}
+
+inline int max(int a, int b) { return a > b ? a : b; }
+inline int max4(int *p) { return max(p[0], max(p[1], max(p[2], p[3]))); }
+
+/*
+ * Set timing parameters
+ */
+
+static void cmd640_set_timing(int if_num, int dr_num)
+{
+ int b_reg;
+ int ac, rc, at;
+
+ /*
+ * Set address setup count and drive read/write timing registers.
+ * Primary interface has individual count/timing registers for
+ * each drive. Secondary interface has common set of registers, and
+ * we should set timings for the slowest drive.
+ */
+
+ if (if_num == 0) {
+ b_reg = dr_num ? ARTTIM1 : ARTTIM0;
+ at = arttim[dr_num];
+ ac = a_count[dr_num];
+ rc = r_count[dr_num];
+ } else {
+ b_reg = ARTTIM23;
+ at = max(arttim[2], arttim[3]);
+ ac = max(a_count[2], a_count[3]);
+ rc = max(r_count[2], r_count[3]);
+ }
+
+ put_cmd640_reg(b_reg, pack_arttim(at));
+ put_cmd640_reg(b_reg + 1, pack_counts(ac, rc));
+
+ /*
+ * Update CMDTIM (IDE Command Block Timing Register)
+ */
+
+ ac = max4(r_count);
+ rc = max4(a_count);
+ put_cmd640_reg(CMDTIM, pack_counts(ac, rc));
+}
+
+/*
+ * Standard timings for PIO modes
+ */
+
+static struct pio_timing {
+ int mc_time; /* Minimal cycle time (ns) */
+ int av_time; /* Address valid to DIOR-/DIOW- setup (ns) */
+ int ds_time; /* DIOR data setup (ns) */
+} pio_timings[6] = {
+ { 70, 165, 600 }, /* PIO Mode 0 */
+ { 50, 125, 383 }, /* PIO Mode 1 */
+ { 30, 100, 240 }, /* PIO Mode 2 */
+ { 30, 80, 180 }, /* PIO Mode 3 */
+ { 25, 70, 125 }, /* PIO Mode 4 -- should be 120, not 125 */
+ { 20, 50, 100 } /* PIO Mode ? (nonstandard) */
+};
+
+static void cmd640_timings_to_clocks(int mc_time, int av_time, int ds_time,
+ int clock_time, int drv_idx)
+{
+ int a, b;
+
+ arttim[drv_idx] = (mc_time + clock_time - 1)/clock_time;
+
+ a = (av_time + clock_time - 1)/clock_time;
+ if (a < 2)
+ a = 2;
+ b = (ds_time + clock_time - 1)/clock_time - a;
+ if (b < 2)
+ b = 2;
+ if (b > 0x11) {
+ a += b - 0x11;
+ b = 0x11;
+ }
+ if (a > 0x10)
+ a = 0x10;
+ if (cmd640_chip_version > 1)
+ b -= 1;
+ if (b > 0x10)
+ b = 0x10;
+
+ a_count[drv_idx] = a;
+ r_count[drv_idx] = b;
+}
+
+static void set_pio_mode(int if_num, int drv_num, int mode_num) {
+ int p_base;
+ int i;
+
+ p_base = if_num ? 0x170 : 0x1f0;
+ outb_p(3, p_base + 1);
+ outb_p(mode_num | 8, p_base + 2);
+ outb_p((drv_num | 0xa) << 4, p_base + 6);
+ outb_p(0xef, p_base + 7);
+ for (i = 0; (i < 100) && (inb (p_base + 7) & 0x80); i++)
+ udelay(10000);
+}
+
+/*
+ * Set a specific pio_mode for a drive
+ */
+
+static void cmd640_set_mode(ide_drive_t* drive, int pio_mode) {
+ int interface_number;
+ int drive_number;
+ int clock_time; /* ns */
+ int mc_time, av_time, ds_time;
+
+ interface_number = HWIF(drive)->index;
+ drive_number = drive->select.b.unit;
+ clock_time = 1000/bus_speed;
+
+ mc_time = pio_timings[pio_mode].mc_time;
+ av_time = pio_timings[pio_mode].av_time;
+ ds_time = pio_timings[pio_mode].ds_time;
+
+ cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
+ interface_number*2 + drive_number);
+ set_pio_mode(interface_number, drive_number, pio_mode);
+ cmd640_set_timing(interface_number, drive_number);
+}
+
+/*
+ * Drive PIO mode "autoconfiguration".
+ * Ideally, this code should *always* call cmd640_set_mode(), but it doesn't.
+ */
+
+static void cmd640_tune_drive(ide_drive_t *drive, byte pio_mode) {
+ int interface_number;
+ int drive_number;
+ int clock_time; /* ns */
+ int max_pio;
+ int mc_time, av_time, ds_time;
+ struct hd_driveid* id;
+ int readahead; /* there is a global named read_ahead */
+
+ if (pio_mode != 255) {
+ cmd640_set_mode(drive, pio_mode);
+ return;
+ }
+
+ interface_number = HWIF(drive)->index;
+ drive_number = drive->select.b.unit;
+ clock_time = 1000/bus_speed;
+ id = drive->id;
+ if ((max_pio = ide_scan_pio_blacklist(id->model)) != -1) {
+ ds_time = pio_timings[max_pio].ds_time;
+ } else {
+ max_pio = id->tPIO;
+ ds_time = pio_timings[max_pio].ds_time;
+ if (id->field_valid & 2) {
+ if ((id->capability & 8) && (id->eide_pio_modes & 7)) {
+ if (id->eide_pio_modes & 4) max_pio = 5;
+ else if (id->eide_pio_modes & 2) max_pio = 4;
+ else max_pio = 3;
+ ds_time = id->eide_pio_iordy;
+ } else {
+ ds_time = id->eide_pio;
+ }
+ if (ds_time == 0)
+ ds_time = pio_timings[max_pio].ds_time;
+ }
+
+ /*
+ * Conservative "downgrade"
+ */
+ if (max_pio < 4 && max_pio != 0) {
+ max_pio -= 1;
+ ds_time = pio_timings[max_pio].ds_time;
+ }
+ }
+ mc_time = pio_timings[max_pio].mc_time;
+ av_time = pio_timings[max_pio].av_time;
+ cmd640_timings_to_clocks(mc_time, av_time, ds_time, clock_time,
+ interface_number*2 + drive_number);
+ set_pio_mode(interface_number, drive_number, max_pio);
+ cmd640_set_timing(interface_number, drive_number);
+
+ /*
+ * Disable (or set) readahead mode
+ */
+
+ readahead = 0;
+ if (cmd640_chip_version > 1) { /* Mmmm.. probably should be > 2 ?? */
+ readahead = known_drive_readahead(id->model);
+ if (readahead == -1)
+ readahead = 1; /* Mmmm.. probably be 0 ?? */
+ set_readahead_mode(readahead, interface_number, drive_number);
+ }
+
+ printk ("Mode and Timing set to PIO%d, Readahead is %s\n",
+ max_pio, readahead ? "enabled" : "disabled");
+}
+
diff --git a/i386/i386at/gpl/linux/block/floppy.c b/i386/i386at/gpl/linux/block/floppy.c
new file mode 100644
index 00000000..ee4a8980
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/floppy.c
@@ -0,0 +1,4100 @@
+/*
+ * linux/kernel/floppy.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1993, 1994 Alain Knaff
+ */
+/*
+ * 02.12.91 - Changed to static variables to indicate need for reset
+ * and recalibrate. This makes some things easier (output_byte reset
+ * checking etc), and means less interrupt jumping in case of errors,
+ * so the code is hopefully easier to understand.
+ */
+
+/*
+ * This file is certainly a mess. I've tried my best to get it working,
+ * but I don't like programming floppies, and I have only one anyway.
+ * Urgel. I should check for more errors, and do more graceful error
+ * recovery. Seems there are problems with several drives. I've tried to
+ * correct them. No promises.
+ */
+
+/*
+ * As with hd.c, all routines within this file can (and will) be called
+ * by interrupts, so extreme caution is needed. A hardware interrupt
+ * handler may not sleep, or a kernel panic will happen. Thus I cannot
+ * call "floppy-on" directly, but have to set a special timer interrupt
+ * etc.
+ */
+
+/*
+ * 28.02.92 - made track-buffering routines, based on the routines written
+ * by entropy@wintermute.wpi.edu (Lawrence Foard). Linus.
+ */
+
+/*
+ * Automatic floppy-detection and formatting written by Werner Almesberger
+ * (almesber@nessie.cs.id.ethz.ch), who also corrected some problems with
+ * the floppy-change signal detection.
+ */
+
+/*
+ * 1992/7/22 -- Hennus Bergman: Added better error reporting, fixed
+ * FDC data overrun bug, added some preliminary stuff for vertical
+ * recording support.
+ *
+ * 1992/9/17: Added DMA allocation & DMA functions. -- hhb.
+ *
+ * TODO: Errors are still not counted properly.
+ */
+
+/* 1992/9/20
+ * Modifications for ``Sector Shifting'' by Rob Hooft (hooft@chem.ruu.nl)
+ * modelled after the freeware MS/DOS program fdformat/88 V1.8 by
+ * Christoph H. Hochst\"atter.
+ * I have fixed the shift values to the ones I always use. Maybe a new
+ * ioctl() should be created to be able to modify them.
+ * There is a bug in the driver that makes it impossible to format a
+ * floppy as the first thing after bootup.
+ */
+
+/*
+ * 1993/4/29 -- Linus -- cleaned up the timer handling in the kernel, and
+ * this helped the floppy driver as well. Much cleaner, and still seems to
+ * work.
+ */
+
+/* 1994/6/24 --bbroad-- added the floppy table entries and made
+ * minor modifications to allow 2.88 floppies to be run.
+ */
+
+/* 1994/7/13 -- Paul Vojta -- modified the probing code to allow three or more
+ * disk types.
+ */
+
+/*
+ * 1994/8/8 -- Alain Knaff -- Switched to fdpatch driver: Support for bigger
+ * format bug fixes, but unfortunately some new bugs too...
+ */
+
+/* 1994/9/17 -- Koen Holtman -- added logging of physical floppy write
+ * errors to allow safe writing by specialized programs.
+ */
+
+/* 1995/4/24 -- Dan Fandrich -- added support for Commodore 1581 3.5" disks
+ * by defining bit 1 of the "stretch" parameter to mean put sectors on the
+ * opposite side of the disk, leaving the sector IDs alone (i.e. Commodore's
+ * drives are "upside-down").
+ */
+
+/*
+ * 1995/8/26 -- Andreas Busse -- added Mips support.
+ */
+
+/*
+ * 1995/10/18 -- Ralf Baechle -- Portability cleanup; move machine dependend
+ * features to asm/floppy.h.
+ */
+
+
+#define FLOPPY_SANITY_CHECK
+#undef FLOPPY_SILENT_DCL_CLEAR
+
+#define REALLY_SLOW_IO
+
+#define DEBUGT 2
+#define DCL_DEBUG /* debug disk change line */
+
+/* do print messages for unexpected interrupts */
+static int print_unex=1;
+#include <linux/utsname.h>
+#include <linux/module.h>
+
+/* the following is the mask of allowed drives. By default units 2 and
+ * 3 of both floppy controllers are disabled, because switching on the
+ * motor of these drives causes system hangs on some PCI computers. drive
+ * 0 is the low bit (0x1), and drive 7 is the high bit (0x80). Bits are on if
+ * a drive is allowed. */
+static int FLOPPY_IRQ=6;
+static int FLOPPY_DMA=2;
+static int allowed_drive_mask = 0x33;
+
+
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/tqueue.h>
+#define FDPATCHES
+#include <linux/fdreg.h>
+
+
+#include <linux/fd.h>
+
+
+#define OLDFDRAWCMD 0x020d /* send a raw command to the fdc */
+
+struct old_floppy_raw_cmd {
+ void *data;
+ long length;
+
+ unsigned char rate;
+ unsigned char flags;
+ unsigned char cmd_count;
+ unsigned char cmd[9];
+ unsigned char reply_count;
+ unsigned char reply[7];
+ int track;
+};
+
+#include <linux/errno.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/fcntl.h>
+#include <linux/delay.h>
+#include <linux/mc146818rtc.h> /* CMOS defines */
+#include <linux/ioport.h>
+
+#include <asm/dma.h>
+#include <asm/floppy.h>
+#include <asm/irq.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/segment.h>
+
+#define MAJOR_NR FLOPPY_MAJOR
+
+#include <linux/blk.h>
+
+
+/* Dma Memory related stuff */
+
+/* Pure 2^n version of get_order */
+static inline int __get_order (int size)
+{
+ int order;
+
+#ifdef _ASM_IO_H2
+ __asm__ __volatile__("bsr %1,%0"
+ : "=r" (order)
+ : "r" (size / PAGE_SIZE));
+#else
+ for (order = 0; order < NR_MEM_LISTS; ++order)
+ if (size <= (PAGE_SIZE << order))
+ return order;
+#endif
+ return NR_MEM_LISTS;
+}
+
+static unsigned long dma_mem_alloc(int size)
+{
+ int order = __get_order(size);
+
+ if (order >= NR_MEM_LISTS)
+ return(0);
+ return __get_dma_pages(GFP_KERNEL,order);
+}
+
+/* End dma memory related stuff */
+
+static unsigned int fake_change = 0;
+static int initialising=1;
+
+static inline int TYPE(kdev_t x) {
+ return (MINOR(x)>>2) & 0x1f;
+}
+static inline int DRIVE(kdev_t x) {
+ return (MINOR(x)&0x03) | ((MINOR(x)&0x80) >> 5);
+}
+#define ITYPE(x) (((x)>>2) & 0x1f)
+#define TOMINOR(x) ((x & 3) | ((x & 4) << 5))
+#define UNIT(x) ((x) & 0x03) /* drive on fdc */
+#define FDC(x) (((x) & 0x04) >> 2) /* fdc of drive */
+#define REVDRIVE(fdc, unit) ((unit) + ((fdc) << 2))
+ /* reverse mapping from unit and fdc to drive */
+#define DP (&drive_params[current_drive])
+#define DRS (&drive_state[current_drive])
+#define DRWE (&write_errors[current_drive])
+#define FDCS (&fdc_state[fdc])
+#define CLEARF(x) (clear_bit(x##_BIT, &DRS->flags))
+#define SETF(x) (set_bit(x##_BIT, &DRS->flags))
+#define TESTF(x) (test_bit(x##_BIT, &DRS->flags))
+
+#define UDP (&drive_params[drive])
+#define UDRS (&drive_state[drive])
+#define UDRWE (&write_errors[drive])
+#define UFDCS (&fdc_state[FDC(drive)])
+#define UCLEARF(x) (clear_bit(x##_BIT, &UDRS->flags))
+#define USETF(x) (set_bit(x##_BIT, &UDRS->flags))
+#define UTESTF(x) (test_bit(x##_BIT, &UDRS->flags))
+
+#define DPRINT(x) printk(DEVICE_NAME "%d: " x,current_drive)
+
+#define DPRINT1(x,x1) printk(DEVICE_NAME "%d: " x,current_drive,(x1))
+
+#define DPRINT2(x,x1,x2) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2))
+
+#define DPRINT3(x,x1,x2,x3) printk(DEVICE_NAME "%d: " x,current_drive,(x1),(x2),(x3))
+
+#define PH_HEAD(floppy,head) (((((floppy)->stretch & 2) >>1) ^ head) << 2)
+#define STRETCH(floppy) ((floppy)->stretch & FD_STRETCH)
+
+#define CLEARSTRUCT(x) memset((x), 0, sizeof(*(x)))
+
+/* read/write */
+#define COMMAND raw_cmd->cmd[0]
+#define DR_SELECT raw_cmd->cmd[1]
+#define TRACK raw_cmd->cmd[2]
+#define HEAD raw_cmd->cmd[3]
+#define SECTOR raw_cmd->cmd[4]
+#define SIZECODE raw_cmd->cmd[5]
+#define SECT_PER_TRACK raw_cmd->cmd[6]
+#define GAP raw_cmd->cmd[7]
+#define SIZECODE2 raw_cmd->cmd[8]
+#define NR_RW 9
+
+/* format */
+#define F_SIZECODE raw_cmd->cmd[2]
+#define F_SECT_PER_TRACK raw_cmd->cmd[3]
+#define F_GAP raw_cmd->cmd[4]
+#define F_FILL raw_cmd->cmd[5]
+#define NR_F 6
+
+/*
+ * Maximum disk size (in kilobytes). This default is used whenever the
+ * current disk size is unknown.
+ * [Now it is rather a minimum]
+ */
+#define MAX_DISK_SIZE 2 /* 3984*/
+
+#define K_64 0x10000 /* 64KB */
+
+/*
+ * globals used by 'result()'
+ */
+#define MAX_REPLIES 17
+static unsigned char reply_buffer[MAX_REPLIES];
+static int inr; /* size of reply buffer, when called from interrupt */
+#define ST0 (reply_buffer[0])
+#define ST1 (reply_buffer[1])
+#define ST2 (reply_buffer[2])
+#define ST3 (reply_buffer[0]) /* result of GETSTATUS */
+#define R_TRACK (reply_buffer[3])
+#define R_HEAD (reply_buffer[4])
+#define R_SECTOR (reply_buffer[5])
+#define R_SIZECODE (reply_buffer[6])
+
+#define SEL_DLY (2*HZ/100)
+
+#define ARRAY_SIZE(x) (sizeof(x) / sizeof((x)[0]))
+/*
+ * this struct defines the different floppy drive types.
+ */
+static struct {
+ struct floppy_drive_params params;
+ const char *name; /* name printed while booting */
+} default_drive_params[]= {
+/* NOTE: the time values in jiffies should be in msec!
+ CMOS drive type
+ | Maximum data rate supported by drive type
+ | | Head load time, msec
+ | | | Head unload time, msec (not used)
+ | | | | Step rate interval, usec
+ | | | | | Time needed for spinup time (jiffies)
+ | | | | | | Timeout for spinning down (jiffies)
+ | | | | | | | Spindown offset (where disk stops)
+ | | | | | | | | Select delay
+ | | | | | | | | | RPS
+ | | | | | | | | | | Max number of tracks
+ | | | | | | | | | | | Interrupt timeout
+ | | | | | | | | | | | | Max nonintlv. sectors
+ | | | | | | | | | | | | | -Max Errors- flags */
+{{0, 500, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 80, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4, 8, 2, 1, 5, 3,10}, 3*HZ/2, 0 }, "unknown" },
+
+{{1, 300, 16, 16, 8000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 40, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 1, 0, 0, 0, 0, 0, 0, 0}, 3*HZ/2, 1 }, "360K PC" }, /*5 1/4 360 KB PC*/
+
+{{2, 500, 16, 16, 6000, 4*HZ/10, 3*HZ, 14, SEL_DLY, 6, 83, 3*HZ, 17, {3,1,2,0,2}, 0,
+ 0, { 2, 5, 6,23,10,20,11, 0}, 3*HZ/2, 2 }, "1.2M" }, /*5 1/4 HD AT*/
+
+{{3, 250, 16, 16, 3000, 1*HZ, 3*HZ, 0, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 4,22,21,30, 3, 0, 0, 0}, 3*HZ/2, 4 }, "720k" }, /*3 1/2 DD*/
+
+{{4, 500, 16, 16, 4000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 20, {3,1,2,0,2}, 0,
+ 0, { 7, 4,25,22,31,21,29,11}, 3*HZ/2, 7 }, "1.44M" }, /*3 1/2 HD*/
+
+{{5, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M AMI BIOS" }, /*3 1/2 ED*/
+
+{{6, 1000, 15, 8, 3000, 4*HZ/10, 3*HZ, 10, SEL_DLY, 5, 83, 3*HZ, 40, {3,1,2,0,2}, 0,
+ 0, { 7, 8, 4,25,28,22,31,21}, 3*HZ/2, 8 }, "2.88M" } /*3 1/2 ED*/
+/* | --autodetected formats--- | | |
+ * read_track | | Name printed when booting
+ * | Native format
+ * Frequency of disk change checks */
+};
+
+static struct floppy_drive_params drive_params[N_DRIVE];
+static struct floppy_drive_struct drive_state[N_DRIVE];
+static struct floppy_write_errors write_errors[N_DRIVE];
+static struct floppy_raw_cmd *raw_cmd, default_raw_cmd;
+
+/*
+ * This struct defines the different floppy types.
+ *
+ * Bit 0 of 'stretch' tells if the tracks need to be doubled for some
+ * types (e.g. 360kB diskette in 1.2MB drive, etc.). Bit 1 of 'stretch'
+ * tells if the disk is in Commodore 1581 format, which means side 0 sectors
+ * are located on side 1 of the disk but with a side 0 ID, and vice-versa.
+ * This is the same as the Sharp MZ-80 5.25" CP/M disk format, except that the
+ * 1581's logical side 0 is on physical side 1, whereas the Sharp's logical
+ * side 0 is on physical side 0 (but with the misnamed sector IDs).
+ * 'stretch' should probably be renamed to something more general, like
+ * 'options'. Other parameters should be self-explanatory (see also
+ * setfdprm(8)).
+ */
+static struct floppy_struct floppy_type[32] = {
+ { 0, 0,0, 0,0,0x00,0x00,0x00,0x00,NULL }, /* 0 no testing */
+ { 720, 9,2,40,0,0x2A,0x02,0xDF,0x50,"d360" }, /* 1 360KB PC */
+ { 2400,15,2,80,0,0x1B,0x00,0xDF,0x54,"h1200" }, /* 2 1.2MB AT */
+ { 720, 9,1,80,0,0x2A,0x02,0xDF,0x50,"D360" }, /* 3 360KB SS 3.5" */
+ { 1440, 9,2,80,0,0x2A,0x02,0xDF,0x50,"D720" }, /* 4 720KB 3.5" */
+ { 720, 9,2,40,1,0x23,0x01,0xDF,0x50,"h360" }, /* 5 360KB AT */
+ { 1440, 9,2,80,0,0x23,0x01,0xDF,0x50,"h720" }, /* 6 720KB AT */
+ { 2880,18,2,80,0,0x1B,0x00,0xCF,0x6C,"H1440" }, /* 7 1.44MB 3.5" */
+ { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"E2880" }, /* 8 2.88MB 3.5" */
+ { 5760,36,2,80,0,0x1B,0x43,0xAF,0x54,"CompaQ"}, /* 9 2.88MB 3.5" */
+
+ { 2880,18,2,80,0,0x25,0x00,0xDF,0x02,"h1440" }, /* 10 1.44MB 5.25" */
+ { 3360,21,2,80,0,0x1C,0x00,0xCF,0x0C,"H1680" }, /* 11 1.68MB 3.5" */
+ { 820,10,2,41,1,0x25,0x01,0xDF,0x2E,"h410" }, /* 12 410KB 5.25" */
+ { 1640,10,2,82,0,0x25,0x02,0xDF,0x2E,"H820" }, /* 13 820KB 3.5" */
+ { 2952,18,2,82,0,0x25,0x00,0xDF,0x02,"h1476" }, /* 14 1.48MB 5.25" */
+ { 3444,21,2,82,0,0x25,0x00,0xDF,0x0C,"H1722" }, /* 15 1.72MB 3.5" */
+ { 840,10,2,42,1,0x25,0x01,0xDF,0x2E,"h420" }, /* 16 420KB 5.25" */
+ { 1660,10,2,83,0,0x25,0x02,0xDF,0x2E,"H830" }, /* 17 830KB 3.5" */
+ { 2988,18,2,83,0,0x25,0x00,0xDF,0x02,"h1494" }, /* 18 1.49MB 5.25" */
+ { 3486,21,2,83,0,0x25,0x00,0xDF,0x0C,"H1743" }, /* 19 1.74 MB 3.5" */
+
+ { 1760,11,2,80,0,0x1C,0x09,0xCF,0x00,"h880" }, /* 20 880KB 5.25" */
+ { 2080,13,2,80,0,0x1C,0x01,0xCF,0x00,"D1040" }, /* 21 1.04MB 3.5" */
+ { 2240,14,2,80,0,0x1C,0x19,0xCF,0x00,"D1120" }, /* 22 1.12MB 3.5" */
+ { 3200,20,2,80,0,0x1C,0x20,0xCF,0x2C,"h1600" }, /* 23 1.6MB 5.25" */
+ { 3520,22,2,80,0,0x1C,0x08,0xCF,0x2e,"H1760" }, /* 24 1.76MB 3.5" */
+ { 3840,24,2,80,0,0x1C,0x20,0xCF,0x00,"H1920" }, /* 25 1.92MB 3.5" */
+ { 6400,40,2,80,0,0x25,0x5B,0xCF,0x00,"E3200" }, /* 26 3.20MB 3.5" */
+ { 7040,44,2,80,0,0x25,0x5B,0xCF,0x00,"E3520" }, /* 27 3.52MB 3.5" */
+ { 7680,48,2,80,0,0x25,0x63,0xCF,0x00,"E3840" }, /* 28 3.84MB 3.5" */
+
+ { 3680,23,2,80,0,0x1C,0x10,0xCF,0x00,"H1840" }, /* 29 1.84MB 3.5" */
+ { 1600,10,2,80,0,0x25,0x02,0xDF,0x2E,"D800" }, /* 30 800KB 3.5" */
+ { 3200,20,2,80,0,0x1C,0x00,0xCF,0x2C,"H1600" }, /* 31 1.6MB 3.5" */
+};
+
+#define NUMBER(x) (sizeof(x) / sizeof(*(x)))
+#define SECTSIZE (_FD_SECTSIZE(*floppy))
+
+/* Auto-detection: Disk type used until the next media change occurs. */
+static struct floppy_struct *current_type[N_DRIVE] = {
+ NULL, NULL, NULL, NULL,
+ NULL, NULL, NULL, NULL
+};
+
+/*
+ * User-provided type information. current_type points to
+ * the respective entry of this array.
+ */
+static struct floppy_struct user_params[N_DRIVE];
+
+static int floppy_sizes[256];
+static int floppy_blocksizes[256] = { 0, };
+
+/*
+ * The driver is trying to determine the correct media format
+ * while probing is set. rw_interrupt() clears it after a
+ * successful access.
+ */
+static int probing = 0;
+
+/* Synchronization of FDC access. */
+#define FD_COMMAND_NONE -1
+#define FD_COMMAND_ERROR 2
+#define FD_COMMAND_OKAY 3
+
+static volatile int command_status = FD_COMMAND_NONE, fdc_busy = 0;
+static struct wait_queue *fdc_wait = NULL, *command_done = NULL;
+#ifdef MACH
+extern int issig (void);
+#define NO_SIGNAL (! issig () || ! interruptible)
+#else
+#define NO_SIGNAL (!(current->signal & ~current->blocked) || !interruptible)
+#endif
+#define CALL(x) if ((x) == -EINTR) return -EINTR
+#define ECALL(x) if ((ret = (x))) return ret;
+#define _WAIT(x,i) CALL(ret=wait_til_done((x),i))
+#define WAIT(x) _WAIT((x),interruptible)
+#define IWAIT(x) _WAIT((x),1)
+
+/* Errors during formatting are counted here. */
+static int format_errors;
+
+/* Format request descriptor. */
+static struct format_descr format_req;
+
+/*
+ * Rate is 0 for 500kb/s, 1 for 300kbps, 2 for 250kbps
+ * Spec1 is 0xSH, where S is stepping rate (F=1ms, E=2ms, D=3ms etc),
+ * H is head unload time (1=16ms, 2=32ms, etc)
+ */
+
+/*
+ * Track buffer
+ * Because these are written to by the DMA controller, they must
+ * not contain a 64k byte boundary crossing, or data will be
+ * corrupted/lost. Alignment of these is enforced in boot/head.S.
+ * Note that you must not change the sizes below without updating head.S.
+ */
+static char *floppy_track_buffer=0;
+static int max_buffer_sectors=0;
+
+static int *errors;
+typedef void (*done_f)(int);
+static struct cont_t {
+ void (*interrupt)(void); /* this is called after the interrupt of the
+ * main command */
+ void (*redo)(void); /* this is called to retry the operation */
+ void (*error)(void); /* this is called to tally an error */
+ done_f done; /* this is called to say if the operation has
+ * succeeded/failed */
+} *cont=NULL;
+
+static void floppy_ready(void);
+static void floppy_start(void);
+static void process_fd_request(void);
+static void recalibrate_floppy(void);
+static void floppy_shutdown(void);
+
+static int floppy_grab_irq_and_dma(void);
+static void floppy_release_irq_and_dma(void);
+
+/*
+ * The "reset" variable should be tested whenever an interrupt is scheduled,
+ * after the commands have been sent. This is to ensure that the driver doesn't
+ * get wedged when the interrupt doesn't come because of a failed command.
+ * reset doesn't need to be tested before sending commands, because
+ * output_byte is automatically disabled when reset is set.
+ */
+#define CHECK_RESET { if (FDCS->reset){ reset_fdc(); return; } }
+static void reset_fdc(void);
+
+/*
+ * These are global variables, as that's the easiest way to give
+ * information to interrupts. They are the data used for the current
+ * request.
+ */
+#define NO_TRACK -1
+#define NEED_1_RECAL -2
+#define NEED_2_RECAL -3
+
+/* */
+static int usage_count = 0;
+
+
+/* buffer related variables */
+static int buffer_track = -1;
+static int buffer_drive = -1;
+static int buffer_min = -1;
+static int buffer_max = -1;
+
+/* fdc related variables, should end up in a struct */
+static struct floppy_fdc_state fdc_state[N_FDC];
+static int fdc; /* current fdc */
+
+static struct floppy_struct * floppy = floppy_type;
+static unsigned char current_drive = 0;
+static long current_count_sectors = 0;
+static unsigned char sector_t; /* sector in track */
+
+#ifdef DEBUGT
+static long unsigned debugtimer;
+#endif
+
+/*
+ * Debugging
+ * =========
+ */
+static inline void set_debugt(void)
+{
+#ifdef DEBUGT
+ debugtimer = jiffies;
+#endif
+}
+
+static inline void debugt(const char *message)
+{
+#ifdef DEBUGT
+ if (DP->flags & DEBUGT)
+ printk("%s dtime=%lu\n", message, jiffies-debugtimer);
+#endif
+}
+
+typedef void (*timeout_fn)(unsigned long);
+static struct timer_list fd_timeout ={ NULL, NULL, 0, 0,
+ (timeout_fn) floppy_shutdown };
+
+static const char *timeout_message;
+
+#ifdef FLOPPY_SANITY_CHECK
+static void is_alive(const char *message)
+{
+ /* this routine checks whether the floppy driver is "alive" */
+ if (fdc_busy && command_status < 2 && !fd_timeout.prev){
+ DPRINT1("timeout handler died: %s\n",message);
+ }
+}
+#endif
+
+#ifdef FLOPPY_SANITY_CHECK
+
+#define OLOGSIZE 20
+
+static void (*lasthandler)(void) = NULL;
+static int interruptjiffies=0;
+static int resultjiffies=0;
+static int resultsize=0;
+static int lastredo=0;
+
+static struct output_log {
+ unsigned char data;
+ unsigned char status;
+ unsigned long jiffies;
+} output_log[OLOGSIZE];
+
+static int output_log_pos=0;
+#endif
+
+#define CURRENTD -1
+#define MAXTIMEOUT -2
+
+static void reschedule_timeout(int drive, const char *message, int marg)
+{
+ if (drive == CURRENTD)
+ drive = current_drive;
+ del_timer(&fd_timeout);
+ if (drive < 0 || drive > N_DRIVE) {
+ fd_timeout.expires = jiffies + 20*HZ;
+ drive=0;
+ } else
+ fd_timeout.expires = jiffies + UDP->timeout;
+ add_timer(&fd_timeout);
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("reschedule timeout ");
+ printk(message, marg);
+ printk("\n");
+ }
+ timeout_message = message;
+}
+
+static int maximum(int a, int b)
+{
+ if(a > b)
+ return a;
+ else
+ return b;
+}
+#define INFBOUND(a,b) (a)=maximum((a),(b));
+
+static int minimum(int a, int b)
+{
+ if(a < b)
+ return a;
+ else
+ return b;
+}
+#define SUPBOUND(a,b) (a)=minimum((a),(b));
+
+
+/*
+ * Bottom half floppy driver.
+ * ==========================
+ *
+ * This part of the file contains the code talking directly to the hardware,
+ * and also the main service loop (seek-configure-spinup-command)
+ */
+
+/*
+ * disk change.
+ * This routine is responsible for maintaining the FD_DISK_CHANGE flag,
+ * and the last_checked date.
+ *
+ * last_checked is the date of the last check which showed 'no disk change'
+ * FD_DISK_CHANGE is set under two conditions:
+ * 1. The floppy has been changed after some i/o to that floppy already
+ * took place.
+ * 2. No floppy disk is in the drive. This is done in order to ensure that
+ * requests are quickly flushed in case there is no disk in the drive. It
+ * follows that FD_DISK_CHANGE can only be cleared if there is a disk in
+ * the drive.
+ *
+ * For 1., maxblock is observed. Maxblock is 0 if no i/o has taken place yet.
+ * For 2., FD_DISK_NEWCHANGE is watched. FD_DISK_NEWCHANGE is cleared on
+ * each seek. If a disk is present, the disk change line should also be
+ * cleared on each seek. Thus, if FD_DISK_NEWCHANGE is clear, but the disk
+ * change line is set, this means either that no disk is in the drive, or
+ * that it has been removed since the last seek.
+ *
+ * This means that we really have a third possibility too:
+ * The floppy has been changed after the last seek.
+ */
+
+static int disk_change(int drive)
+{
+ int fdc=FDC(drive);
+#ifdef FLOPPY_SANITY_CHECK
+ if (jiffies < UDP->select_delay + UDRS->select_date)
+ DPRINT("WARNING disk change called early\n");
+ if (!(FDCS->dor & (0x10 << UNIT(drive))) ||
+ (FDCS->dor & 3) != UNIT(drive) ||
+ fdc != FDC(drive)){
+ DPRINT("probing disk change on unselected drive\n");
+ DPRINT3("drive=%d fdc=%d dor=%x\n",drive, FDC(drive),
+ FDCS->dor);
+ }
+#endif
+
+#ifdef DCL_DEBUG
+ if (UDP->flags & FD_DEBUG){
+ DPRINT1("checking disk change line for drive %d\n",drive);
+ DPRINT1("jiffies=%ld\n", jiffies);
+ DPRINT1("disk change line=%x\n",fd_inb(FD_DIR)&0x80);
+ DPRINT1("flags=%x\n",UDRS->flags);
+ }
+#endif
+ if (UDP->flags & FD_BROKEN_DCL)
+ return UTESTF(FD_DISK_CHANGED);
+ if ((fd_inb(FD_DIR) ^ UDP->flags) & 0x80){
+ USETF(FD_VERIFY); /* verify write protection */
+ if (UDRS->maxblock){
+ /* mark it changed */
+ USETF(FD_DISK_CHANGED);
+
+ /* invalidate its geometry */
+ if (UDRS->keep_data >= 0) {
+ if ((UDP->flags & FTD_MSG) &&
+ current_type[drive] != NULL)
+ DPRINT("Disk type is undefined after "
+ "disk change\n");
+ current_type[drive] = NULL;
+ floppy_sizes[TOMINOR(current_drive)] = MAX_DISK_SIZE;
+ }
+ }
+ /*USETF(FD_DISK_NEWCHANGE);*/
+ return 1;
+ } else {
+ UDRS->last_checked=jiffies;
+ UCLEARF(FD_DISK_NEWCHANGE);
+ }
+ return 0;
+}
+
+static inline int is_selected(int dor, int unit)
+{
+ return ((dor & (0x10 << unit)) && (dor &3) == unit);
+}
+
+static int set_dor(int fdc, char mask, char data)
+{
+ register unsigned char drive, unit, newdor,olddor;
+
+ if (FDCS->address == -1)
+ return -1;
+
+ olddor = FDCS->dor;
+ newdor = (olddor & mask) | data;
+ if (newdor != olddor){
+ unit = olddor & 0x3;
+ if (is_selected(olddor, unit) && !is_selected(newdor,unit)){
+ drive = REVDRIVE(fdc,unit);
+#ifdef DCL_DEBUG
+ if (UDP->flags & FD_DEBUG){
+ DPRINT("calling disk change from set_dor\n");
+ }
+#endif
+ disk_change(drive);
+ }
+ FDCS->dor = newdor;
+ fd_outb(newdor, FD_DOR);
+
+ unit = newdor & 0x3;
+ if (!is_selected(olddor, unit) && is_selected(newdor,unit)){
+ drive = REVDRIVE(fdc,unit);
+ UDRS->select_date = jiffies;
+ }
+ }
+ if (newdor & 0xf0)
+ floppy_grab_irq_and_dma();
+ if (olddor & 0xf0)
+ floppy_release_irq_and_dma();
+ return olddor;
+}
+
+static void twaddle(void)
+{
+ if (DP->select_delay)
+ return;
+ fd_outb(FDCS->dor & ~(0x10<<UNIT(current_drive)),FD_DOR);
+ fd_outb(FDCS->dor, FD_DOR);
+ DRS->select_date = jiffies;
+}
+
+/* reset all driver information about the current fdc. This is needed after
+ * a reset, and after a raw command. */
+static void reset_fdc_info(int mode)
+{
+ int drive;
+
+ FDCS->spec1 = FDCS->spec2 = -1;
+ FDCS->need_configure = 1;
+ FDCS->perp_mode = 1;
+ FDCS->rawcmd = 0;
+ for (drive = 0; drive < N_DRIVE; drive++)
+ if (FDC(drive) == fdc &&
+ (mode || UDRS->track != NEED_1_RECAL))
+ UDRS->track = NEED_2_RECAL;
+}
+
+/* selects the fdc and drive, and enables the fdc's input/dma. */
+static void set_fdc(int drive)
+{
+ if (drive >= 0 && drive < N_DRIVE){
+ fdc = FDC(drive);
+ current_drive = drive;
+ }
+ if (fdc != 1 && fdc != 0) {
+ printk("bad fdc value\n");
+ return;
+ }
+ set_dor(fdc,~0,8);
+ set_dor(1-fdc, ~8, 0);
+ if (FDCS->rawcmd == 2)
+ reset_fdc_info(1);
+ if (fd_inb(FD_STATUS) != STATUS_READY)
+ FDCS->reset = 1;
+}
+
+/* locks the driver */
+static int lock_fdc(int drive, int interruptible)
+{
+ if (!usage_count){
+ printk("trying to lock fdc while usage count=0\n");
+ return -1;
+ }
+ floppy_grab_irq_and_dma();
+ cli();
+ while (fdc_busy && NO_SIGNAL)
+ interruptible_sleep_on(&fdc_wait);
+ if (fdc_busy){
+ sti();
+ return -EINTR;
+ }
+ fdc_busy = 1;
+ sti();
+ command_status = FD_COMMAND_NONE;
+ reschedule_timeout(drive, "lock fdc", 0);
+ set_fdc(drive);
+ return 0;
+}
+
+#define LOCK_FDC(drive,interruptible) \
+if (lock_fdc(drive,interruptible)) return -EINTR;
+
+
+/* unlocks the driver */
+static inline void unlock_fdc(void)
+{
+ raw_cmd = 0;
+ if (!fdc_busy)
+ DPRINT("FDC access conflict!\n");
+
+ if (DEVICE_INTR)
+ DPRINT1("device interrupt still active at FDC release: %p!\n",
+ DEVICE_INTR);
+ command_status = FD_COMMAND_NONE;
+ del_timer(&fd_timeout);
+ cont = NULL;
+ fdc_busy = 0;
+ floppy_release_irq_and_dma();
+ wake_up(&fdc_wait);
+}
+
+/* switches the motor off after a given timeout */
+static void motor_off_callback(unsigned long nr)
+{
+ unsigned char mask = ~(0x10 << UNIT(nr));
+
+ set_dor(FDC(nr), mask, 0);
+}
+
+static struct timer_list motor_off_timer[N_DRIVE] = {
+ { NULL, NULL, 0, 0, motor_off_callback },
+ { NULL, NULL, 0, 1, motor_off_callback },
+ { NULL, NULL, 0, 2, motor_off_callback },
+ { NULL, NULL, 0, 3, motor_off_callback },
+ { NULL, NULL, 0, 4, motor_off_callback },
+ { NULL, NULL, 0, 5, motor_off_callback },
+ { NULL, NULL, 0, 6, motor_off_callback },
+ { NULL, NULL, 0, 7, motor_off_callback }
+};
+
+/* schedules motor off */
+static void floppy_off(unsigned int drive)
+{
+ unsigned long volatile delta;
+ register int fdc=FDC(drive);
+
+ if (!(FDCS->dor & (0x10 << UNIT(drive))))
+ return;
+
+ del_timer(motor_off_timer+drive);
+
+ /* make spindle stop in a position which minimizes spinup time
+ * next time */
+ if (UDP->rps){
+ delta = jiffies - UDRS->first_read_date + HZ -
+ UDP->spindown_offset;
+ delta = ((delta * UDP->rps) % HZ) / UDP->rps;
+ motor_off_timer[drive].expires = jiffies + UDP->spindown - delta;
+ }
+ add_timer(motor_off_timer+drive);
+}
+
+/*
+ * cycle through all N_DRIVE floppy drives, for disk change testing.
+ * stopping at current drive. This is done before any long operation, to
+ * be sure to have up to date disk change information.
+ */
+static void scandrives(void)
+{
+ int i, drive, saved_drive;
+
+ if (DP->select_delay)
+ return;
+
+ saved_drive = current_drive;
+ for (i=0; i < N_DRIVE; i++){
+ drive = (saved_drive + i + 1) % N_DRIVE;
+ if (UDRS->fd_ref == 0 || UDP->select_delay != 0)
+ continue; /* skip closed drives */
+ set_fdc(drive);
+ if (!(set_dor(fdc, ~3, UNIT(drive) | (0x10 << UNIT(drive))) &
+ (0x10 << UNIT(drive))))
+ /* switch the motor off again, if it was off to
+ * begin with */
+ set_dor(fdc, ~(0x10 << UNIT(drive)), 0);
+ }
+ set_fdc(saved_drive);
+}
+
+static struct timer_list fd_timer ={ NULL, NULL, 0, 0, 0 };
+
+/* this function makes sure that the disk stays in the drive during the
+ * transfer */
+static void fd_watchdog(void)
+{
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from watchdog\n");
+ }
+#endif
+
+ if (disk_change(current_drive)){
+ DPRINT("disk removed during i/o\n");
+ floppy_shutdown();
+ } else {
+ del_timer(&fd_timer);
+ fd_timer.function = (timeout_fn) fd_watchdog;
+ fd_timer.expires = jiffies + HZ / 10;
+ add_timer(&fd_timer);
+ }
+}
+
+static void main_command_interrupt(void)
+{
+ del_timer(&fd_timer);
+ cont->interrupt();
+}
+
+/* waits for a delay (spinup or select) to pass */
+static int wait_for_completion(int delay, timeout_fn function)
+{
+ if (FDCS->reset){
+ reset_fdc(); /* do the reset during sleep to win time
+ * if we don't need to sleep, it's a good
+ * occasion anyways */
+ return 1;
+ }
+
+ if (jiffies < delay){
+ del_timer(&fd_timer);
+ fd_timer.function = function;
+ fd_timer.expires = delay;
+ add_timer(&fd_timer);
+ return 1;
+ }
+ return 0;
+}
+
+static int hlt_disabled=0;
+static void floppy_disable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if (!hlt_disabled){
+ hlt_disabled=1;
+#ifdef HAVE_DISABLE_HLT
+ disable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+static void floppy_enable_hlt(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ if (hlt_disabled){
+ hlt_disabled=0;
+#ifdef HAVE_DISABLE_HLT
+ enable_hlt();
+#endif
+ }
+ restore_flags(flags);
+}
+
+
+static void setup_DMA(void)
+{
+#ifdef FLOPPY_SANITY_CHECK
+ if (raw_cmd->length == 0){
+ int i;
+
+ printk("zero dma transfer size:");
+ for (i=0; i < raw_cmd->cmd_count; i++)
+ printk("%x,", raw_cmd->cmd[i]);
+ printk("\n");
+ cont->done(0);
+ FDCS->reset = 1;
+ return;
+ }
+ if ((long) raw_cmd->kernel_data % 512){
+ printk("non aligned address: %p\n", raw_cmd->kernel_data);
+ cont->done(0);
+ FDCS->reset=1;
+ return;
+ }
+ if (CROSS_64KB(raw_cmd->kernel_data, raw_cmd->length)) {
+ printk("DMA crossing 64-K boundary %p-%p\n",
+ raw_cmd->kernel_data,
+ raw_cmd->kernel_data + raw_cmd->length);
+ cont->done(0);
+ FDCS->reset=1;
+ return;
+ }
+#endif
+ cli();
+ fd_disable_dma();
+ fd_clear_dma_ff();
+ fd_set_dma_mode((raw_cmd->flags & FD_RAW_READ)?
+ DMA_MODE_READ : DMA_MODE_WRITE);
+ fd_set_dma_addr(virt_to_bus(raw_cmd->kernel_data));
+ fd_set_dma_count(raw_cmd->length);
+ fd_enable_dma();
+ sti();
+ floppy_disable_hlt();
+}
+
+/* sends a command byte to the fdc */
+static int output_byte(char byte)
+{
+ int counter;
+ unsigned char status = 0;
+ unsigned char rstatus;
+
+ if (FDCS->reset)
+ return -1;
+ for (counter = 0; counter < 10000 && !FDCS->reset; counter++) {
+ rstatus = fd_inb(FD_STATUS);
+ status = rstatus &(STATUS_READY|STATUS_DIR|STATUS_DMA);
+ if (!(status & STATUS_READY))
+ continue;
+ if (status == STATUS_READY){
+ fd_outb(byte,FD_DATA);
+
+#ifdef FLOPPY_SANITY_CHECK
+ output_log[output_log_pos].data = byte;
+ output_log[output_log_pos].status = rstatus;
+ output_log[output_log_pos].jiffies = jiffies;
+ output_log_pos = (output_log_pos + 1) % OLOGSIZE;
+#endif
+ return 0;
+ } else
+ break;
+ }
+ FDCS->reset = 1;
+ if (!initialising)
+ DPRINT2("Unable to send byte %x to FDC. Status=%x\n",
+ byte, status);
+ return -1;
+}
+#define LAST_OUT(x) if (output_byte(x)){ reset_fdc();return;}
+
+/* gets the response from the fdc */
+static int result(void)
+{
+ int i = 0, counter, status = 0;
+
+ if (FDCS->reset)
+ return -1;
+ for (counter = 0; counter < 10000 && !FDCS->reset; counter++) {
+ status = fd_inb(FD_STATUS)&
+ (STATUS_DIR|STATUS_READY|STATUS_BUSY|STATUS_DMA);
+ if (!(status & STATUS_READY))
+ continue;
+ if (status == STATUS_READY){
+#ifdef FLOPPY_SANITY_CHECK
+ resultjiffies = jiffies;
+ resultsize = i;
+#endif
+ return i;
+ }
+ if (status & STATUS_DMA)
+ break;
+ if (status == (STATUS_DIR|STATUS_READY|STATUS_BUSY)) {
+ if (i >= MAX_REPLIES) {
+ DPRINT("floppy_stat reply overrun\n");
+ break;
+ }
+ reply_buffer[i++] = fd_inb(FD_DATA);
+ }
+ }
+ FDCS->reset = 1;
+ if (!initialising)
+ DPRINT3("Getstatus times out (%x) on fdc %d [%d]\n",
+ status, fdc, i);
+ return -1;
+}
+
+/* Set perpendicular mode as required, based on data rate, if supported.
+ * 82077 Now tested. 1Mbps data rate only possible with 82077-1.
+ */
+static inline void perpendicular_mode(void)
+{
+ unsigned char perp_mode;
+
+ if (raw_cmd->rate & 0x40){
+ switch(raw_cmd->rate & 3){
+ case 0:
+ perp_mode=2;
+ break;
+ case 3:
+ perp_mode=3;
+ break;
+ default:
+ DPRINT("Invalid data rate for perpendicular mode!\n");
+ cont->done(0);
+ FDCS->reset = 1; /* convenient way to return to
+ * redo without to much hassle (deep
+ * stack et al. */
+ return;
+ }
+ } else
+ perp_mode = 0;
+
+ if (FDCS->perp_mode == perp_mode)
+ return;
+ if (FDCS->version >= FDC_82077_ORIG && FDCS->has_fifo) {
+ output_byte(FD_PERPENDICULAR);
+ output_byte(perp_mode);
+ FDCS->perp_mode = perp_mode;
+ } else if (perp_mode) {
+ DPRINT("perpendicular mode not supported by this FDC.\n");
+ }
+} /* perpendicular_mode */
+
+#define NOMINAL_DTR 500
+
+/* Issue a "SPECIFY" command to set the step rate time, head unload time,
+ * head load time, and DMA disable flag to values needed by floppy.
+ *
+ * The value "dtr" is the data transfer rate in Kbps. It is needed
+ * to account for the data rate-based scaling done by the 82072 and 82077
+ * FDC types. This parameter is ignored for other types of FDCs (i.e.
+ * 8272a).
+ *
+ * Note that changing the data transfer rate has a (probably deleterious)
+ * effect on the parameters subject to scaling for 82072/82077 FDCs, so
+ * fdc_specify is called again after each data transfer rate
+ * change.
+ *
+ * srt: 1000 to 16000 in microseconds
+ * hut: 16 to 240 milliseconds
+ * hlt: 2 to 254 milliseconds
+ *
+ * These values are rounded up to the next highest available delay time.
+ */
+static void fdc_specify(void)
+{
+ unsigned char spec1, spec2;
+ int srt, hlt, hut;
+ unsigned long dtr = NOMINAL_DTR;
+ unsigned long scale_dtr = NOMINAL_DTR;
+ int hlt_max_code = 0x7f;
+ int hut_max_code = 0xf;
+
+ if (FDCS->need_configure && FDCS->has_fifo) {
+ if (FDCS->reset)
+ return;
+ /* Turn on FIFO for 82077-class FDC (improves performance) */
+ /* TODO: lock this in via LOCK during initialization */
+ output_byte(FD_CONFIGURE);
+ output_byte(0);
+ output_byte(0x2A); /* FIFO on, polling off, 10 byte threshold */
+ output_byte(0); /* precompensation from track 0 upwards */
+ if (FDCS->reset){
+ FDCS->has_fifo=0;
+ return;
+ }
+ FDCS->need_configure = 0;
+ /*DPRINT("FIFO enabled\n");*/
+ }
+
+ switch (raw_cmd->rate & 0x03) {
+ case 3:
+ dtr = 1000;
+ break;
+ case 1:
+ dtr = 300;
+ break;
+ case 2:
+ dtr = 250;
+ break;
+ }
+
+ if (FDCS->version >= FDC_82072) {
+ scale_dtr = dtr;
+ hlt_max_code = 0x00; /* 0==256msec*dtr0/dtr (not linear!) */
+ hut_max_code = 0x0; /* 0==256msec*dtr0/dtr (not linear!) */
+ }
+
+ /* Convert step rate from microseconds to milliseconds and 4 bits */
+ srt = 16 - (DP->srt*scale_dtr/1000 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+ SUPBOUND(srt, 0xf);
+ INFBOUND(srt, 0);
+
+ hlt = (DP->hlt*scale_dtr/2 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+ if (hlt < 0x01)
+ hlt = 0x01;
+ else if (hlt > 0x7f)
+ hlt = hlt_max_code;
+
+ hut = (DP->hut*scale_dtr/16 + NOMINAL_DTR - 1)/NOMINAL_DTR;
+ if (hut < 0x1)
+ hut = 0x1;
+ else if (hut > 0xf)
+ hut = hut_max_code;
+
+ spec1 = (srt << 4) | hut;
+ spec2 = (hlt << 1);
+
+ /* If these parameters did not change, just return with success */
+ if (FDCS->spec1 != spec1 || FDCS->spec2 != spec2) {
+ /* Go ahead and set spec1 and spec2 */
+ output_byte(FD_SPECIFY);
+ output_byte(FDCS->spec1 = spec1);
+ output_byte(FDCS->spec2 = spec2);
+ }
+} /* fdc_specify */
+
+/* Set the FDC's data transfer rate on behalf of the specified drive.
+ * NOTE: with 82072/82077 FDCs, changing the data rate requires a reissue
+ * of the specify command (i.e. using the fdc_specify function).
+ */
+static int fdc_dtr(void)
+{
+ /* If data rate not already set to desired value, set it. */
+ if ((raw_cmd->rate & 3) == FDCS->dtr)
+ return 0;
+
+ /* Set dtr */
+ fd_outb(raw_cmd->rate & 3, FD_DCR);
+
+ /* TODO: some FDC/drive combinations (C&T 82C711 with TEAC 1.2MB)
+ * need a stabilization period of several milliseconds to be
+ * enforced after data rate changes before R/W operations.
+ * Pause 5 msec to avoid trouble. (Needs to be 2 jiffies)
+ */
+ FDCS->dtr = raw_cmd->rate & 3;
+ return(wait_for_completion(jiffies+2*HZ/100,
+ (timeout_fn) floppy_ready));
+} /* fdc_dtr */
+
+static void tell_sector(void)
+{
+ printk(": track %d, head %d, sector %d, size %d",
+ R_TRACK, R_HEAD, R_SECTOR, R_SIZECODE);
+} /* tell_sector */
+
+
+/*
+ * Ok, this error interpreting routine is called after a
+ * DMA read/write has succeeded
+ * or failed, so we check the results, and copy any buffers.
+ * hhb: Added better error reporting.
+ * ak: Made this into a separate routine.
+ */
+static int interpret_errors(void)
+{
+ char bad;
+
+ if (inr!=7) {
+ DPRINT("-- FDC reply error");
+ FDCS->reset = 1;
+ return 1;
+ }
+
+ /* check IC to find cause of interrupt */
+ switch (ST0 & ST0_INTR) {
+ case 0x40: /* error occurred during command execution */
+ bad = 1;
+ if (ST1 & ST1_WP) {
+ DPRINT("Drive is write protected\n");
+ CLEARF(FD_DISK_WRITABLE);
+ cont->done(0);
+ bad = 2;
+ } else if (ST1 & ST1_ND) {
+ SETF(FD_NEED_TWADDLE);
+ } else if (ST1 & ST1_OR) {
+ if (DP->flags & FTD_MSG)
+ DPRINT("Over/Underrun - retrying\n");
+ bad = 0;
+ }else if (*errors >= DP->max_errors.reporting){
+ DPRINT("");
+ if (ST0 & ST0_ECE) {
+ printk("Recalibrate failed!");
+ } else if (ST2 & ST2_CRC) {
+ printk("data CRC error");
+ tell_sector();
+ } else if (ST1 & ST1_CRC) {
+ printk("CRC error");
+ tell_sector();
+ } else if ((ST1 & (ST1_MAM|ST1_ND)) || (ST2 & ST2_MAM)) {
+ if (!probing) {
+ printk("sector not found");
+ tell_sector();
+ } else
+ printk("probe failed...");
+ } else if (ST2 & ST2_WC) { /* seek error */
+ printk("wrong cylinder");
+ } else if (ST2 & ST2_BC) { /* cylinder marked as bad */
+ printk("bad cylinder");
+ } else {
+ printk("unknown error. ST[0..2] are: 0x%x 0x%x 0x%x", ST0, ST1, ST2);
+ tell_sector();
+ }
+ printk("\n");
+
+ }
+ if (ST2 & ST2_WC || ST2 & ST2_BC)
+ /* wrong cylinder => recal */
+ DRS->track = NEED_2_RECAL;
+ return bad;
+ case 0x80: /* invalid command given */
+ DPRINT("Invalid FDC command given!\n");
+ cont->done(0);
+ return 2;
+ case 0xc0:
+ DPRINT("Abnormal termination caused by polling\n");
+ cont->error();
+ return 2;
+ default: /* (0) Normal command termination */
+ return 0;
+ }
+}
+
+/*
+ * This routine is called when everything should be correctly set up
+ * for the transfer (ie floppy motor is on, the correct floppy is
+ * selected, and the head is sitting on the right track).
+ */
+static void setup_rw_floppy(void)
+{
+ int i,ready_date,r, flags,dflags;
+ timeout_fn function;
+
+ flags = raw_cmd->flags;
+ if (flags & (FD_RAW_READ | FD_RAW_WRITE))
+ flags |= FD_RAW_INTR;
+
+ if ((flags & FD_RAW_SPIN) && !(flags & FD_RAW_NO_MOTOR)){
+ ready_date = DRS->spinup_date + DP->spinup;
+ /* If spinup will take a long time, rerun scandrives
+ * again just before spinup completion. Beware that
+ * after scandrives, we must again wait for selection.
+ */
+ if (ready_date > jiffies + DP->select_delay){
+ ready_date -= DP->select_delay;
+ function = (timeout_fn) floppy_start;
+ } else
+ function = (timeout_fn) setup_rw_floppy;
+
+ /* wait until the floppy is spinning fast enough */
+ if (wait_for_completion(ready_date,function))
+ return;
+ }
+ dflags = DRS->flags;
+
+ if ((flags & FD_RAW_READ) || (flags & FD_RAW_WRITE))
+ setup_DMA();
+
+ if (flags & FD_RAW_INTR)
+ SET_INTR(main_command_interrupt);
+
+ r=0;
+ for (i=0; i< raw_cmd->cmd_count; i++)
+ r|=output_byte(raw_cmd->cmd[i]);
+
+#ifdef DEBUGT
+ debugt("rw_command: ");
+#endif
+ if (r){
+ reset_fdc();
+ return;
+ }
+
+ if (!(flags & FD_RAW_INTR)){
+ inr = result();
+ cont->interrupt();
+ } else if (flags & FD_RAW_NEED_DISK)
+ fd_watchdog();
+}
+
+static int blind_seek;
+
+/*
+ * This is the routine called after every seek (or recalibrate) interrupt
+ * from the floppy controller.
+ */
+static void seek_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("seek interrupt:");
+#endif
+ if (inr != 2 || (ST0 & 0xF8) != 0x20) {
+ DPRINT("seek failed\n");
+ DRS->track = NEED_2_RECAL;
+ cont->error();
+ cont->redo();
+ return;
+ }
+ if (DRS->track >= 0 && DRS->track != ST1 && !blind_seek){
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of effective seek\n");
+ DPRINT1("jiffies=%ld\n", jiffies);
+ }
+#endif
+ CLEARF(FD_DISK_NEWCHANGE); /* effective seek */
+ DRS->select_date = jiffies;
+ }
+ DRS->track = ST1;
+ floppy_ready();
+}
+
+static void check_wp(void)
+{
+ if (TESTF(FD_VERIFY)) {
+ /* check write protection */
+ output_byte(FD_GETSTATUS);
+ output_byte(UNIT(current_drive));
+ if (result() != 1){
+ FDCS->reset = 1;
+ return;
+ }
+ CLEARF(FD_VERIFY);
+ CLEARF(FD_NEED_TWADDLE);
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("checking whether disk is write protected\n");
+ DPRINT1("wp=%x\n",ST3 & 0x40);
+ }
+#endif
+ if (!(ST3 & 0x40))
+ SETF(FD_DISK_WRITABLE);
+ else
+ CLEARF(FD_DISK_WRITABLE);
+ }
+}
+
+static void seek_floppy(void)
+{
+ int track;
+
+ blind_seek=0;
+
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from seek\n");
+ }
+#endif
+
+ if (!TESTF(FD_DISK_NEWCHANGE) &&
+ disk_change(current_drive) &&
+ (raw_cmd->flags & FD_RAW_NEED_DISK)){
+ /* the media changed flag should be cleared after the seek.
+ * If it isn't, this means that there is really no disk in
+ * the drive.
+ */
+ SETF(FD_DISK_CHANGED);
+ cont->done(0);
+ cont->redo();
+ return;
+ }
+ if (DRS->track <= NEED_1_RECAL){
+ recalibrate_floppy();
+ return;
+ } else if (TESTF(FD_DISK_NEWCHANGE) &&
+ (raw_cmd->flags & FD_RAW_NEED_DISK) &&
+ (DRS->track <= NO_TRACK || DRS->track == raw_cmd->track)) {
+ /* we seek to clear the media-changed condition. Does anybody
+ * know a more elegant way, which works on all drives? */
+ if (raw_cmd->track)
+ track = raw_cmd->track - 1;
+ else {
+ if (DP->flags & FD_SILENT_DCL_CLEAR){
+ set_dor(fdc, ~(0x10 << UNIT(current_drive)), 0);
+ blind_seek = 1;
+ raw_cmd->flags |= FD_RAW_NEED_SEEK;
+ }
+ track = 1;
+ }
+ } else {
+ check_wp();
+ if (raw_cmd->track != DRS->track &&
+ (raw_cmd->flags & FD_RAW_NEED_SEEK))
+ track = raw_cmd->track;
+ else {
+ setup_rw_floppy();
+ return;
+ }
+ }
+
+ SET_INTR(seek_interrupt);
+ output_byte(FD_SEEK);
+ output_byte(UNIT(current_drive));
+ LAST_OUT(track);
+#ifdef DEBUGT
+ debugt("seek command:");
+#endif
+}
+
+static void recal_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("recal interrupt:");
+#endif
+ if (inr !=2)
+ FDCS->reset = 1;
+ else if (ST0 & ST0_ECE) {
+ switch(DRS->track){
+ case NEED_1_RECAL:
+#ifdef DEBUGT
+ debugt("recal interrupt need 1 recal:");
+#endif
+ /* after a second recalibrate, we still haven't
+ * reached track 0. Probably no drive. Raise an
+ * error, as failing immediately might upset
+ * computers possessed by the Devil :-) */
+ cont->error();
+ cont->redo();
+ return;
+ case NEED_2_RECAL:
+#ifdef DEBUGT
+ debugt("recal interrupt need 2 recal:");
+#endif
+ /* If we already did a recalibrate,
+ * and we are not at track 0, this
+ * means we have moved. (The only way
+ * not to move at recalibration is to
+ * be already at track 0.) Clear the
+ * new change flag */
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("clearing NEWCHANGE flag because of second recalibrate\n");
+ }
+#endif
+
+ CLEARF(FD_DISK_NEWCHANGE);
+ DRS->select_date = jiffies;
+ /* fall through */
+ default:
+#ifdef DEBUGT
+ debugt("recal interrupt default:");
+#endif
+ /* Recalibrate moves the head by at
+ * most 80 steps. If after one
+ * recalibrate we don't have reached
+ * track 0, this might mean that we
+ * started beyond track 80. Try
+ * again. */
+ DRS->track = NEED_1_RECAL;
+ break;
+ }
+ } else
+ DRS->track = ST1;
+ floppy_ready();
+}
+
+/*
+ * Unexpected interrupt - Print as much debugging info as we can...
+ * All bets are off...
+ */
+static void unexpected_floppy_interrupt(void)
+{
+ int i;
+ if (initialising)
+ return;
+ if (print_unex){
+ DPRINT("unexpected interrupt\n");
+ if (inr >= 0)
+ for (i=0; i<inr; i++)
+ printk("%d %x\n", i, reply_buffer[i]);
+ }
+ while(1){
+ output_byte(FD_SENSEI);
+ inr=result();
+ if (inr != 2)
+ break;
+ if (print_unex){
+ printk("sensei\n");
+ for (i=0; i<inr; i++)
+ printk("%d %x\n", i, reply_buffer[i]);
+ }
+ }
+ FDCS->reset = 1;
+}
+
+static struct tq_struct floppy_tq =
+{ 0, 0, (void *) (void *) unexpected_floppy_interrupt, 0 };
+
+/* interrupt handler */
+static void floppy_interrupt(int irq, struct pt_regs * regs)
+{
+ void (*handler)(void) = DEVICE_INTR;
+
+ lasthandler = handler;
+ interruptjiffies = jiffies;
+
+ floppy_enable_hlt();
+ CLEAR_INTR;
+ if (fdc >= N_FDC || FDCS->address == -1){
+ /* we don't even know which FDC is the culprit */
+ printk("DOR0=%x\n", fdc_state[0].dor);
+ printk("floppy interrupt on bizarre fdc %d\n",fdc);
+ printk("handler=%p\n", handler);
+ is_alive("bizarre fdc");
+ return;
+ }
+ inr = result();
+ if (!handler){
+ unexpected_floppy_interrupt();
+ is_alive("unexpected");
+ return;
+ }
+ if (inr == 0){
+ do {
+ output_byte(FD_SENSEI);
+ inr = result();
+ } while ((ST0 & 0x83) != UNIT(current_drive) && inr == 2);
+ }
+ floppy_tq.routine = (void *)(void *) handler;
+ queue_task_irq(&floppy_tq, &tq_timer);
+ is_alive("normal interrupt end");
+}
+
+static void recalibrate_floppy(void)
+{
+#ifdef DEBUGT
+ debugt("recalibrate floppy:");
+#endif
+ SET_INTR(recal_interrupt);
+ output_byte(FD_RECALIBRATE);
+ LAST_OUT(UNIT(current_drive));
+}
+
+/*
+ * Must do 4 FD_SENSEIs after reset because of ``drive polling''.
+ */
+static void reset_interrupt(void)
+{
+#ifdef DEBUGT
+ debugt("reset interrupt:");
+#endif
+ /* fdc_specify(); reprogram fdc */
+ result(); /* get the status ready for set_fdc */
+ if (FDCS->reset) {
+ printk("reset set in interrupt, calling %p\n", cont->error);
+ cont->error(); /* a reset just after a reset. BAD! */
+ }
+ cont->redo();
+}
+
+/*
+ * reset is done by pulling bit 2 of DOR low for a while (old FDC's),
+ * or by setting the self clearing bit 7 of STATUS (newer FDC's)
+ */
+static void reset_fdc(void)
+{
+ SET_INTR(reset_interrupt);
+ FDCS->reset = 0;
+ reset_fdc_info(0);
+ if (FDCS->version >= FDC_82077)
+ fd_outb(0x80 | (FDCS->dtr &3), FD_STATUS);
+ else {
+ fd_outb(FDCS->dor & ~0x04, FD_DOR);
+ udelay(FD_RESET_DELAY);
+ outb(FDCS->dor, FD_DOR);
+ }
+}
+
+static void empty(void)
+{
+}
+
+void show_floppy(void)
+{
+ int i;
+
+ printk("\n");
+ printk("floppy driver state\n");
+ printk("-------------------\n");
+ printk("now=%ld last interrupt=%d last called handler=%p\n",
+ jiffies, interruptjiffies, lasthandler);
+
+
+#ifdef FLOPPY_SANITY_CHECK
+ printk("timeout_message=%s\n", timeout_message);
+ printk("last output bytes:\n");
+ for (i=0; i < OLOGSIZE; i++)
+ printk("%2x %2x %ld\n",
+ output_log[(i+output_log_pos) % OLOGSIZE].data,
+ output_log[(i+output_log_pos) % OLOGSIZE].status,
+ output_log[(i+output_log_pos) % OLOGSIZE].jiffies);
+ printk("last result at %d\n", resultjiffies);
+ printk("last redo_fd_request at %d\n", lastredo);
+ for (i=0; i<resultsize; i++){
+ printk("%2x ", reply_buffer[i]);
+ }
+ printk("\n");
+#endif
+
+ printk("status=%x\n", fd_inb(FD_STATUS));
+ printk("fdc_busy=%d\n", fdc_busy);
+ if (DEVICE_INTR)
+ printk("DEVICE_INTR=%p\n", DEVICE_INTR);
+ if (floppy_tq.sync)
+ printk("floppy_tq.routine=%p\n", floppy_tq.routine);
+ if (fd_timer.prev)
+ printk("fd_timer.function=%p\n", fd_timer.function);
+ if (fd_timeout.prev){
+ printk("timer_table=%p\n",fd_timeout.function);
+ printk("expires=%ld\n",fd_timeout.expires-jiffies);
+ printk("now=%ld\n",jiffies);
+ }
+ printk("cont=%p\n", cont);
+ printk("CURRENT=%p\n", CURRENT);
+ printk("command_status=%d\n", command_status);
+ printk("\n");
+}
+
+static void floppy_shutdown(void)
+{
+ if (!initialising)
+ show_floppy();
+ CLEAR_INTR;
+ floppy_tq.routine = (void *)(void *) empty;
+ del_timer(&fd_timer);
+ sti();
+
+ floppy_enable_hlt();
+ fd_disable_dma();
+ /* avoid dma going to a random drive after shutdown */
+
+ if (!initialising)
+ DPRINT("floppy timeout\n");
+ FDCS->reset = 1;
+ if (cont){
+ cont->done(0);
+ cont->redo(); /* this will recall reset when needed */
+ } else {
+ printk("no cont in shutdown!\n");
+ process_fd_request();
+ }
+ is_alive("floppy shutdown");
+}
+/*typedef void (*timeout_fn)(unsigned long);*/
+
+/* start motor, check media-changed condition and write protection */
+static int start_motor(void (*function)(void) )
+{
+ int mask, data;
+
+ mask = 0xfc;
+ data = UNIT(current_drive);
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR)){
+ if (!(FDCS->dor & (0x10 << UNIT(current_drive)))){
+ set_debugt();
+ /* no read since this drive is running */
+ DRS->first_read_date = 0;
+ /* note motor start time if motor is not yet running */
+ DRS->spinup_date = jiffies;
+ data |= (0x10 << UNIT(current_drive));
+ }
+ } else
+ if (FDCS->dor & (0x10 << UNIT(current_drive)))
+ mask &= ~(0x10 << UNIT(current_drive));
+
+ /* starts motor and selects floppy */
+ del_timer(motor_off_timer + current_drive);
+ set_dor(fdc, mask, data);
+
+ /* wait_for_completion also schedules reset if needed. */
+ return(wait_for_completion(DRS->select_date+DP->select_delay,
+ (timeout_fn) function));
+}
+
+static void floppy_ready(void)
+{
+ CHECK_RESET;
+ if (start_motor(floppy_ready)) return;
+ if (fdc_dtr()) return;
+
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from floppy_ready\n");
+ }
+#endif
+
+ if (!(raw_cmd->flags & FD_RAW_NO_MOTOR) &&
+ disk_change(current_drive) &&
+ !DP->select_delay)
+ twaddle(); /* this clears the dcl on certain drive/controller
+ * combinations */
+
+ if (raw_cmd->flags & (FD_RAW_NEED_SEEK | FD_RAW_NEED_DISK)){
+ perpendicular_mode();
+ fdc_specify(); /* must be done here because of hut, hlt ... */
+ seek_floppy();
+ } else
+ setup_rw_floppy();
+}
+
+static void floppy_start(void)
+{
+ reschedule_timeout(CURRENTD, "floppy start", 0);
+
+ scandrives();
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in floppy_start\n");
+ }
+#endif
+ SETF(FD_DISK_NEWCHANGE);
+ floppy_ready();
+}
+
+/*
+ * ========================================================================
+ * here ends the bottom half. Exported routines are:
+ * floppy_start, floppy_off, floppy_ready, lock_fdc, unlock_fdc, set_fdc,
+ * start_motor, reset_fdc, reset_fdc_info, interpret_errors.
+ * Initialisation also uses output_byte, result, set_dor, floppy_interrupt
+ * and set_dor.
+ * ========================================================================
+ */
+/*
+ * General purpose continuations.
+ * ==============================
+ */
+
+static void do_wakeup(void)
+{
+ reschedule_timeout(MAXTIMEOUT, "do wakeup", 0);
+ cont = 0;
+ command_status += 2;
+ wake_up(&command_done);
+}
+
+static struct cont_t wakeup_cont={
+ empty,
+ do_wakeup,
+ empty,
+ (done_f)empty
+};
+
+static int wait_til_done(void (*handler)(void), int interruptible)
+{
+ int ret;
+
+ floppy_tq.routine = (void *)(void *) handler;
+ queue_task(&floppy_tq, &tq_timer);
+
+ cli();
+ while(command_status < 2 && NO_SIGNAL){
+ is_alive("wait_til_done");
+ if (interruptible)
+ interruptible_sleep_on(&command_done);
+ else
+ sleep_on(&command_done);
+ }
+ if (command_status < 2){
+ floppy_shutdown();
+ sti();
+ process_fd_request();
+ return -EINTR;
+ }
+ sti();
+
+ if (FDCS->reset)
+ command_status = FD_COMMAND_ERROR;
+ if (command_status == FD_COMMAND_OKAY)
+ ret=0;
+ else
+ ret=-EIO;
+ command_status = FD_COMMAND_NONE;
+ return ret;
+}
+
+static void generic_done(int result)
+{
+ command_status = result;
+ cont = &wakeup_cont;
+}
+
+static void generic_success(void)
+{
+ cont->done(1);
+}
+
+static void generic_failure(void)
+{
+ cont->done(0);
+}
+
+static void success_and_wakeup(void)
+{
+ generic_success();
+ cont->redo();
+}
+
+
+/*
+ * formatting and rw support.
+ * ==========================
+ */
+
+static int next_valid_format(void)
+{
+ int probed_format;
+
+ probed_format = DRS->probed_format;
+ while(1){
+ if (probed_format >= 8 ||
+ !DP->autodetect[probed_format]){
+ DRS->probed_format = 0;
+ return 1;
+ }
+ if (floppy_type[DP->autodetect[probed_format]].sect){
+ DRS->probed_format = probed_format;
+ return 0;
+ }
+ probed_format++;
+ }
+}
+
+static void bad_flp_intr(void)
+{
+ if (probing){
+ DRS->probed_format++;
+ if (!next_valid_format())
+ return;
+ }
+ (*errors)++;
+ INFBOUND(DRWE->badness, *errors);
+ if (*errors > DP->max_errors.abort)
+ cont->done(0);
+ if (*errors > DP->max_errors.reset)
+ FDCS->reset = 1;
+ else if (*errors > DP->max_errors.recal)
+ DRS->track = NEED_2_RECAL;
+}
+
+static void set_floppy(kdev_t device)
+{
+ if (TYPE(device))
+ floppy = TYPE(device) + floppy_type;
+ else
+ floppy = current_type[ DRIVE(device) ];
+}
+
+/*
+ * formatting and support.
+ * =======================
+ */
+static void format_interrupt(void)
+{
+ switch (interpret_errors()){
+ case 1:
+ cont->error();
+ case 2:
+ break;
+ case 0:
+ cont->done(1);
+ }
+ cont->redo();
+}
+
+#define CODE2SIZE (ssize = ((1 << SIZECODE) + 3) >> 2)
+#define FM_MODE(x,y) ((y) & ~(((x)->rate & 0x80) >>1))
+#define CT(x) ((x) | 0x40)
+static void setup_format_params(int track)
+{
+ struct fparm {
+ unsigned char track,head,sect,size;
+ } *here = (struct fparm *)floppy_track_buffer;
+ int il,n;
+ int count,head_shift,track_shift;
+
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->track = track;
+
+ raw_cmd->flags = FD_RAW_WRITE | FD_RAW_INTR | FD_RAW_SPIN |
+ /*FD_RAW_NEED_DISK |*/ FD_RAW_NEED_SEEK;
+ raw_cmd->rate = floppy->rate & 0x43;
+ raw_cmd->cmd_count = NR_F;
+ COMMAND = FM_MODE(floppy,FD_FORMAT);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,format_req.head);
+ F_SIZECODE = FD_SIZECODE(floppy);
+ F_SECT_PER_TRACK = floppy->sect << 2 >> F_SIZECODE;
+ F_GAP = floppy->fmt_gap;
+ F_FILL = FD_FILL_BYTE;
+
+ raw_cmd->kernel_data = floppy_track_buffer;
+ raw_cmd->length = 4 * F_SECT_PER_TRACK;
+
+ /* allow for about 30ms for data transport per track */
+ head_shift = (F_SECT_PER_TRACK + 5) / 6;
+
+ /* a ``cylinder'' is two tracks plus a little stepping time */
+ track_shift = 2 * head_shift + 3;
+
+ /* position of logical sector 1 on this track */
+ n = (track_shift * format_req.track + head_shift * format_req.head)
+ % F_SECT_PER_TRACK;
+
+ /* determine interleave */
+ il = 1;
+ if (floppy->sect > DP->interleave_sect && F_SIZECODE == 2)
+ il++;
+
+ /* initialize field */
+ for (count = 0; count < F_SECT_PER_TRACK; ++count) {
+ here[count].track = format_req.track;
+ here[count].head = format_req.head;
+ here[count].sect = 0;
+ here[count].size = F_SIZECODE;
+ }
+ /* place logical sectors */
+ for (count = 1; count <= F_SECT_PER_TRACK; ++count) {
+ here[n].sect = count;
+ n = (n+il) % F_SECT_PER_TRACK;
+ if (here[n].sect) { /* sector busy, find next free sector */
+ ++n;
+ if (n>= F_SECT_PER_TRACK) {
+ n-=F_SECT_PER_TRACK;
+ while (here[n].sect) ++n;
+ }
+ }
+ }
+}
+
+static void redo_format(void)
+{
+ buffer_track = -1;
+ setup_format_params(format_req.track << STRETCH(floppy));
+ floppy_start();
+#ifdef DEBUGT
+ debugt("queue format request");
+#endif
+}
+
+static struct cont_t format_cont={
+ format_interrupt,
+ redo_format,
+ bad_flp_intr,
+ generic_done };
+
+static int do_format(kdev_t device, struct format_descr *tmp_format_req)
+{
+ int ret;
+ int drive=DRIVE(device);
+
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ if (!floppy ||
+ floppy->track > DP->tracks ||
+ tmp_format_req->track >= floppy->track ||
+ tmp_format_req->head >= floppy->head ||
+ (floppy->sect << 2) % (1 << FD_SIZECODE(floppy)) ||
+ !floppy->fmt_gap) {
+ process_fd_request();
+ return -EINVAL;
+ }
+ format_req = *tmp_format_req;
+ format_errors = 0;
+ cont = &format_cont;
+ errors = &format_errors;
+ IWAIT(redo_format);
+ process_fd_request();
+ return ret;
+}
+
+/*
+ * Buffer read/write and support
+ * =============================
+ */
+
+/* new request_done. Can handle physical sectors which are smaller than a
+ * logical buffer */
+static void request_done(int uptodate)
+{
+ int block;
+
+ probing = 0;
+ reschedule_timeout(MAXTIMEOUT, "request done %d", uptodate);
+
+ if (!CURRENT){
+ DPRINT("request list destroyed in floppy request done\n");
+ return;
+ }
+ if (uptodate){
+ /* maintain values for invalidation on geometry
+ * change */
+ block = current_count_sectors + CURRENT->sector;
+ INFBOUND(DRS->maxblock, block);
+ if (block > floppy->sect)
+ DRS->maxtrack = 1;
+
+ /* unlock chained buffers */
+ while (current_count_sectors && CURRENT &&
+ current_count_sectors >= CURRENT->current_nr_sectors){
+ current_count_sectors -= CURRENT->current_nr_sectors;
+ CURRENT->nr_sectors -= CURRENT->current_nr_sectors;
+ CURRENT->sector += CURRENT->current_nr_sectors;
+ end_request(1);
+ }
+ if (current_count_sectors && CURRENT){
+ /* "unlock" last subsector */
+ CURRENT->buffer += current_count_sectors <<9;
+ CURRENT->current_nr_sectors -= current_count_sectors;
+ CURRENT->nr_sectors -= current_count_sectors;
+ CURRENT->sector += current_count_sectors;
+ return;
+ }
+
+ if (current_count_sectors && !CURRENT)
+ DPRINT("request list destroyed in floppy request done\n");
+
+ } else {
+ if (CURRENT->cmd == WRITE) {
+ /* record write error information */
+ DRWE->write_errors++;
+ if (DRWE->write_errors == 1) {
+ DRWE->first_error_sector = CURRENT->sector;
+ DRWE->first_error_generation = DRS->generation;
+ }
+ DRWE->last_error_sector = CURRENT->sector;
+ DRWE->last_error_generation = DRS->generation;
+ }
+ end_request(0);
+ }
+}
+
+/* Interrupt handler evaluating the result of the r/w operation */
+static void rw_interrupt(void)
+{
+ int nr_sectors, ssize;
+
+ if (!DRS->first_read_date)
+ DRS->first_read_date = jiffies;
+
+ nr_sectors = 0;
+ CODE2SIZE;
+ nr_sectors = ((R_TRACK-TRACK)*floppy->head+R_HEAD-HEAD) *
+ floppy->sect + ((R_SECTOR-SECTOR) << SIZECODE >> 2) -
+ (sector_t % floppy->sect) % ssize;
+
+#ifdef FLOPPY_SANITY_CHECK
+ if (nr_sectors > current_count_sectors + ssize -
+ (current_count_sectors + sector_t) % ssize +
+ sector_t % ssize){
+ DPRINT2("long rw: %x instead of %lx\n",
+ nr_sectors, current_count_sectors);
+ printk("rs=%d s=%d\n", R_SECTOR, SECTOR);
+ printk("rh=%d h=%d\n", R_HEAD, HEAD);
+ printk("rt=%d t=%d\n", R_TRACK, TRACK);
+ printk("spt=%d st=%d ss=%d\n", SECT_PER_TRACK,
+ sector_t, ssize);
+ }
+#endif
+ INFBOUND(nr_sectors,0);
+ SUPBOUND(current_count_sectors, nr_sectors);
+
+ switch (interpret_errors()){
+ case 2:
+ cont->redo();
+ return;
+ case 1:
+ if (!current_count_sectors){
+ cont->error();
+ cont->redo();
+ return;
+ }
+ break;
+ case 0:
+ if (!current_count_sectors){
+ cont->redo();
+ return;
+ }
+ current_type[current_drive] = floppy;
+ floppy_sizes[TOMINOR(current_drive) ]= floppy->size>>1;
+ break;
+ }
+
+ if (probing) {
+ if (DP->flags & FTD_MSG)
+ DPRINT2("Auto-detected floppy type %s in fd%d\n",
+ floppy->name,current_drive);
+ current_type[current_drive] = floppy;
+ floppy_sizes[TOMINOR(current_drive)] = floppy->size >> 1;
+ probing = 0;
+ }
+
+ if (CT(COMMAND) != FD_READ ||
+ raw_cmd->kernel_data == CURRENT->buffer){
+ /* transfer directly from buffer */
+ cont->done(1);
+ } else if (CT(COMMAND) == FD_READ){
+ buffer_track = raw_cmd->track;
+ buffer_drive = current_drive;
+ INFBOUND(buffer_max, nr_sectors + sector_t);
+ }
+ cont->redo();
+}
+
+/* Compute maximal contiguous buffer size. */
+static int buffer_chain_size(void)
+{
+ struct buffer_head *bh;
+ int size;
+ char *base;
+
+ base = CURRENT->buffer;
+ size = CURRENT->current_nr_sectors << 9;
+ bh = CURRENT->bh;
+
+ if (bh){
+ bh = bh->b_reqnext;
+ while (bh && bh->b_data == base + size){
+ size += bh->b_size;
+ bh = bh->b_reqnext;
+ }
+ }
+ return size >> 9;
+}
+
+/* Compute the maximal transfer size */
+static int transfer_size(int ssize, int max_sector, int max_size)
+{
+ SUPBOUND(max_sector, sector_t + max_size);
+
+ /* alignment */
+ max_sector -= (max_sector % floppy->sect) % ssize;
+
+ /* transfer size, beginning not aligned */
+ current_count_sectors = max_sector - sector_t ;
+
+ return max_sector;
+}
+
+/*
+ * Move data from/to the track buffer to/from the buffer cache.
+ */
+static void copy_buffer(int ssize, int max_sector, int max_sector_2)
+{
+ int remaining; /* number of transferred 512-byte sectors */
+ struct buffer_head *bh;
+ char *buffer, *dma_buffer;
+ int size;
+
+ max_sector = transfer_size(ssize,
+ minimum(max_sector, max_sector_2),
+ CURRENT->nr_sectors);
+
+ if (current_count_sectors <= 0 && CT(COMMAND) == FD_WRITE &&
+ buffer_max > sector_t + CURRENT->nr_sectors)
+ current_count_sectors = minimum(buffer_max - sector_t,
+ CURRENT->nr_sectors);
+
+ remaining = current_count_sectors << 9;
+#ifdef FLOPPY_SANITY_CHECK
+ if ((remaining >> 9) > CURRENT->nr_sectors &&
+ CT(COMMAND) == FD_WRITE){
+ DPRINT("in copy buffer\n");
+ printk("current_count_sectors=%ld\n", current_count_sectors);
+ printk("remaining=%d\n", remaining >> 9);
+ printk("CURRENT->nr_sectors=%ld\n",CURRENT->nr_sectors);
+ printk("CURRENT->current_nr_sectors=%ld\n",
+ CURRENT->current_nr_sectors);
+ printk("max_sector=%d\n", max_sector);
+ printk("ssize=%d\n", ssize);
+ }
+#endif
+
+ buffer_max = maximum(max_sector, buffer_max);
+
+ dma_buffer = floppy_track_buffer + ((sector_t - buffer_min) << 9);
+
+ bh = CURRENT->bh;
+ size = CURRENT->current_nr_sectors << 9;
+ buffer = CURRENT->buffer;
+
+ while (remaining > 0){
+ SUPBOUND(size, remaining);
+#ifdef FLOPPY_SANITY_CHECK
+ if (dma_buffer + size >
+ floppy_track_buffer + (max_buffer_sectors << 10) ||
+ dma_buffer < floppy_track_buffer){
+ DPRINT1("buffer overrun in copy buffer %d\n",
+ (int) ((floppy_track_buffer - dma_buffer) >>9));
+ printk("sector_t=%d buffer_min=%d\n",
+ sector_t, buffer_min);
+ printk("current_count_sectors=%ld\n",
+ current_count_sectors);
+ if (CT(COMMAND) == FD_READ)
+ printk("read\n");
+ if (CT(COMMAND) == FD_READ)
+ printk("write\n");
+ break;
+ }
+ if (((unsigned long)buffer) % 512)
+ DPRINT1("%p buffer not aligned\n", buffer);
+#endif
+ if (CT(COMMAND) == FD_READ) {
+ fd_cacheflush(dma_buffer, size);
+ memcpy(buffer, dma_buffer, size);
+ } else {
+ memcpy(dma_buffer, buffer, size);
+ fd_cacheflush(dma_buffer, size);
+ }
+ remaining -= size;
+ if (!remaining)
+ break;
+
+ dma_buffer += size;
+ bh = bh->b_reqnext;
+#ifdef FLOPPY_SANITY_CHECK
+ if (!bh){
+ DPRINT("bh=null in copy buffer after copy\n");
+ break;
+ }
+#endif
+ size = bh->b_size;
+ buffer = bh->b_data;
+ }
+#ifdef FLOPPY_SANITY_CHECK
+ if (remaining){
+ if (remaining > 0)
+ max_sector -= remaining >> 9;
+ DPRINT1("weirdness: remaining %d\n", remaining>>9);
+ }
+#endif
+}
+
+/*
+ * Formulate a read/write request.
+ * this routine decides where to load the data (directly to buffer, or to
+ * tmp floppy area), how much data to load (the size of the buffer, the whole
+ * track, or a single sector)
+ * All floppy_track_buffer handling goes in here. If we ever add track buffer
+ * allocation on the fly, it should be done here. No other part should need
+ * modification.
+ */
+
+static int make_raw_rw_request(void)
+{
+ int aligned_sector_t;
+ int max_sector, max_size, tracksize, ssize;
+
+ set_fdc(DRIVE(CURRENT->rq_dev));
+
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags = FD_RAW_SPIN | FD_RAW_NEED_DISK | FD_RAW_NEED_DISK |
+ FD_RAW_NEED_SEEK;
+ raw_cmd->cmd_count = NR_RW;
+ if (CURRENT->cmd == READ){
+ raw_cmd->flags |= FD_RAW_READ;
+ COMMAND = FM_MODE(floppy,FD_READ);
+ } else if (CURRENT->cmd == WRITE){
+ raw_cmd->flags |= FD_RAW_WRITE;
+ COMMAND = FM_MODE(floppy,FD_WRITE);
+ } else {
+ DPRINT("make_raw_rw_request: unknown command\n");
+ return 0;
+ }
+
+ max_sector = floppy->sect * floppy->head;
+
+ TRACK = CURRENT->sector / max_sector;
+ sector_t = CURRENT->sector % max_sector;
+ if (floppy->track && TRACK >= floppy->track)
+ return 0;
+ HEAD = sector_t / floppy->sect;
+
+ if (((floppy->stretch & FD_SWAPSIDES) || TESTF(FD_NEED_TWADDLE)) &&
+ sector_t < floppy->sect)
+ max_sector = floppy->sect;
+
+ /* 2M disks have phantom sectors on the first track */
+ if ((floppy->rate & FD_2M) && (!TRACK) && (!HEAD)){
+ max_sector = 2 * floppy->sect / 3;
+ if (sector_t >= max_sector){
+ current_count_sectors = minimum(floppy->sect - sector_t,
+ CURRENT->nr_sectors);
+ return 1;
+ }
+ SIZECODE = 2;
+ } else
+ SIZECODE = FD_SIZECODE(floppy);
+ raw_cmd->rate = floppy->rate & 0x43;
+ if ((floppy->rate & FD_2M) &&
+ (TRACK || HEAD) &&
+ raw_cmd->rate == 2)
+ raw_cmd->rate = 1;
+
+ if (SIZECODE)
+ SIZECODE2 = 0xff;
+ else
+ SIZECODE2 = 0x80;
+ raw_cmd->track = TRACK << STRETCH(floppy);
+ DR_SELECT = UNIT(current_drive) + PH_HEAD(floppy,HEAD);
+ GAP = floppy->gap;
+ CODE2SIZE;
+ SECT_PER_TRACK = floppy->sect << 2 >> SIZECODE;
+ SECTOR = ((sector_t % floppy->sect) << 2 >> SIZECODE) + 1;
+ tracksize = floppy->sect - floppy->sect % ssize;
+ if (tracksize < floppy->sect){
+ SECT_PER_TRACK ++;
+ if (tracksize <= sector_t % floppy->sect)
+ SECTOR--;
+ while (tracksize <= sector_t % floppy->sect){
+ while(tracksize + ssize > floppy->sect){
+ SIZECODE--;
+ ssize >>= 1;
+ }
+ SECTOR++; SECT_PER_TRACK ++;
+ tracksize += ssize;
+ }
+ max_sector = HEAD * floppy->sect + tracksize;
+ } else if (!TRACK && !HEAD && !(floppy->rate & FD_2M) && probing)
+ max_sector = floppy->sect;
+
+ aligned_sector_t = sector_t - (sector_t % floppy->sect) % ssize;
+ max_size = CURRENT->nr_sectors;
+ if ((raw_cmd->track == buffer_track) &&
+ (current_drive == buffer_drive) &&
+ (sector_t >= buffer_min) && (sector_t < buffer_max)) {
+ /* data already in track buffer */
+ if (CT(COMMAND) == FD_READ) {
+ copy_buffer(1, max_sector, buffer_max);
+ return 1;
+ }
+ } else if (aligned_sector_t != sector_t || CURRENT->nr_sectors < ssize){
+ if (CT(COMMAND) == FD_WRITE){
+ if (sector_t + CURRENT->nr_sectors > ssize &&
+ sector_t + CURRENT->nr_sectors < ssize + ssize)
+ max_size = ssize + ssize;
+ else
+ max_size = ssize;
+ }
+ raw_cmd->flags &= ~FD_RAW_WRITE;
+ raw_cmd->flags |= FD_RAW_READ;
+ COMMAND = FM_MODE(floppy,FD_READ);
+ } else if ((unsigned long)CURRENT->buffer < MAX_DMA_ADDRESS) {
+ unsigned long dma_limit;
+ int direct, indirect;
+
+ indirect= transfer_size(ssize,max_sector,max_buffer_sectors*2) -
+ sector_t;
+
+ /*
+ * Do NOT use minimum() here---MAX_DMA_ADDRESS is 64 bits wide
+ * on a 64 bit machine!
+ */
+ max_size = buffer_chain_size();
+ dma_limit = (MAX_DMA_ADDRESS - ((unsigned long) CURRENT->buffer)) >> 9;
+ if ((unsigned long) max_size > dma_limit) {
+ max_size = dma_limit;
+ }
+ /* 64 kb boundaries */
+ if (CROSS_64KB(CURRENT->buffer, max_size << 9))
+ max_size = (K_64 - ((long) CURRENT->buffer) % K_64)>>9;
+ direct = transfer_size(ssize,max_sector,max_size) - sector_t;
+ /*
+ * We try to read tracks, but if we get too many errors, we
+ * go back to reading just one sector at a time.
+ *
+ * This means we should be able to read a sector even if there
+ * are other bad sectors on this track.
+ */
+ if (!direct ||
+ (indirect * 2 > direct * 3 &&
+ *errors < DP->max_errors.read_track &&
+ /*!TESTF(FD_NEED_TWADDLE) &&*/
+ ((!probing || (DP->read_track&(1<<DRS->probed_format)))))){
+ max_size = CURRENT->nr_sectors;
+ } else {
+ raw_cmd->kernel_data = CURRENT->buffer;
+ raw_cmd->length = current_count_sectors << 9;
+ if (raw_cmd->length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ DPRINT3("indirect=%d direct=%d sector_t=%d",
+ indirect, direct, sector_t);
+ return 0;
+ }
+ return 2;
+ }
+ }
+
+ if (CT(COMMAND) == FD_READ)
+ max_size = max_sector; /* unbounded */
+
+ /* claim buffer track if needed */
+ if (buffer_track != raw_cmd->track || /* bad track */
+ buffer_drive !=current_drive || /* bad drive */
+ sector_t > buffer_max ||
+ sector_t < buffer_min ||
+ ((CT(COMMAND) == FD_READ ||
+ (aligned_sector_t == sector_t && CURRENT->nr_sectors >= ssize))&&
+ max_sector > 2 * max_buffer_sectors + buffer_min &&
+ max_size + sector_t > 2 * max_buffer_sectors + buffer_min)
+ /* not enough space */){
+ buffer_track = -1;
+ buffer_drive = current_drive;
+ buffer_max = buffer_min = aligned_sector_t;
+ }
+ raw_cmd->kernel_data = floppy_track_buffer +
+ ((aligned_sector_t-buffer_min)<<9);
+
+ if (CT(COMMAND) == FD_WRITE){
+ /* copy write buffer to track buffer.
+ * if we get here, we know that the write
+ * is either aligned or the data already in the buffer
+ * (buffer will be overwritten) */
+#ifdef FLOPPY_SANITY_CHECK
+ if (sector_t != aligned_sector_t && buffer_track == -1)
+ DPRINT("internal error offset !=0 on write\n");
+#endif
+ buffer_track = raw_cmd->track;
+ buffer_drive = current_drive;
+ copy_buffer(ssize, max_sector, 2*max_buffer_sectors+buffer_min);
+ } else
+ transfer_size(ssize, max_sector,
+ 2*max_buffer_sectors+buffer_min-aligned_sector_t);
+
+ /* round up current_count_sectors to get dma xfer size */
+ raw_cmd->length = sector_t+current_count_sectors-aligned_sector_t;
+ raw_cmd->length = ((raw_cmd->length -1)|(ssize-1))+1;
+ raw_cmd->length <<= 9;
+#ifdef FLOPPY_SANITY_CHECK
+ if ((raw_cmd->length < current_count_sectors << 9) ||
+ (raw_cmd->kernel_data != CURRENT->buffer &&
+ CT(COMMAND) == FD_WRITE &&
+ (aligned_sector_t + (raw_cmd->length >> 9) > buffer_max ||
+ aligned_sector_t < buffer_min)) ||
+ raw_cmd->length % (128 << SIZECODE) ||
+ raw_cmd->length <= 0 || current_count_sectors <= 0){
+ DPRINT2("fractionary current count b=%lx s=%lx\n",
+ raw_cmd->length, current_count_sectors);
+ if (raw_cmd->kernel_data != CURRENT->buffer)
+ printk("addr=%d, length=%ld\n",
+ (int) ((raw_cmd->kernel_data -
+ floppy_track_buffer) >> 9),
+ current_count_sectors);
+ printk("st=%d ast=%d mse=%d msi=%d\n",
+ sector_t, aligned_sector_t, max_sector, max_size);
+ printk("ssize=%x SIZECODE=%d\n", ssize, SIZECODE);
+ printk("command=%x SECTOR=%d HEAD=%d, TRACK=%d\n",
+ COMMAND, SECTOR, HEAD, TRACK);
+ printk("buffer drive=%d\n", buffer_drive);
+ printk("buffer track=%d\n", buffer_track);
+ printk("buffer_min=%d\n", buffer_min);
+ printk("buffer_max=%d\n", buffer_max);
+ return 0;
+ }
+
+ if (raw_cmd->kernel_data != CURRENT->buffer){
+ if (raw_cmd->kernel_data < floppy_track_buffer ||
+ current_count_sectors < 0 ||
+ raw_cmd->length < 0 ||
+ raw_cmd->kernel_data + raw_cmd->length >
+ floppy_track_buffer + (max_buffer_sectors << 10)){
+ DPRINT("buffer overrun in schedule dma\n");
+ printk("sector_t=%d buffer_min=%d current_count=%ld\n",
+ sector_t, buffer_min,
+ raw_cmd->length >> 9);
+ printk("current_count_sectors=%ld\n",
+ current_count_sectors);
+ if (CT(COMMAND) == FD_READ)
+ printk("read\n");
+ if (CT(COMMAND) == FD_READ)
+ printk("write\n");
+ return 0;
+ }
+ } else if (raw_cmd->length > CURRENT->nr_sectors << 9 ||
+ current_count_sectors > CURRENT->nr_sectors){
+ DPRINT("buffer overrun in direct transfer\n");
+ return 0;
+ } else if (raw_cmd->length < current_count_sectors << 9){
+ DPRINT("more sectors than bytes\n");
+ printk("bytes=%ld\n", raw_cmd->length >> 9);
+ printk("sectors=%ld\n", current_count_sectors);
+ }
+ if (raw_cmd->length == 0){
+ DPRINT("zero dma transfer attempted from make_raw_request\n");
+ return 0;
+ }
+#endif
+ return 2;
+}
+
+static void redo_fd_request(void)
+{
+#define REPEAT {request_done(0); continue; }
+ kdev_t device;
+ int tmp;
+
+ lastredo = jiffies;
+ if (current_drive < N_DRIVE)
+ floppy_off(current_drive);
+
+ if (CURRENT && CURRENT->rq_status == RQ_INACTIVE){
+ DPRINT("current not active!\n");
+ return;
+ }
+
+ while(1){
+ if (!CURRENT) {
+ CLEAR_INTR;
+ unlock_fdc();
+ return;
+ }
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR)
+ panic(DEVICE_NAME ": request list destroyed");
+ if (CURRENT->bh && !buffer_locked(CURRENT->bh))
+ panic(DEVICE_NAME ": block not locked");
+
+ device = CURRENT->rq_dev;
+ set_fdc(DRIVE(device));
+ reschedule_timeout(CURRENTD, "redo fd request", 0);
+
+ set_floppy(device);
+ raw_cmd = & default_raw_cmd;
+ raw_cmd->flags = 0;
+ if (start_motor(redo_fd_request)) return;
+ if (test_bit(current_drive, &fake_change) ||
+ TESTF(FD_DISK_CHANGED)){
+ DPRINT("disk absent or changed during operation\n");
+ REPEAT;
+ }
+ if (!floppy) { /* Autodetection */
+ if (!probing){
+ DRS->probed_format = 0;
+ if (next_valid_format()){
+ DPRINT("no autodetectable formats\n");
+ floppy = NULL;
+ REPEAT;
+ }
+ }
+ probing = 1;
+ floppy = floppy_type+DP->autodetect[DRS->probed_format];
+ } else
+ probing = 0;
+ errors = & (CURRENT->errors);
+ tmp = make_raw_rw_request();
+ if (tmp < 2){
+ request_done(tmp);
+ continue;
+ }
+
+ if (TESTF(FD_NEED_TWADDLE))
+ twaddle();
+ floppy_tq.routine = (void *)(void *) floppy_start;
+ queue_task(&floppy_tq, &tq_timer);
+#ifdef DEBUGT
+ debugt("queue fd request");
+#endif
+ return;
+ }
+#undef REPEAT
+}
+
+static struct cont_t rw_cont={
+ rw_interrupt,
+ redo_fd_request,
+ bad_flp_intr,
+ request_done };
+
+static struct tq_struct request_tq =
+{ 0, 0, (void *) (void *) redo_fd_request, 0 };
+
+static void process_fd_request(void)
+{
+ cont = &rw_cont;
+ queue_task(&request_tq, &tq_timer);
+}
+
+static void do_fd_request(void)
+{
+ if (fdc_busy){
+ /* fdc busy, this new request will be treated when the
+ current one is done */
+ is_alive("do fd request, old request running");
+ return;
+ }
+ lock_fdc(MAXTIMEOUT,0);
+ process_fd_request();
+ is_alive("do fd request");
+}
+
+static struct cont_t poll_cont={
+ success_and_wakeup,
+ floppy_ready,
+ generic_failure,
+ generic_done };
+
+static int poll_drive(int interruptible, int flag)
+{
+ int ret;
+ /* no auto-sense, just clear dcl */
+ raw_cmd = &default_raw_cmd;
+ raw_cmd->flags= flag;
+ raw_cmd->track=0;
+ raw_cmd->cmd_count=0;
+ cont = &poll_cont;
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("setting NEWCHANGE in poll_drive\n");
+ }
+#endif
+ SETF(FD_DISK_NEWCHANGE);
+ WAIT(floppy_ready);
+ return ret;
+}
+
+/*
+ * User triggered reset
+ * ====================
+ */
+
+static void reset_intr(void)
+{
+ printk("weird, reset interrupt called\n");
+}
+
+static struct cont_t reset_cont={
+ reset_intr,
+ success_and_wakeup,
+ generic_failure,
+ generic_done };
+
+static int user_reset_fdc(int drive, int arg, int interruptible)
+{
+ int ret;
+
+ ret=0;
+ LOCK_FDC(drive,interruptible);
+ if (arg == FD_RESET_ALWAYS)
+ FDCS->reset=1;
+ if (FDCS->reset){
+ cont = &reset_cont;
+ WAIT(reset_fdc);
+ }
+ process_fd_request();
+ return ret;
+}
+
+/*
+ * Misc Ioctl's and support
+ * ========================
+ */
+static int fd_copyout(void *param, const void *address, int size)
+{
+ int ret;
+
+ ECALL(verify_area(VERIFY_WRITE,param,size));
+ fd_cacheflush(address, size); /* is this necessary ??? */
+ /* Ralf: Yes; only the l2 cache is completly chipset
+ controlled */
+ memcpy_tofs(param,(void *) address, size);
+ return 0;
+}
+
+static int fd_copyin(void *param, void *address, int size)
+{
+ int ret;
+
+ ECALL(verify_area(VERIFY_READ,param,size));
+ memcpy_fromfs((void *) address, param, size);
+ return 0;
+}
+
+#define COPYOUT(x) ECALL(fd_copyout((void *)param, &(x), sizeof(x)))
+#define COPYIN(x) ECALL(fd_copyin((void *)param, &(x), sizeof(x)))
+
+static inline const char *drive_name(int type, int drive)
+{
+ struct floppy_struct *floppy;
+
+ if (type)
+ floppy = floppy_type + type;
+ else {
+ if (UDP->native_format)
+ floppy = floppy_type + UDP->native_format;
+ else
+ return "(null)";
+ }
+ if (floppy->name)
+ return floppy->name;
+ else
+ return "(null)";
+}
+
+
+/* raw commands */
+static void raw_cmd_done(int flag)
+{
+ int i;
+
+ if (!flag) {
+ raw_cmd->flags = FD_RAW_FAILURE;
+ raw_cmd->flags |= FD_RAW_HARDFAILURE;
+ } else {
+ raw_cmd->reply_count = inr;
+ for (i=0; i< raw_cmd->reply_count; i++)
+ raw_cmd->reply[i] = reply_buffer[i];
+
+ if (raw_cmd->flags & (FD_RAW_READ | FD_RAW_WRITE))
+ raw_cmd->length = get_dma_residue(FLOPPY_DMA);
+
+ if ((raw_cmd->flags & FD_RAW_SOFTFAILURE) &&
+ (!raw_cmd->reply_count || (raw_cmd->reply[0] & 0xc0)))
+ raw_cmd->flags |= FD_RAW_FAILURE;
+
+ if (disk_change(current_drive))
+ raw_cmd->flags |= FD_RAW_DISK_CHANGE;
+ else
+ raw_cmd->flags &= ~FD_RAW_DISK_CHANGE;
+ if (raw_cmd->flags & FD_RAW_NO_MOTOR_AFTER)
+ motor_off_callback(current_drive);
+
+ if (raw_cmd->next &&
+ (!(raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags & FD_RAW_STOP_IF_FAILURE)) &&
+ ((raw_cmd->flags & FD_RAW_FAILURE) ||
+ !(raw_cmd->flags &FD_RAW_STOP_IF_SUCCESS))) {
+ raw_cmd = raw_cmd->next;
+ return;
+ }
+ }
+ generic_done(flag);
+}
+
+
+static struct cont_t raw_cmd_cont={
+ success_and_wakeup,
+ floppy_start,
+ generic_failure,
+ raw_cmd_done
+};
+
+static inline int raw_cmd_copyout(int cmd, char *param,
+ struct floppy_raw_cmd *ptr)
+{
+ struct old_floppy_raw_cmd old_raw_cmd;
+ int ret;
+
+ while(ptr) {
+ if (cmd == OLDFDRAWCMD) {
+ old_raw_cmd.flags = ptr->flags;
+ old_raw_cmd.data = ptr->data;
+ old_raw_cmd.length = ptr->length;
+ old_raw_cmd.rate = ptr->rate;
+ old_raw_cmd.reply_count = ptr->reply_count;
+ memcpy(old_raw_cmd.reply, ptr->reply, 7);
+ COPYOUT(old_raw_cmd);
+ param += sizeof(old_raw_cmd);
+ } else {
+ COPYOUT(*ptr);
+ param += sizeof(struct floppy_raw_cmd);
+ }
+
+ if ((ptr->flags & FD_RAW_READ) && ptr->buffer_length){
+ if (ptr->length>=0 && ptr->length<=ptr->buffer_length)
+ ECALL(fd_copyout(ptr->data,
+ ptr->kernel_data,
+ ptr->buffer_length -
+ ptr->length));
+ }
+ ptr = ptr->next;
+ }
+ return 0;
+}
+
+
+static void raw_cmd_free(struct floppy_raw_cmd **ptr)
+{
+ struct floppy_raw_cmd *next,*this;
+
+ this = *ptr;
+ *ptr = 0;
+ while(this) {
+ if (this->buffer_length) {
+ free_pages((unsigned long)this->kernel_data,
+ __get_order(this->buffer_length));
+ this->buffer_length = 0;
+ }
+ next = this->next;
+ kfree(this);
+ this = next;
+ }
+}
+
+
+static inline int raw_cmd_copyin(int cmd, char *param,
+ struct floppy_raw_cmd **rcmd)
+{
+ struct floppy_raw_cmd *ptr;
+ struct old_floppy_raw_cmd old_raw_cmd;
+ int ret;
+ int i;
+
+ *rcmd = 0;
+ while(1) {
+ ptr = (struct floppy_raw_cmd *)
+ kmalloc(sizeof(struct floppy_raw_cmd), GFP_USER);
+ if (!ptr)
+ return -ENOMEM;
+ *rcmd = ptr;
+ if (cmd == OLDFDRAWCMD){
+ COPYIN(old_raw_cmd);
+ ptr->flags = old_raw_cmd.flags;
+ ptr->data = old_raw_cmd.data;
+ ptr->length = old_raw_cmd.length;
+ ptr->rate = old_raw_cmd.rate;
+ ptr->cmd_count = old_raw_cmd.cmd_count;
+ ptr->track = old_raw_cmd.track;
+ ptr->phys_length = 0;
+ ptr->next = 0;
+ ptr->buffer_length = 0;
+ memcpy(ptr->cmd, old_raw_cmd.cmd, 9);
+ param += sizeof(struct old_floppy_raw_cmd);
+ if (ptr->cmd_count > 9)
+ return -EINVAL;
+ } else {
+ COPYIN(*ptr);
+ ptr->next = 0;
+ ptr->buffer_length = 0;
+ param += sizeof(struct floppy_raw_cmd);
+ if (ptr->cmd_count > 16)
+ return -EINVAL;
+ }
+
+ for (i=0; i< 16; i++)
+ ptr->reply[i] = 0;
+ ptr->resultcode = 0;
+ ptr->kernel_data = 0;
+
+ if (ptr->flags & (FD_RAW_READ | FD_RAW_WRITE)) {
+ if (ptr->length <= 0)
+ return -EINVAL;
+ ptr->kernel_data =(char*)dma_mem_alloc(ptr->length);
+ if (!ptr->kernel_data)
+ return -ENOMEM;
+ ptr->buffer_length = ptr->length;
+ }
+ if ( ptr->flags & FD_RAW_READ )
+ ECALL( verify_area( VERIFY_WRITE, ptr->data,
+ ptr->length ));
+ if (ptr->flags & FD_RAW_WRITE)
+ ECALL(fd_copyin(ptr->data, ptr->kernel_data,
+ ptr->length));
+ rcmd = & (ptr->next);
+ if (!(ptr->flags & FD_RAW_MORE))
+ return 0;
+ ptr->rate &= 0x43;
+ }
+}
+
+
+static int raw_cmd_ioctl(int cmd, void *param)
+{
+ int drive, ret, ret2;
+ struct floppy_raw_cmd *my_raw_cmd;
+
+ if (FDCS->rawcmd <= 1)
+ FDCS->rawcmd = 1;
+ for (drive= 0; drive < N_DRIVE; drive++){
+ if (FDC(drive) != fdc)
+ continue;
+ if (drive == current_drive){
+ if (UDRS->fd_ref > 1){
+ FDCS->rawcmd = 2;
+ break;
+ }
+ } else if (UDRS->fd_ref){
+ FDCS->rawcmd = 2;
+ break;
+ }
+ }
+
+ if (FDCS->reset)
+ return -EIO;
+
+ ret = raw_cmd_copyin(cmd, param, &my_raw_cmd);
+ if (ret) {
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
+ }
+
+ raw_cmd = my_raw_cmd;
+ cont = &raw_cmd_cont;
+ ret=wait_til_done(floppy_start,1);
+#ifdef DCL_DEBUG
+ if (DP->flags & FD_DEBUG){
+ DPRINT("calling disk change from raw_cmd ioctl\n");
+ }
+#endif
+
+ if (ret != -EINTR && FDCS->reset)
+ ret = -EIO;
+
+ DRS->track = NO_TRACK;
+
+ ret2 = raw_cmd_copyout(cmd, param, my_raw_cmd);
+ if (!ret)
+ ret = ret2;
+ raw_cmd_free(&my_raw_cmd);
+ return ret;
+}
+
+static int invalidate_drive(kdev_t rdev)
+{
+ /* invalidate the buffer track to force a reread */
+ set_bit(DRIVE(rdev), &fake_change);
+ process_fd_request();
+ check_disk_change(rdev);
+ return 0;
+}
+
+
+static inline void clear_write_error(int drive)
+{
+ CLEARSTRUCT(UDRWE);
+}
+
+static inline int set_geometry(unsigned int cmd, struct floppy_struct *g,
+ int drive, int type, kdev_t device)
+{
+ int cnt;
+
+ /* sanity checking for parameters.*/
+ if (g->sect <= 0 ||
+ g->head <= 0 ||
+ g->track <= 0 ||
+ g->track > UDP->tracks>>STRETCH(g) ||
+ /* check if reserved bits are set */
+ (g->stretch&~(FD_STRETCH|FD_SWAPSIDES)) != 0)
+ return -EINVAL;
+ if (type){
+ if (!suser())
+ return -EPERM;
+ LOCK_FDC(drive,1);
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ set_bit(drive, &fake_change);
+ }
+ floppy_type[type] = *g;
+ floppy_type[type].name="user format";
+ for (cnt = type << 2; cnt < (type << 2) + 4; cnt++)
+ floppy_sizes[cnt]= floppy_sizes[cnt+0x80]=
+ floppy_type[type].size>>1;
+ process_fd_request();
+ for (cnt = 0; cnt < N_DRIVE; cnt++){
+ if (ITYPE(drive_state[cnt].fd_device) == type &&
+ drive_state[cnt].fd_ref)
+ check_disk_change(
+ MKDEV(FLOPPY_MAJOR,
+ drive_state[cnt].fd_device));
+ }
+ } else {
+ LOCK_FDC(drive,1);
+ if (cmd != FDDEFPRM)
+ /* notice a disk change immediately, else
+ * we loose our settings immediately*/
+ CALL(poll_drive(1,0));
+ user_params[drive] = *g;
+ if (buffer_drive == drive)
+ SUPBOUND(buffer_max, user_params[drive].sect);
+ current_type[drive] = &user_params[drive];
+ floppy_sizes[drive] = user_params[drive].size >> 1;
+ if (cmd == FDDEFPRM)
+ DRS->keep_data = -1;
+ else
+ DRS->keep_data = 1;
+ /* invalidation. Invalidate only when needed, i.e.
+ * when there are already sectors in the buffer cache
+ * whose number will change. This is useful, because
+ * mtools often changes the geometry of the disk after
+ * looking at the boot block */
+ if (DRS->maxblock > user_params[drive].sect || DRS->maxtrack)
+ invalidate_drive(device);
+ else
+ process_fd_request();
+ }
+ return 0;
+}
+
+/* handle obsolete ioctl's */
+static struct translation_entry {
+ int newcmd;
+ int oldcmd;
+ int oldsize; /* size of 0x00xx-style ioctl. Reflects old structures, thus
+ * use numeric values. NO SIZEOFS */
+} translation_table[]= {
+ {FDCLRPRM, 0, 0},
+ {FDSETPRM, 1, 28},
+ {FDDEFPRM, 2, 28},
+ {FDGETPRM, 3, 28},
+ {FDMSGON, 4, 0},
+ {FDMSGOFF, 5, 0},
+ {FDFMTBEG, 6, 0},
+ {FDFMTTRK, 7, 12},
+ {FDFMTEND, 8, 0},
+ {FDSETEMSGTRESH, 10, 0},
+ {FDFLUSH, 11, 0},
+ {FDSETMAXERRS, 12, 20},
+ {OLDFDRAWCMD, 30, 0},
+ {FDGETMAXERRS, 14, 20},
+ {FDGETDRVTYP, 16, 16},
+ {FDSETDRVPRM, 20, 88},
+ {FDGETDRVPRM, 21, 88},
+ {FDGETDRVSTAT, 22, 52},
+ {FDPOLLDRVSTAT, 23, 52},
+ {FDRESET, 24, 0},
+ {FDGETFDCSTAT, 25, 40},
+ {FDWERRORCLR, 27, 0},
+ {FDWERRORGET, 28, 24},
+ {FDRAWCMD, 0, 0},
+ {FDTWADDLE, 40, 0} };
+
+static inline int normalize_0x02xx_ioctl(int *cmd, int *size)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(translation_table); i++) {
+ if ((*cmd & 0xffff) == (translation_table[i].newcmd & 0xffff)){
+ *size = _IOC_SIZE(*cmd);
+ *cmd = translation_table[i].newcmd;
+ if (*size > _IOC_SIZE(*cmd)) {
+ printk("ioctl not yet supported\n");
+ return -EFAULT;
+ }
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static inline int xlate_0x00xx_ioctl(int *cmd, int *size)
+{
+ int i;
+ /* old ioctls' for kernels <= 1.3.33 */
+ /* When the next even release will come around, we'll start
+ * warning against these.
+ * When the next odd release will come around, we'll fail with
+ * -EINVAL */
+ if(strcmp(system_utsname.version, "1.4.0") >= 0)
+ printk("obsolete floppy ioctl %x\n", *cmd);
+ if((system_utsname.version[0] == '1' &&
+ strcmp(system_utsname.version, "1.5.0") >= 0) ||
+ (system_utsname.version[0] >= '2' &&
+ strcmp(system_utsname.version, "2.1.0") >= 0))
+ return -EINVAL;
+ for (i=0; i < ARRAY_SIZE(translation_table); i++) {
+ if (*cmd == translation_table[i].oldcmd) {
+ *size = translation_table[i].oldsize;
+ *cmd = translation_table[i].newcmd;
+ return 0;
+ }
+ }
+ return -EINVAL;
+}
+
+static int fd_ioctl(struct inode *inode, struct file *filp, unsigned int cmd,
+ unsigned long param)
+{
+#define IOCTL_MODE_BIT 8
+#define OPEN_WRITE_BIT 16
+#define IOCTL_ALLOWED (filp && (filp->f_mode & IOCTL_MODE_BIT))
+#define OUT(c,x) case c: outparam = (const char *) (x); break
+#define IN(c,x,tag) case c: *(x) = inparam. tag ; return 0
+
+ int i,drive,type;
+ kdev_t device;
+ int ret;
+ int size;
+ union inparam {
+ struct floppy_struct g; /* geometry */
+ struct format_descr f;
+ struct floppy_max_errors max_errors;
+ struct floppy_drive_params dp;
+ } inparam; /* parameters coming from user space */
+ const char *outparam; /* parameters passed back to user space */
+
+ device = inode->i_rdev;
+ switch (cmd) {
+ RO_IOCTLS(device,param);
+ }
+ type = TYPE(device);
+ drive = DRIVE(device);
+
+ /* convert the old style command into a new style command */
+ if ((cmd & 0xff00) == 0x0200) {
+ ECALL(normalize_0x02xx_ioctl(&cmd, &size));
+ } else if ((cmd & 0xff00) == 0x0000) {
+ ECALL(xlate_0x00xx_ioctl(&cmd, &size));
+ } else
+ return -EINVAL;
+
+ /* permission checks */
+ if (((cmd & 0x80) && !suser()) ||
+ ((cmd & 0x40) && !IOCTL_ALLOWED))
+ return -EPERM;
+
+ /* verify writability of result, and fail early */
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ ECALL(verify_area(VERIFY_WRITE,(void *) param, size));
+
+ /* copyin */
+ CLEARSTRUCT(&inparam);
+ if (_IOC_DIR(cmd) & _IOC_WRITE)
+ ECALL(fd_copyin((void *)param, &inparam, size))
+
+ switch (cmd) {
+ case FDCLRPRM:
+ LOCK_FDC(drive,1);
+ current_type[drive] = NULL;
+ floppy_sizes[drive] = MAX_DISK_SIZE;
+ UDRS->keep_data = 0;
+ return invalidate_drive(device);
+ case FDSETPRM:
+ case FDDEFPRM:
+ return set_geometry(cmd, & inparam.g,
+ drive, type, device);
+ case FDGETPRM:
+ if (type)
+ outparam = (char *) &floppy_type[type];
+ else
+ outparam = (char *) current_type[drive];
+ if(!outparam)
+ return -ENODEV;
+ break;
+
+ case FDMSGON:
+ UDP->flags |= FTD_MSG;
+ return 0;
+ case FDMSGOFF:
+ UDP->flags &= ~FTD_MSG;
+ return 0;
+
+ case FDFMTBEG:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ ret = UDRS->flags;
+ process_fd_request();
+ if(ret & FD_VERIFY)
+ return -ENODEV;
+ if(!(ret & FD_DISK_WRITABLE))
+ return -EROFS;
+ return 0;
+ case FDFMTTRK:
+ if (UDRS->fd_ref != 1)
+ return -EBUSY;
+ return do_format(device, &inparam.f);
+ case FDFMTEND:
+ case FDFLUSH:
+ LOCK_FDC(drive,1);
+ return invalidate_drive(device);
+
+ case FDSETEMSGTRESH:
+ UDP->max_errors.reporting =
+ (unsigned short) (param & 0x0f);
+ return 0;
+ OUT(FDGETMAXERRS, &UDP->max_errors);
+ IN(FDSETMAXERRS, &UDP->max_errors, max_errors);
+
+ case FDGETDRVTYP:
+ outparam = drive_name(type,drive);
+ SUPBOUND(size,strlen(outparam)+1);
+ break;
+
+ IN(FDSETDRVPRM, UDP, dp);
+ OUT(FDGETDRVPRM, UDP);
+
+ case FDPOLLDRVSTAT:
+ LOCK_FDC(drive,1);
+ CALL(poll_drive(1, FD_RAW_NEED_DISK));
+ process_fd_request();
+ /* fall through */
+ OUT(FDGETDRVSTAT, UDRS);
+
+ case FDRESET:
+ return user_reset_fdc(drive, (int)param, 1);
+
+ OUT(FDGETFDCSTAT,UFDCS);
+
+ case FDWERRORCLR:
+ CLEARSTRUCT(UDRWE);
+ return 0;
+ OUT(FDWERRORGET,UDRWE);
+
+ case OLDFDRAWCMD:
+ case FDRAWCMD:
+ if (type)
+ return -EINVAL;
+ LOCK_FDC(drive,1);
+ set_floppy(device);
+ CALL(i = raw_cmd_ioctl(cmd,(void *) param));
+ process_fd_request();
+ return i;
+
+ case FDTWADDLE:
+ LOCK_FDC(drive,1);
+ twaddle();
+ process_fd_request();
+ return 0;
+
+ default:
+ return -EINVAL;
+ }
+
+ if (_IOC_DIR(cmd) & _IOC_READ)
+ return fd_copyout((void *)param, outparam, size);
+ else
+ return 0;
+#undef IOCTL_ALLOWED
+#undef OUT
+#undef IN
+}
+
+static void config_types(void)
+{
+ int first=1;
+ int drive;
+
+ /* read drive info out of physical cmos */
+ drive=0;
+ if (!UDP->cmos)
+ UDP->cmos= FLOPPY0_TYPE;
+ drive=1;
+ if (!UDP->cmos && FLOPPY1_TYPE)
+ UDP->cmos = FLOPPY1_TYPE;
+
+ /* XXX */
+ /* additional physical CMOS drive detection should go here */
+
+ for (drive=0; drive < N_DRIVE; drive++){
+ if (UDP->cmos >= 0 && UDP->cmos <= NUMBER(default_drive_params))
+ memcpy((char *) UDP,
+ (char *) (&default_drive_params[(int)UDP->cmos].params),
+ sizeof(struct floppy_drive_params));
+ if (UDP->cmos){
+ if (first)
+ printk("Floppy drive(s): ");
+ else
+ printk(", ");
+ first=0;
+ if (UDP->cmos > 0){
+ allowed_drive_mask |= 1 << drive;
+ printk("fd%d is %s", drive,
+ default_drive_params[(int)UDP->cmos].name);
+ } else
+ printk("fd%d is unknown type %d",drive,
+ UDP->cmos);
+ }
+ }
+ if (!first)
+ printk("\n");
+}
+
+static int floppy_read(struct inode * inode, struct file * filp,
+ char * buf, int count)
+{
+ int drive = DRIVE(inode->i_rdev);
+
+ check_disk_change(inode->i_rdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ return -ENXIO;
+ return block_read(inode, filp, buf, count);
+}
+
+static int floppy_write(struct inode * inode, struct file * filp,
+ const char * buf, int count)
+{
+ int block;
+ int ret;
+ int drive = DRIVE(inode->i_rdev);
+
+ if (!UDRS->maxblock)
+ UDRS->maxblock=1;/* make change detectable */
+ check_disk_change(inode->i_rdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ return -ENXIO;
+ if (!UTESTF(FD_DISK_WRITABLE))
+ return -EROFS;
+ block = (filp->f_pos + count) >> 9;
+ INFBOUND(UDRS->maxblock, block);
+ ret= block_write(inode, filp, buf, count);
+ return ret;
+}
+
+static void floppy_release(struct inode * inode, struct file * filp)
+{
+ int drive;
+
+ drive = DRIVE(inode->i_rdev);
+
+ if (!filp || (filp->f_mode & (2 | OPEN_WRITE_BIT)))
+ /* if the file is mounted OR (writable now AND writable at
+ * open time) Linus: Does this cover all cases? */
+ block_fsync(inode,filp);
+
+ if (UDRS->fd_ref < 0)
+ UDRS->fd_ref=0;
+ else if (!UDRS->fd_ref--) {
+ DPRINT("floppy_release with fd_ref == 0");
+ UDRS->fd_ref = 0;
+ }
+ floppy_release_irq_and_dma();
+}
+
+/*
+ * floppy_open check for aliasing (/dev/fd0 can be the same as
+ * /dev/PS0 etc), and disallows simultaneous access to the same
+ * drive with different device numbers.
+ */
+#define RETERR(x) do{floppy_release(inode,filp); return -(x);}while(0)
+
+static int floppy_open(struct inode * inode, struct file * filp)
+{
+ int drive;
+ int old_dev;
+ int try;
+ char *tmp;
+
+ if (!filp) {
+ DPRINT("Weird, open called with filp=0\n");
+ return -EIO;
+ }
+
+ drive = DRIVE(inode->i_rdev);
+ if (drive >= N_DRIVE ||
+ !(allowed_drive_mask & (1 << drive)) ||
+ fdc_state[FDC(drive)].version == FDC_NONE)
+ return -ENXIO;
+
+ if (TYPE(inode->i_rdev) >= NUMBER(floppy_type))
+ return -ENXIO;
+ old_dev = UDRS->fd_device;
+ if (UDRS->fd_ref && old_dev != MINOR(inode->i_rdev))
+ return -EBUSY;
+
+ if (!UDRS->fd_ref && (UDP->flags & FD_BROKEN_DCL)){
+ USETF(FD_DISK_CHANGED);
+ USETF(FD_VERIFY);
+ }
+
+ if (UDRS->fd_ref == -1 ||
+ (UDRS->fd_ref && (filp->f_flags & O_EXCL)))
+ return -EBUSY;
+
+ if (floppy_grab_irq_and_dma())
+ return -EBUSY;
+
+ if (filp->f_flags & O_EXCL)
+ UDRS->fd_ref = -1;
+ else
+ UDRS->fd_ref++;
+
+ if (!floppy_track_buffer){
+ /* if opening an ED drive, reserve a big buffer,
+ * else reserve a small one */
+ if ((UDP->cmos == 6) || (UDP->cmos == 5))
+ try = 64; /* Only 48 actually useful */
+ else
+ try = 32; /* Only 24 actually useful */
+
+ tmp=(char *)dma_mem_alloc(1024 * try);
+ if (!tmp) {
+ try >>= 1; /* buffer only one side */
+ INFBOUND(try, 16);
+ tmp= (char *)dma_mem_alloc(1024*try);
+ }
+ if (!tmp) {
+ DPRINT("Unable to allocate DMA memory\n");
+ RETERR(ENXIO);
+ }
+ if (floppy_track_buffer){
+ free_pages((unsigned long)tmp,__get_order(try*1024));
+ }else {
+ buffer_min = buffer_max = -1;
+ floppy_track_buffer = tmp;
+ max_buffer_sectors = try;
+ }
+ }
+
+ UDRS->fd_device = MINOR(inode->i_rdev);
+ if (old_dev != -1 && old_dev != MINOR(inode->i_rdev)) {
+ if (buffer_drive == drive)
+ buffer_track = -1;
+ invalidate_buffers(MKDEV(FLOPPY_MAJOR,old_dev));
+ }
+
+ /* Allow ioctls if we have write-permissions even if read-only open */
+ if ((filp->f_mode & 2) || (permission(inode,2) == 0))
+ filp->f_mode |= IOCTL_MODE_BIT;
+ if (filp->f_mode & 2)
+ filp->f_mode |= OPEN_WRITE_BIT;
+
+ if (UFDCS->rawcmd == 1)
+ UFDCS->rawcmd = 2;
+
+ if (filp->f_flags & O_NDELAY)
+ return 0;
+ if (filp->f_mode & 3) {
+ UDRS->last_checked = 0;
+ check_disk_change(inode->i_rdev);
+ if (UTESTF(FD_DISK_CHANGED))
+ RETERR(ENXIO);
+ }
+ if ((filp->f_mode & 2) && !(UTESTF(FD_DISK_WRITABLE)))
+ RETERR(EROFS);
+ return 0;
+#undef RETERR
+}
+
+/*
+ * Check if the disk has been changed or if a change has been faked.
+ */
+static int check_floppy_change(kdev_t dev)
+{
+ int drive = DRIVE(dev);
+
+ if (MAJOR(dev) != MAJOR_NR) {
+ DPRINT("floppy_changed: not a floppy\n");
+ return 0;
+ }
+
+ if (UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY))
+ return 1;
+
+ if (UDRS->last_checked + UDP->checkfreq < jiffies){
+ lock_fdc(drive,0);
+ poll_drive(0,0);
+ process_fd_request();
+ }
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ (!TYPE(dev) && !current_type[drive]))
+ return 1;
+ return 0;
+}
+
+/* revalidate the floppy disk, i.e. trigger format autodetection by reading
+ * the bootblock (block 0). "Autodetection" is also needed to check whether
+ * there is a disk in the drive at all... Thus we also do it for fixed
+ * geometry formats */
+static int floppy_revalidate(kdev_t dev)
+{
+#define NO_GEOM (!current_type[drive] && !TYPE(dev))
+ struct buffer_head * bh;
+ int drive=DRIVE(dev);
+ int cf;
+
+ if (UTESTF(FD_DISK_CHANGED) ||
+ UTESTF(FD_VERIFY) ||
+ test_bit(drive, &fake_change) ||
+ NO_GEOM){
+ lock_fdc(drive,0);
+ cf = UTESTF(FD_DISK_CHANGED) || UTESTF(FD_VERIFY);
+ if (!(cf || test_bit(drive, &fake_change) || NO_GEOM)){
+ process_fd_request(); /*already done by another thread*/
+ return 0;
+ }
+ UDRS->maxblock = 0;
+ UDRS->maxtrack = 0;
+ if (buffer_drive == drive)
+ buffer_track = -1;
+ clear_bit(drive, &fake_change);
+ UCLEARF(FD_DISK_CHANGED);
+ if (cf)
+ UDRS->generation++;
+ if (NO_GEOM){
+ /* auto-sensing */
+ int size = floppy_blocksizes[MINOR(dev)];
+ if (!size)
+ size = 1024;
+ if (!(bh = getblk(dev,0,size))){
+ process_fd_request();
+ return 1;
+ }
+ if (bh && !buffer_uptodate(bh))
+ ll_rw_block(READ, 1, &bh);
+ process_fd_request();
+ wait_on_buffer(bh);
+ brelse(bh);
+ return 0;
+ }
+ if (cf)
+ poll_drive(0, FD_RAW_NEED_DISK);
+ process_fd_request();
+ }
+ return 0;
+}
+
+static struct file_operations floppy_fops = {
+ NULL, /* lseek - default */
+ floppy_read, /* read - general block-dev read */
+ floppy_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ fd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ floppy_open, /* open */
+ floppy_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_floppy_change, /* media_change */
+ floppy_revalidate, /* revalidate */
+};
+
+/*
+ * Floppy Driver initialisation
+ * =============================
+ */
+
+/* Determine the floppy disk controller type */
+/* This routine was written by David C. Niemi */
+static char get_fdc_version(void)
+{
+ int r;
+
+ output_byte(FD_DUMPREGS); /* 82072 and better know DUMPREGS */
+ if (FDCS->reset)
+ return FDC_NONE;
+ if ((r = result()) <= 0x00)
+ return FDC_NONE; /* No FDC present ??? */
+ if ((r==1) && (reply_buffer[0] == 0x80)){
+ printk("FDC %d is a 8272A\n",fdc);
+ return FDC_8272A; /* 8272a/765 don't know DUMPREGS */
+ }
+ if (r != 10) {
+ printk("FDC %d init: DUMPREGS: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ output_byte(FD_VERSION);
+ r = result();
+ if ((r == 1) && (reply_buffer[0] == 0x80)){
+ printk("FDC %d is a 82072\n",fdc);
+ return FDC_82072; /* 82072 doesn't know VERSION */
+ }
+ if ((r != 1) || (reply_buffer[0] != 0x90)) {
+ printk("FDC %d init: VERSION: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ output_byte(FD_UNLOCK);
+ r = result();
+ if ((r == 1) && (reply_buffer[0] == 0x80)){
+ printk("FDC %d is a pre-1991 82077\n", fdc);
+ return FDC_82077_ORIG; /* Pre-1991 82077 doesn't know LOCK/UNLOCK */
+ }
+ if ((r != 1) || (reply_buffer[0] != 0x00)) {
+ printk("FDC %d init: UNLOCK: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ output_byte(FD_PARTID);
+ r = result();
+ if (r != 1) {
+ printk("FDC %d init: PARTID: unexpected return of %d bytes.\n",
+ fdc, r);
+ return FDC_UNKNOWN;
+ }
+ if (reply_buffer[0] == 0x80) {
+ printk("FDC %d is a post-1991 82077\n",fdc);
+ return FDC_82077; /* Revised 82077AA passes all the tests */
+ }
+ switch (reply_buffer[0] >> 5) {
+ case 0x0:
+ output_byte(FD_SAVE);
+ r = result();
+ if (r != 16) {
+ printk("FDC %d init: SAVE: unexpected return of %d bytes.\n", fdc, r);
+ return FDC_UNKNOWN;
+ }
+ if (!(reply_buffer[0] & 0x40)) {
+ printk("FDC %d is a 3Volt 82078SL.\n",fdc);
+ return FDC_82078;
+ }
+ /* Either a 82078-1 or a 82078SL running at 5Volt */
+ printk("FDC %d is a 82078-1.\n",fdc);
+ return FDC_82078_1;
+ case 0x1:
+ printk("FDC %d is a 44pin 82078\n",fdc);
+ return FDC_82078;
+ case 0x2:
+ printk("FDC %d is a S82078B\n", fdc);
+ return FDC_S82078B;
+ case 0x3:
+ printk("FDC %d is a National Semiconductor PC87306\n", fdc);
+ return FDC_87306;
+ default:
+ printk("FDC %d init: 82077 variant with PARTID=%d.\n",
+ fdc, reply_buffer[0] >> 5);
+ return FDC_82077_UNKN;
+ }
+} /* get_fdc_version */
+
+/* lilo configuration */
+
+/* we make the invert_dcl function global. One day, somebody might
+ * want to centralize all thinkpad related options into one lilo option,
+ * there are just so many thinkpad related quirks! */
+void floppy_invert_dcl(int *ints,int param)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param)
+ default_drive_params[i].params.flags |= 0x80;
+ else
+ default_drive_params[i].params.flags &= ~0x80;
+ }
+ DPRINT("Configuring drives for inverted dcl\n");
+}
+
+static void daring(int *ints,int param)
+{
+ int i;
+
+ for (i=0; i < ARRAY_SIZE(default_drive_params); i++){
+ if (param){
+ default_drive_params[i].params.select_delay = 0;
+ default_drive_params[i].params.flags |= FD_SILENT_DCL_CLEAR;
+ } else {
+ default_drive_params[i].params.select_delay = 2*HZ/100;
+ default_drive_params[i].params.flags &= ~FD_SILENT_DCL_CLEAR;
+ }
+ }
+ DPRINT1("Assuming %s floppy hardware\n", param ? "standard" : "broken");
+}
+
+static void allow_drives(int *ints, int param)
+{
+ allowed_drive_mask=param;
+ DPRINT1("setting allowed_drive_mask to 0x%x\n", param);
+}
+
+static void fdc2_adr(int *ints, int param)
+{
+ FDC2 = param;
+ if (param)
+ DPRINT1("enabling second fdc at address 0x%3x\n", FDC2);
+ else
+ DPRINT("disabling second fdc\n");
+}
+
+static void unex(int *ints,int param)
+{
+ print_unex = param;
+ DPRINT1("%sprinting messages for unexpected interrupts\n",
+ param ? "" : "not ");
+}
+
+static void set_cmos(int *ints, int dummy)
+{
+ int current_drive=0;
+
+ if (ints[0] != 2){
+ DPRINT("wrong number of parameter for cmos\n");
+ return;
+ }
+ current_drive = ints[1];
+ if (current_drive < 0 || current_drive >= 8){
+ DPRINT("bad drive for set_cmos\n");
+ return;
+ }
+ if (current_drive >= 4 && !FDC2)
+ fdc2_adr(0, 0x370);
+ if (ints[2] <= 0 || ints[2] >= NUMBER(default_drive_params)){
+ DPRINT1("bad cmos code %d\n", ints[2]);
+ return;
+ }
+ DP->cmos = ints[2];
+ DPRINT1("setting cmos code to %d\n", ints[2]);
+}
+
+static struct param_table {
+ const char *name;
+ void (*fn)(int *ints, int param);
+ int def_param;
+} config_params[]={
+ { "allowed_drive_mask", allow_drives, 0xff },
+ { "all_drives", allow_drives, 0xff },
+ { "asus_pci", allow_drives, 0x33 },
+
+ { "daring", daring, 1},
+
+ { "two_fdc", fdc2_adr, 0x370 },
+ { "one_fdc", fdc2_adr, 0 },
+
+ { "thinkpad", floppy_invert_dcl, 1 },
+
+ { "cmos", set_cmos, 0 },
+
+ { "unexpected_interrupts", unex, 1 },
+ { "no_unexpected_interrupts", unex, 0 },
+ { "L40SX", unex, 0 } };
+
+#define FLOPPY_SETUP
+void floppy_setup(char *str, int *ints)
+{
+ int i;
+ int param;
+ if (str)
+ for (i=0; i< ARRAY_SIZE(config_params); i++){
+ if (strcmp(str,config_params[i].name) == 0){
+ if (ints[0])
+ param = ints[1];
+ else
+ param = config_params[i].def_param;
+ config_params[i].fn(ints,param);
+ return;
+ }
+ }
+ if (str) {
+ DPRINT1("unknown floppy option [%s]\n", str);
+
+ DPRINT("allowed options are:");
+ for (i=0; i< ARRAY_SIZE(config_params); i++)
+ printk(" %s",config_params[i].name);
+ printk("\n");
+ } else
+ DPRINT("botched floppy option\n");
+ DPRINT("Read linux/drivers/block/README.fd\n");
+}
+
+int floppy_init(void)
+{
+ int i,unit,drive;
+ int have_no_fdc= -EIO;
+
+ raw_cmd = 0;
+
+ sti();
+
+ if (register_blkdev(MAJOR_NR,"fd",&floppy_fops)) {
+ printk("Unable to get major %d for floppy\n",MAJOR_NR);
+ return -EBUSY;
+ }
+
+ for (i=0; i<256; i++)
+ if (ITYPE(i))
+ floppy_sizes[i] = floppy_type[ITYPE(i)].size >> 1;
+ else
+ floppy_sizes[i] = MAX_DISK_SIZE;
+
+ blk_size[MAJOR_NR] = floppy_sizes;
+ blksize_size[MAJOR_NR] = floppy_blocksizes;
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ reschedule_timeout(MAXTIMEOUT, "floppy init", MAXTIMEOUT);
+ config_types();
+
+ for (i = 0; i < N_FDC; i++) {
+ fdc = i;
+ CLEARSTRUCT(FDCS);
+ FDCS->dtr = -1;
+ FDCS->dor = 0x4;
+ }
+
+ fdc_state[0].address = FDC1;
+#if N_FDC > 1
+ fdc_state[1].address = FDC2;
+#endif
+
+ if (floppy_grab_irq_and_dma()){
+ unregister_blkdev(MAJOR_NR,"fd");
+ return -EBUSY;
+ }
+
+ /* initialise drive state */
+ for (drive = 0; drive < N_DRIVE; drive++) {
+ CLEARSTRUCT(UDRS);
+ CLEARSTRUCT(UDRWE);
+ UDRS->flags = FD_VERIFY | FD_DISK_NEWCHANGE | FD_DISK_CHANGED;
+ UDRS->fd_device = -1;
+ floppy_track_buffer = NULL;
+ max_buffer_sectors = 0;
+ }
+
+ for (i = 0; i < N_FDC; i++) {
+ fdc = i;
+ FDCS->driver_version = FD_DRIVER_VERSION;
+ for (unit=0; unit<4; unit++)
+ FDCS->track[unit] = 0;
+ if (FDCS->address == -1)
+ continue;
+ FDCS->rawcmd = 2;
+ if (user_reset_fdc(-1,FD_RESET_ALWAYS,0)){
+ FDCS->address = -1;
+ continue;
+ }
+ /* Try to determine the floppy controller type */
+ FDCS->version = get_fdc_version();
+ if (FDCS->version == FDC_NONE){
+ FDCS->address = -1;
+ continue;
+ }
+
+ request_region(FDCS->address, 6, "floppy");
+ request_region(FDCS->address+7, 1, "floppy DIR");
+ /* address + 6 is reserved, and may be taken by IDE.
+ * Unfortunately, Adaptec doesn't know this :-(, */
+
+ have_no_fdc = 0;
+ /* Not all FDCs seem to be able to handle the version command
+ * properly, so force a reset for the standard FDC clones,
+ * to avoid interrupt garbage.
+ */
+ FDCS->has_fifo = FDCS->version >= FDC_82077_ORIG;
+ user_reset_fdc(-1,FD_RESET_ALWAYS,0);
+ }
+ fdc=0;
+ del_timer(&fd_timeout);
+ current_drive = 0;
+ floppy_release_irq_and_dma();
+ initialising=0;
+ if (have_no_fdc) {
+ DPRINT("no floppy controllers found\n");
+ unregister_blkdev(MAJOR_NR,"fd");
+ } else
+ virtual_dma_init();
+ return have_no_fdc;
+}
+
+static int floppy_grab_irq_and_dma(void)
+{
+ int i;
+ cli();
+ if (usage_count++){
+ sti();
+ return 0;
+ }
+ sti();
+ MOD_INC_USE_COUNT;
+ for (i=0; i< N_FDC; i++){
+ if (FDCS->address != -1){
+ fdc = i;
+ reset_fdc_info(1);
+ fd_outb(FDCS->dor, FD_DOR);
+ }
+ }
+ set_dor(0, ~0, 8); /* avoid immediate interrupt */
+
+ if (fd_request_irq()) {
+ DPRINT1("Unable to grab IRQ%d for the floppy driver\n",
+ FLOPPY_IRQ);
+ return -1;
+ }
+ if (fd_request_dma()) {
+ DPRINT1("Unable to grab DMA%d for the floppy driver\n",
+ FLOPPY_DMA);
+ fd_free_irq();
+ return -1;
+ }
+ for (fdc = 0; fdc < N_FDC; fdc++)
+ if (FDCS->address != -1)
+ fd_outb(FDCS->dor, FD_DOR);
+ fdc = 0;
+ fd_enable_irq();
+ return 0;
+}
+
+static void floppy_release_irq_and_dma(void)
+{
+#ifdef FLOPPY_SANITY_CHECK
+ int drive;
+#endif
+ long tmpsize;
+ void *tmpaddr;
+
+ cli();
+ if (--usage_count){
+ sti();
+ return;
+ }
+ sti();
+ MOD_DEC_USE_COUNT;
+ fd_disable_dma();
+ fd_free_dma();
+ fd_disable_irq();
+ fd_free_irq();
+
+ set_dor(0, ~0, 8);
+#if N_FDC > 1
+ set_dor(1, ~8, 0);
+#endif
+ floppy_enable_hlt();
+
+ if (floppy_track_buffer && max_buffer_sectors) {
+ tmpsize = max_buffer_sectors*1024;
+ tmpaddr = (void *)floppy_track_buffer;
+ floppy_track_buffer = 0;
+ max_buffer_sectors = 0;
+ buffer_min = buffer_max = -1;
+ free_pages((unsigned long)tmpaddr, __get_order(tmpsize));
+ }
+
+#ifdef FLOPPY_SANITY_CHECK
+ for (drive=0; drive < N_FDC * 4; drive++)
+ if (motor_off_timer[drive].next)
+ printk("motor off timer %d still active\n", drive);
+
+ if (fd_timeout.next)
+ printk("floppy timer still active:%s\n", timeout_message);
+ if (fd_timer.next)
+ printk("auxiliary floppy timer still active\n");
+ if (floppy_tq.sync)
+ printk("task queue still active\n");
+#endif
+}
+
+
+#ifdef MODULE
+
+extern char *get_options(char *str, int *ints);
+
+static void mod_setup(char *pattern, void (*setup)(char *, int *))
+{
+ int i;
+ char c;
+ int j;
+ int match;
+ char buffer[100];
+ int ints[11];
+ int length = strlen(pattern)+1;
+
+ match=0;
+ j=1;
+
+ for (i=current->mm->env_start; i< current->mm->env_end; i ++){
+ c= get_fs_byte(i);
+ if (match){
+ if (j==99)
+ c='\0';
+ buffer[j] = c;
+ if (!c || c == ' ' || c == '\t'){
+ if (j){
+ buffer[j] = '\0';
+ setup(get_options(buffer,ints),ints);
+ }
+ j=0;
+ } else
+ j++;
+ if (!c)
+ break;
+ continue;
+ }
+ if ((!j && !c) || (j && c == pattern[j-1]))
+ j++;
+ else
+ j=0;
+ if (j==length){
+ match=1;
+ j=0;
+ }
+ }
+}
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+int init_module(void)
+{
+ printk("inserting floppy driver for %s\n", kernel_version);
+
+ mod_setup("floppy=", floppy_setup);
+
+ return floppy_init();
+}
+
+void cleanup_module(void)
+{
+ int fdc;
+
+ for (fdc=0; fdc<2; fdc++)
+ if (FDCS->address != -1){
+ release_region(FDCS->address, 6);
+ release_region(FDCS->address+7, 1);
+ }
+
+ unregister_blkdev(MAJOR_NR, "fd");
+
+ blk_dev[MAJOR_NR].request_fn = 0;
+}
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/block/genhd.c b/i386/i386at/gpl/linux/block/genhd.c
new file mode 100644
index 00000000..60cba6c1
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/genhd.c
@@ -0,0 +1,610 @@
+/*
+ * Code extracted from
+ * linux/kernel/hd.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ *
+ *
+ * Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * in the early extended-partition checks and added DM partitions
+ *
+ * Support for DiskManager v6.0x added by Mark Lord (mlord@bnr.ca)
+ * with information provided by OnTrack. This now works for linux fdisk
+ * and LILO, as well as loadlin and bootln. Note that disks other than
+ * /dev/hda *must* have a "DOS" type 0x51 partition in the first slot (hda1).
+ *
+ * More flexible handling of extended partitions - aeb, 950831
+ *
+ * Check partition table on IDE disks for common CHS translations
+ */
+
+#include <linux/config.h>
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/major.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+
+#ifdef __alpha__
+/*
+ * On the Alpha, we get unaligned access exceptions on
+ * p->nr_sects and p->start_sect, when the partition table
+ * is not on a 4-byte boundary, which is frequently the case.
+ * This code uses unaligned load instructions to prevent
+ * such exceptions.
+ */
+#include <asm/unaligned.h>
+#define NR_SECTS(p) ldl_u(&p->nr_sects)
+#define START_SECT(p) ldl_u(&p->start_sect)
+#else /* __alpha__ */
+#define NR_SECTS(p) p->nr_sects
+#define START_SECT(p) p->start_sect
+#endif /* __alpha__ */
+
+#ifdef MACH
+#include <i386/ipl.h>
+#endif
+
+struct gendisk *gendisk_head = NULL;
+
+static int current_minor = 0;
+extern int *blk_size[];
+extern void rd_load(void);
+
+extern int chr_dev_init(void);
+extern int blk_dev_init(void);
+extern int scsi_dev_init(void);
+extern int net_dev_init(void);
+
+static void print_minor_name (struct gendisk *hd, int minor)
+{
+ unsigned int unit = minor >> hd->minor_shift;
+ unsigned int part = minor & ((1 << hd->minor_shift) - 1);
+
+#ifdef CONFIG_BLK_DEV_IDE
+ /*
+ * IDE devices use multiple major numbers, but the drives
+ * are named as: {hda,hdb}, {hdc,hdd}, {hde,hdf}, {hdg,hdh}..
+ * This requires some creative handling here to find the
+ * correct name to use, with some help from ide.c
+ */
+ if (!strcmp(hd->major_name,"ide")) {
+ char name[16]; /* more than large enough */
+ strcpy(name, hd->real_devices); /* courtesy ide.c */
+ name[strlen(name)-1] += unit;
+ printk(" %s", name);
+ } else
+#endif
+ printk(" %s%c", hd->major_name, 'a' + unit);
+ if (part)
+ printk("%d", part);
+ else
+ printk(":");
+}
+
+static void add_partition (struct gendisk *hd, int minor, int start, int size)
+{
+ hd->part[minor].start_sect = start;
+ hd->part[minor].nr_sects = size;
+ print_minor_name(hd, minor);
+}
+
+static inline int is_extended_partition(struct partition *p)
+{
+ return (p->sys_ind == DOS_EXTENDED_PARTITION ||
+ p->sys_ind == LINUX_EXTENDED_PARTITION);
+}
+
+#ifdef CONFIG_MSDOS_PARTITION
+/*
+ * Create devices for each logical partition in an extended partition.
+ * The logical partitions form a linked list, with each entry being
+ * a partition table with two entries. The first entry
+ * is the real data partition (with a start relative to the partition
+ * table start). The second is a pointer to the next logical partition
+ * (with a start relative to the entire extended partition).
+ * We do not create a Linux partition for the partition tables, but
+ * only for the actual data partitions.
+ */
+
+static void extended_partition(struct gendisk *hd, kdev_t dev)
+{
+ struct buffer_head *bh;
+ struct partition *p;
+ unsigned long first_sector, first_size, this_sector, this_size;
+ int mask = (1 << hd->minor_shift) - 1;
+ int i;
+
+ first_sector = hd->part[MINOR(dev)].start_sect;
+ first_size = hd->part[MINOR(dev)].nr_sects;
+ this_sector = first_sector;
+
+ while (1) {
+ if ((current_minor & mask) == 0)
+ return;
+ if (!(bh = bread(dev,0,1024)))
+ return;
+ /*
+ * This block is from a device that we're about to stomp on.
+ * So make sure nobody thinks this block is usable.
+ */
+ bh->b_state = 0;
+
+ if (*(unsigned short *) (bh->b_data+510) != 0xAA55)
+ goto done;
+
+ p = (struct partition *) (0x1BE + bh->b_data);
+
+ this_size = hd->part[MINOR(dev)].nr_sects;
+
+ /*
+ * Usually, the first entry is the real data partition,
+ * the 2nd entry is the next extended partition, or empty,
+ * and the 3rd and 4th entries are unused.
+ * However, DRDOS sometimes has the extended partition as
+ * the first entry (when the data partition is empty),
+ * and OS/2 seems to use all four entries.
+ */
+
+ /*
+ * First process the data partition(s)
+ */
+ for (i=0; i<4; i++, p++) {
+ if (!NR_SECTS(p) || is_extended_partition(p))
+ continue;
+
+ /* Check the 3rd and 4th entries -
+ these sometimes contain random garbage */
+ if (i >= 2
+ && START_SECT(p) + NR_SECTS(p) > this_size
+ && (this_sector + START_SECT(p) < first_sector ||
+ this_sector + START_SECT(p) + NR_SECTS(p) >
+ first_sector + first_size))
+ continue;
+
+ add_partition(hd, current_minor, this_sector+START_SECT(p), NR_SECTS(p));
+ current_minor++;
+ if ((current_minor & mask) == 0)
+ goto done;
+ }
+ /*
+ * Next, process the (first) extended partition, if present.
+ * (So far, there seems to be no reason to make
+ * extended_partition() recursive and allow a tree
+ * of extended partitions.)
+ * It should be a link to the next logical partition.
+ * Create a minor for this just long enough to get the next
+ * partition table. The minor will be reused for the next
+ * data partition.
+ */
+ p -= 4;
+ for (i=0; i<4; i++, p++)
+ if(NR_SECTS(p) && is_extended_partition(p))
+ break;
+ if (i == 4)
+ goto done; /* nothing left to do */
+
+ hd->part[current_minor].nr_sects = NR_SECTS(p);
+ hd->part[current_minor].start_sect = first_sector + START_SECT(p);
+ this_sector = first_sector + START_SECT(p);
+ dev = MKDEV(hd->major, current_minor);
+ brelse(bh);
+ }
+done:
+ brelse(bh);
+}
+
+static int msdos_partition(struct gendisk *hd, kdev_t dev, unsigned long first_sector)
+{
+ int i, minor = current_minor;
+ struct buffer_head *bh;
+ struct partition *p;
+ unsigned char *data;
+ int mask = (1 << hd->minor_shift) - 1;
+#ifdef CONFIG_BLK_DEV_IDE
+ int tested_for_xlate = 0;
+
+read_mbr:
+#endif
+ if (!(bh = bread(dev,0,1024))) {
+ printk(" unable to read partition table\n");
+ return -1;
+ }
+ data = bh->b_data;
+ /* In some cases we modify the geometry */
+ /* of the drive (below), so ensure that */
+ /* nobody else tries to re-use this data. */
+ bh->b_state = 0;
+#ifdef CONFIG_BLK_DEV_IDE
+check_table:
+#endif
+ if (*(unsigned short *) (0x1fe + data) != 0xAA55) {
+ brelse(bh);
+ return 0;
+ }
+ p = (struct partition *) (0x1be + data);
+
+#ifdef CONFIG_BLK_DEV_IDE
+ if (!tested_for_xlate++) { /* Do this only once per disk */
+ /*
+ * Look for various forms of IDE disk geometry translation
+ */
+ extern int ide_xlate_1024(kdev_t, int, const char *);
+ unsigned int sig = *(unsigned short *)(data + 2);
+ if (p->sys_ind == EZD_PARTITION) {
+ /*
+ * The remainder of the disk must be accessed using
+ * a translated geometry that reduces the number of
+ * apparent cylinders to less than 1024 if possible.
+ *
+ * ide_xlate_1024() will take care of the necessary
+ * adjustments to fool fdisk/LILO and partition check.
+ */
+ if (ide_xlate_1024(dev, -1, " [EZD]")) {
+ data += 512;
+ goto check_table;
+ }
+ } else if (p->sys_ind == DM6_PARTITION) {
+
+ /*
+ * Everything on the disk is offset by 63 sectors,
+ * including a "new" MBR with its own partition table,
+ * and the remainder of the disk must be accessed using
+ * a translated geometry that reduces the number of
+ * apparent cylinders to less than 1024 if possible.
+ *
+ * ide_xlate_1024() will take care of the necessary
+ * adjustments to fool fdisk/LILO and partition check.
+ */
+ if (ide_xlate_1024(dev, 1, " [DM6:DDO]")) {
+ brelse(bh);
+ goto read_mbr; /* start over with new MBR */
+ }
+ } else if (sig <= 0x1ae && *(unsigned short *)(data + sig) == 0x55AA
+ && (1 & *(unsigned char *)(data + sig + 2)) )
+ {
+ /*
+ * DM6 signature in MBR, courtesy of OnTrack
+ */
+ (void) ide_xlate_1024 (dev, 0, " [DM6:MBR]");
+ } else if (p->sys_ind == DM6_AUX1PARTITION || p->sys_ind == DM6_AUX3PARTITION) {
+ /*
+ * DM6 on other than the first (boot) drive
+ */
+ (void) ide_xlate_1024(dev, 0, " [DM6:AUX]");
+ } else {
+ /*
+ * Examine the partition table for common translations.
+ * This is necessary for drives for situations where
+ * the translated geometry is unavailable from the BIOS.
+ */
+ for (i = 0; i < 4 ; i++) {
+ struct partition *q = &p[i];
+ if (NR_SECTS(q) && q->sector == 1 && q->end_sector == 63) {
+ unsigned int heads = q->end_head + 1;
+ if (heads == 32 || heads == 64 || heads == 128) {
+
+ (void) ide_xlate_1024(dev, heads, " [PTBL]");
+ break;
+ }
+ }
+ }
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDE */
+
+ current_minor += 4; /* first "extra" minor (for extended partitions) */
+ for (i=1 ; i<=4 ; minor++,i++,p++) {
+ if (!NR_SECTS(p))
+ continue;
+ add_partition(hd, minor, first_sector+START_SECT(p), NR_SECTS(p));
+ if (is_extended_partition(p)) {
+ printk(" <");
+ /*
+ * If we are rereading the partition table, we need
+ * to set the size of the partition so that we will
+ * be able to bread the block containing the extended
+ * partition info.
+ */
+ hd->sizes[minor] = hd->part[minor].nr_sects
+ >> (BLOCK_SIZE_BITS - 9);
+ extended_partition(hd, MKDEV(hd->major, minor));
+ printk(" >");
+ /* prevent someone doing mkfs or mkswap on an
+ extended partition, but leave room for LILO */
+ if (hd->part[minor].nr_sects > 2)
+ hd->part[minor].nr_sects = 2;
+ }
+ }
+ /*
+ * Check for old-style Disk Manager partition table
+ */
+ if (*(unsigned short *) (data+0xfc) == 0x55AA) {
+ p = (struct partition *) (0x1be + data);
+ for (i = 4 ; i < 16 ; i++, current_minor++) {
+ p--;
+ if ((current_minor & mask) == 0)
+ break;
+ if (!(START_SECT(p) && NR_SECTS(p)))
+ continue;
+ add_partition(hd, current_minor, START_SECT(p), NR_SECTS(p));
+ }
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_MSDOS_PARTITION */
+
+#ifdef CONFIG_OSF_PARTITION
+
+static int osf_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+ int i;
+ int mask = (1 << hd->minor_shift) - 1;
+ struct buffer_head *bh;
+ struct disklabel {
+ u32 d_magic;
+ u16 d_type,d_subtype;
+ u8 d_typename[16];
+ u8 d_packname[16];
+ u32 d_secsize;
+ u32 d_nsectors;
+ u32 d_ntracks;
+ u32 d_ncylinders;
+ u32 d_secpercyl;
+ u32 d_secprtunit;
+ u16 d_sparespertrack;
+ u16 d_sparespercyl;
+ u32 d_acylinders;
+ u16 d_rpm, d_interleave, d_trackskew, d_cylskew;
+ u32 d_headswitch, d_trkseek, d_flags;
+ u32 d_drivedata[5];
+ u32 d_spare[5];
+ u32 d_magic2;
+ u16 d_checksum;
+ u16 d_npartitions;
+ u32 d_bbsize, d_sbsize;
+ struct d_partition {
+ u32 p_size;
+ u32 p_offset;
+ u32 p_fsize;
+ u8 p_fstype;
+ u8 p_frag;
+ u16 p_cpg;
+ } d_partitions[8];
+ } * label;
+ struct d_partition * partition;
+#define DISKLABELMAGIC (0x82564557UL)
+
+ if (!(bh = bread(dev,0,1024))) {
+ printk("unable to read partition table\n");
+ return -1;
+ }
+ label = (struct disklabel *) (bh->b_data+64);
+ partition = label->d_partitions;
+ if (label->d_magic != DISKLABELMAGIC) {
+ printk("magic: %08x\n", label->d_magic);
+ brelse(bh);
+ return 0;
+ }
+ if (label->d_magic2 != DISKLABELMAGIC) {
+ printk("magic2: %08x\n", label->d_magic2);
+ brelse(bh);
+ return 0;
+ }
+ for (i = 0 ; i < label->d_npartitions; i++, partition++) {
+ if ((current_minor & mask) == 0)
+ break;
+ if (partition->p_size)
+ add_partition(hd, current_minor,
+ first_sector+partition->p_offset,
+ partition->p_size);
+ current_minor++;
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_OSF_PARTITION */
+
+#ifdef CONFIG_SUN_PARTITION
+
+static int sun_partition(struct gendisk *hd, unsigned int dev, unsigned long first_sector)
+{
+ int i, csum;
+ unsigned short *ush;
+ struct buffer_head *bh;
+ struct sun_disklabel {
+ unsigned char info[128]; /* Informative text string */
+ unsigned char spare[292]; /* Boot information etc. */
+ unsigned short rspeed; /* Disk rotational speed */
+ unsigned short pcylcount; /* Physical cylinder count */
+ unsigned short sparecyl; /* extra sects per cylinder */
+ unsigned char spare2[4]; /* More magic... */
+ unsigned short ilfact; /* Interleave factor */
+ unsigned short ncyl; /* Data cylinder count */
+ unsigned short nacyl; /* Alt. cylinder count */
+ unsigned short ntrks; /* Tracks per cylinder */
+ unsigned short nsect; /* Sectors per track */
+ unsigned char spare3[4]; /* Even more magic... */
+ struct sun_partition {
+ unsigned long start_cylinder;
+ unsigned long num_sectors;
+ } partitions[8];
+ unsigned short magic; /* Magic number */
+ unsigned short csum; /* Label xor'd checksum */
+ } * label;
+ struct sun_partition *p;
+ unsigned long spc;
+#define SUN_LABEL_MAGIC 0xDABE
+
+ if(!(bh = bread(dev, 0, 1024))) {
+ printk("Dev %d: unable to read partition table\n", dev);
+ return -1;
+ }
+ label = (struct sun_disklabel *) bh->b_data;
+ p = label->partitions;
+ if(label->magic != SUN_LABEL_MAGIC) {
+ printk("Dev %d Sun disklabel: bad magic %08x\n", dev, label->magic);
+ brelse(bh);
+ return 0;
+ }
+ /* Look at the checksum */
+ ush = ((unsigned short *) (label+1)) - 1;
+ for(csum = 0; ush >= ((unsigned short *) label);)
+ csum ^= *ush--;
+ if(csum) {
+ printk("Dev %d Sun disklabel: Csum bad, label corrupted\n", dev);
+ brelse(bh);
+ return 0;
+ }
+ /* All Sun disks have 8 partition entries */
+ spc = (label->ntrks * label->nsect);
+ for(i=0; i < 8; i++, p++) {
+ unsigned long st_sector;
+
+ /* We register all partitions, even if zero size, so that
+ * the minor numbers end up ok as per SunOS interpretation.
+ */
+ st_sector = first_sector + (p->start_cylinder * spc);
+ add_partition(hd, current_minor, st_sector, p->num_sectors);
+ current_minor++;
+ }
+ printk("\n");
+ brelse(bh);
+ return 1;
+}
+
+#endif /* CONFIG_SUN_PARTITION */
+
+static void check_partition(struct gendisk *hd, kdev_t dev)
+{
+ static int first_time = 1;
+ unsigned long first_sector;
+
+ if (first_time)
+ printk("Partition check:\n");
+ first_time = 0;
+ first_sector = hd->part[MINOR(dev)].start_sect;
+
+ /*
+ * This is a kludge to allow the partition check to be
+ * skipped for specific drives (e.g. IDE cd-rom drives)
+ */
+ if ((int)first_sector == -1) {
+ hd->part[MINOR(dev)].start_sect = 0;
+ return;
+ }
+
+ printk(" ");
+ print_minor_name(hd, MINOR(dev));
+#ifdef CONFIG_MSDOS_PARTITION
+ if (msdos_partition(hd, dev, first_sector))
+ return;
+#endif
+#ifdef CONFIG_OSF_PARTITION
+ if (osf_partition(hd, dev, first_sector))
+ return;
+#endif
+#ifdef CONFIG_SUN_PARTITION
+ if(sun_partition(hd, dev, first_sector))
+ return;
+#endif
+ printk(" unknown partition table\n");
+}
+
+/* This function is used to re-read partition tables for removable disks.
+ Much of the cleanup from the old partition tables should have already been
+ done */
+
+/* This function will re-read the partition tables for a given device,
+and set things back up again. There are some important caveats,
+however. You must ensure that no one is using the device, and no one
+can start using the device while this function is being executed. */
+
+void resetup_one_dev(struct gendisk *dev, int drive)
+{
+ int i;
+ int first_minor = drive << dev->minor_shift;
+ int end_minor = first_minor + dev->max_p;
+
+ blk_size[dev->major] = NULL;
+ current_minor = 1 + first_minor;
+ check_partition(dev, MKDEV(dev->major, first_minor));
+
+ /*
+ * We need to set the sizes array before we will be able to access
+ * any of the partitions on this device.
+ */
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = first_minor; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ blk_size[dev->major] = dev->sizes;
+ }
+}
+
+static void setup_dev(struct gendisk *dev)
+{
+ int i, drive;
+ int end_minor = dev->max_nr * dev->max_p;
+
+ blk_size[dev->major] = NULL;
+ for (i = 0 ; i < end_minor; i++) {
+ dev->part[i].start_sect = 0;
+ dev->part[i].nr_sects = 0;
+ }
+ dev->init(dev);
+ for (drive = 0 ; drive < dev->nr_real ; drive++) {
+ int first_minor = drive << dev->minor_shift;
+ current_minor = 1 + first_minor;
+ check_partition(dev, MKDEV(dev->major, first_minor));
+ }
+ if (dev->sizes != NULL) { /* optional safeguard in ll_rw_blk.c */
+ for (i = 0; i < end_minor; i++)
+ dev->sizes[i] = dev->part[i].nr_sects >> (BLOCK_SIZE_BITS - 9);
+ blk_size[dev->major] = dev->sizes;
+ }
+}
+
+void device_setup(void)
+{
+ extern void console_map_init(void);
+ struct gendisk *p;
+ int nr=0;
+#ifdef MACH
+ extern int linux_intr_pri;
+
+ linux_intr_pri = SPL5;
+#endif
+
+#ifndef MACH
+ chr_dev_init();
+#endif
+ blk_dev_init();
+ sti();
+#ifdef CONFIG_SCSI
+ scsi_dev_init();
+#endif
+#ifdef CONFIG_INET
+#ifdef MACH
+ linux_intr_pri = SPL6;
+#endif
+ net_dev_init();
+#endif
+#ifndef MACH
+ console_map_init();
+#endif
+
+ for (p = gendisk_head ; p ; p=p->next) {
+ setup_dev(p);
+ nr += p->nr_real;
+ }
+#ifdef CONFIG_BLK_DEV_RAM
+ rd_load();
+#endif
+}
diff --git a/i386/i386at/gpl/linux/block/ide-cd.c b/i386/i386at/gpl/linux/block/ide-cd.c
new file mode 100644
index 00000000..6dc93806
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide-cd.c
@@ -0,0 +1,2770 @@
+/*
+ * linux/drivers/block/ide-cd.c
+ *
+ * 1.00 Oct 31, 1994 -- Initial version.
+ * 1.01 Nov 2, 1994 -- Fixed problem with starting request in
+ * cdrom_check_status.
+ * 1.03 Nov 25, 1994 -- leaving unmask_intr[] as a user-setting (as for disks)
+ * (from mlord) -- minor changes to cdrom_setup()
+ * -- renamed ide_dev_s to ide_drive_t, enable irq on command
+ * 2.00 Nov 27, 1994 -- Generalize packet command interface;
+ * add audio ioctls.
+ * 2.01 Dec 3, 1994 -- Rework packet command interface to handle devices
+ * which send an interrupt when ready for a command.
+ * 2.02 Dec 11, 1994 -- Cache the TOC in the driver.
+ * Don't use SCMD_PLAYAUDIO_TI; it's not included
+ * in the current version of ATAPI.
+ * Try to use LBA instead of track or MSF addressing
+ * when possible.
+ * Don't wait for READY_STAT.
+ * 2.03 Jan 10, 1995 -- Rewrite block read routines to handle block sizes
+ * other than 2k and to move multiple sectors in a
+ * single transaction.
+ * 2.04 Apr 21, 1995 -- Add work-around for Creative Labs CD220E drives.
+ * Thanks to Nick Saw <cwsaw@pts7.pts.mot.com> for
+ * help in figuring this out. Ditto for Acer and
+ * Aztech drives, which seem to have the same problem.
+ * 2.04b May 30, 1995 -- Fix to match changes in ide.c version 3.16 -ml
+ * 2.05 Jun 8, 1995 -- Don't attempt to retry after an illegal request
+ * or data protect error.
+ * Use HWIF and DEV_HWIF macros as in ide.c.
+ * Always try to do a request_sense after
+ * a failed command.
+ * Include an option to give textual descriptions
+ * of ATAPI errors.
+ * Fix a bug in handling the sector cache which
+ * showed up if the drive returned data in 512 byte
+ * blocks (like Pioneer drives). Thanks to
+ * Richard Hirst <srh@gpt.co.uk> for diagnosing this.
+ * Properly supply the page number field in the
+ * MODE_SELECT command.
+ * PLAYAUDIO12 is broken on the Aztech; work around it.
+ * 2.05x Aug 11, 1995 -- lots of data structure renaming/restructuring in ide.c
+ * (my apologies to Scott, but now ide-cd.c is independent)
+ * 3.00 Aug 22, 1995 -- Implement CDROMMULTISESSION ioctl.
+ * Implement CDROMREADAUDIO ioctl (UNTESTED).
+ * Use input_ide_data() and output_ide_data().
+ * Add door locking.
+ * Fix usage count leak in cdrom_open, which happened
+ * when a read-write mount was attempted.
+ * Try to load the disk on open.
+ * Implement CDROMEJECT_SW ioctl (off by default).
+ * Read total cdrom capacity during open.
+ * Rearrange logic in cdrom_decode_status. Issue
+ * request sense commands for failed packet commands
+ * from here instead of from cdrom_queue_packet_command.
+ * Fix a race condition in retrieving error information.
+ * Suppress printing normal unit attention errors and
+ * some drive not ready errors.
+ * Implement CDROMVOLREAD ioctl.
+ * Implement CDROMREADMODE1/2 ioctls.
+ * Fix race condition in setting up interrupt handlers
+ * when the `serialize' option is used.
+ * 3.01 Sep 2, 1995 -- Fix ordering of reenabling interrupts in
+ * cdrom_queue_request.
+ * Another try at using ide_[input,output]_data.
+ * 3.02 Sep 16, 1995 -- Stick total disk capacity in partition table as well.
+ * Make VERBOSE_IDE_CD_ERRORS dump failed command again.
+ * Dump out more information for ILLEGAL REQUEST errs.
+ * Fix handling of errors occuring before the
+ * packet command is transferred.
+ * Fix transfers with odd bytelengths.
+ * 3.03 Oct 27, 1995 -- Some Creative drives have an id of just `CD'.
+ * `DCI-2S10' drives are broken too.
+ * 3.04 Nov 20, 1995 -- So are Vertos drives.
+ * 3.05 Dec 1, 1995 -- Changes to go with overhaul of ide.c and ide-tape.c
+ * 3.06 Dec 16, 1995 -- Add support needed for partitions.
+ * More workarounds for Vertos bugs (based on patches
+ * from Holger Dietze <dietze@aix520.informatik.uni-leipzig.de>).
+ * Try to eliminate byteorder assumptions.
+ * Use atapi_cdrom_subchnl struct definition.
+ * Add STANDARD_ATAPI compilation option.
+ * 3.07 Jan 29, 1996 -- More twiddling for broken drives: Sony 55D,
+ * Vertos 300.
+ * Add NO_DOOR_LOCKING configuration option.
+ * Handle drive_cmd requests w/NULL args (for hdparm -t).
+ * Work around sporadic Sony55e audio play problem.
+ * 3.07a Feb 11, 1996 -- check drive->id for NULL before dereferencing, to fix
+ * problem with "hde=cdrom" with no drive present. -ml
+ *
+ * NOTE: Direct audio reads will only work on some types of drive.
+ * So far, i've received reports of success for Sony and Toshiba drives.
+ *
+ * ATAPI cd-rom driver. To be used with ide.c.
+ *
+ * Copyright (C) 1994, 1995, 1996 scott snyder <snyder@fnald0.fnal.gov>
+ * May be copied or modified under the terms of the GNU General Public License
+ * (../../COPYING).
+ */
+
+
+/***************************************************************************/
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/cdrom.h>
+#include <asm/irq.h>
+#include <asm/io.h>
+#include <asm/byteorder.h>
+#include <asm/segment.h>
+#ifdef __alpha__
+# include <asm/unaligned.h>
+#endif
+
+#include "ide.h"
+
+
+
+/* Turn this on to have the driver print out the meanings of the
+ ATAPI error codes. This will use up additional kernel-space
+ memory, though. */
+
+#ifndef VERBOSE_IDE_CD_ERRORS
+#define VERBOSE_IDE_CD_ERRORS 0
+#endif
+
+
+/* Turning this on will remove code to work around various nonstandard
+ ATAPI implementations. If you know your drive follows the standard,
+ this will give you a slightly smaller kernel. */
+
+#ifndef STANDARD_ATAPI
+#define STANDARD_ATAPI 0
+#endif
+
+
+/* Turning this on will disable the door-locking functionality.
+ This is apparently needed for supermount. */
+
+#ifndef NO_DOOR_LOCKING
+#define NO_DOOR_LOCKING 0
+#endif
+
+
+/************************************************************************/
+
+#define SECTOR_SIZE 512
+#define SECTOR_BITS 9
+#define SECTORS_PER_FRAME (CD_FRAMESIZE / SECTOR_SIZE)
+
+#define MIN(a,b) ((a) < (b) ? (a) : (b))
+
+/* special command codes for strategy routine. */
+#define PACKET_COMMAND 4315
+#define REQUEST_SENSE_COMMAND 4316
+#define RESET_DRIVE_COMMAND 4317
+
+/* Some ATAPI command opcodes (just like SCSI).
+ (Some other cdrom-specific codes are in cdrom.h.) */
+#define TEST_UNIT_READY 0x00
+#define REQUEST_SENSE 0x03
+#define START_STOP 0x1b
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define MODE_SENSE_10 0x5a
+#define MODE_SELECT_10 0x55
+#define READ_CD 0xbe
+
+
+/* ATAPI sense keys (mostly copied from scsi.h). */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define ABORTED_COMMAND 0x0b
+#define MISCOMPARE 0x0e
+
+/* We want some additional flags for cd-rom drives.
+ To save space in the ide_drive_t struct, use some fields which
+ doesn't make sense for cd-roms -- `bios_sect' and `bios_head'. */
+
+/* Configuration flags. These describe the capabilities of the drive.
+ They generally do not change after initialization, unless we learn
+ more about the drive from stuff failing. */
+struct ide_cd_config_flags {
+ __u8 drq_interrupt : 1; /* Device sends an interrupt when ready
+ for a packet command. */
+ __u8 no_doorlock : 1; /* Drive cannot lock the door. */
+#if ! STANDARD_ATAPI
+ __u8 no_playaudio12: 1; /* The PLAYAUDIO12 command is not supported. */
+
+ __u8 no_lba_toc : 1; /* Drive cannot return TOC info in LBA format. */
+ __u8 playmsf_uses_bcd : 1; /* Drive uses BCD in PLAYAUDIO_MSF. */
+ __u8 old_readcd : 1; /* Drive uses old READ CD opcode. */
+ __u8 vertos_lossage: 1; /* Drive is a Vertos 300,
+ and likes to speak BCD. */
+#endif /* not STANDARD_ATAPI */
+ __u8 reserved : 1;
+};
+#define CDROM_CONFIG_FLAGS(drive) ((struct ide_cd_config_flags *)&((drive)->bios_sect))
+
+
+/* State flags. These give information about the current state of the
+ drive, and will change during normal operation. */
+struct ide_cd_state_flags {
+ __u8 media_changed : 1; /* Driver has noticed a media change. */
+ __u8 toc_valid : 1; /* Saved TOC information is current. */
+ __u8 door_locked : 1; /* We think that the drive door is locked. */
+ __u8 eject_on_close: 1; /* Drive should eject when device is closed. */
+ __u8 reserved : 4;
+};
+#define CDROM_STATE_FLAGS(drive) ((struct ide_cd_state_flags *)&((drive)->bios_head))
+
+
+#define SECTOR_BUFFER_SIZE CD_FRAMESIZE
+
+
+
+/****************************************************************************
+ * Routines to read and write data from/to the drive, using
+ * the routines input_ide_data() and output_ide_data() from ide.c.
+ *
+ * These routines will round up any request for an odd number of bytes,
+ * so if an odd bytecount is specified, be sure that there's at least one
+ * extra byte allocated for the buffer.
+ */
+
+
+static inline
+void cdrom_in_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
+{
+ ++bytecount;
+ ide_input_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ {
+ insw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+ }
+}
+
+
+static inline
+void cdrom_out_bytes (ide_drive_t *drive, void *buffer, uint bytecount)
+{
+ ++bytecount;
+ ide_output_data (drive, buffer, bytecount / 4);
+ if ((bytecount & 0x03) >= 2)
+ {
+ outsw (IDE_DATA_REG, ((byte *)buffer) + (bytecount & ~0x03), 1);
+ }
+}
+
+
+
+/****************************************************************************
+ * Descriptions of ATAPI error codes.
+ */
+
+#define ARY_LEN(a) ((sizeof(a) / sizeof(a[0])))
+
+#if VERBOSE_IDE_CD_ERRORS
+
+/* From Table 124 of the ATAPI 1.2 spec. */
+
+char *sense_key_texts[16] = {
+ "No sense data",
+ "Recovered error",
+ "Not ready",
+ "Medium error",
+ "Hardware error",
+ "Illegal request",
+ "Unit attention",
+ "Data protect",
+ "(reserved)",
+ "(reserved)",
+ "(reserved)",
+ "Aborted command",
+ "(reserved)",
+ "(reserved)",
+ "Miscompare",
+ "(reserved)",
+};
+
+
+/* From Table 125 of the ATAPI 1.2 spec. */
+
+struct {
+ short asc_ascq;
+ char *text;
+} sense_data_texts[] = {
+ { 0x0000, "No additional sense information" },
+ { 0x0011, "Audio play operation in progress" },
+ { 0x0012, "Audio play operation paused" },
+ { 0x0013, "Audio play operation successfully completed" },
+ { 0x0014, "Audio play operation stopped due to error" },
+ { 0x0015, "No current audio status to return" },
+
+ { 0x0200, "No seek complete" },
+
+ { 0x0400, "Logical unit not ready - cause not reportable" },
+ { 0x0401, "Logical unit not ready - in progress (sic) of becoming ready" },
+ { 0x0402, "Logical unit not ready - initializing command required" },
+ { 0x0403, "Logical unit not ready - manual intervention required" },
+
+ { 0x0600, "No reference position found" },
+
+ { 0x0900, "Track following error" },
+ { 0x0901, "Tracking servo failure" },
+ { 0x0902, "Focus servo failure" },
+ { 0x0903, "Spindle servo failure" },
+
+ { 0x1100, "Unrecovered read error" },
+ { 0x1106, "CIRC unrecovered error" },
+
+ { 0x1500, "Random positioning error" },
+ { 0x1501, "Mechanical positioning error" },
+ { 0x1502, "Positioning error detected by read of medium" },
+
+ { 0x1700, "Recovered data with no error correction applied" },
+ { 0x1701, "Recovered data with retries" },
+ { 0x1702, "Recovered data with positive head offset" },
+ { 0x1703, "Recovered data with negative head offset" },
+ { 0x1704, "Recovered data with retries and/or CIRC applied" },
+ { 0x1705, "Recovered data using previous sector ID" },
+
+ { 0x1800, "Recovered data with error correction applied" },
+ { 0x1801, "Recovered data with error correction and retries applied" },
+ { 0x1802, "Recovered data - the data was auto-reallocated" },
+ { 0x1803, "Recovered data with CIRC" },
+ { 0x1804, "Recovered data with L-EC" },
+ { 0x1805, "Recovered data - recommend reassignment" },
+ { 0x1806, "Recovered data - recommend rewrite" },
+
+ { 0x1a00, "Parameter list length error" },
+
+ { 0x2000, "Invalid command operation code" },
+
+ { 0x2100, "Logical block address out of range" },
+
+ { 0x2400, "Invalid field in command packet" },
+
+ { 0x2600, "Invalid field in parameter list" },
+ { 0x2601, "Parameter not supported" },
+ { 0x2602, "Parameter value invalid" },
+ { 0x2603, "Threshold parameters not supported" },
+
+ { 0x2800, "Not ready to ready transition, medium may have changed" },
+
+ { 0x2900, "Power on, reset or bus device reset occurred" },
+
+ { 0x2a00, "Parameters changed" },
+ { 0x2a01, "Mode parameters changed" },
+
+ { 0x3000, "Incompatible medium installed" },
+ { 0x3001, "Cannot read medium - unknown format" },
+ { 0x3002, "Cannot read medium - incompatible format" },
+
+ { 0x3700, "Rounded parameter" },
+
+ { 0x3900, "Saving parameters not supported" },
+
+ { 0x3a00, "Medium not present" },
+
+ { 0x3f00, "ATAPI CD-ROM drive operating conditions have changed" },
+ { 0x3f01, "Microcode has been changed" },
+ { 0x3f02, "Changed operating definition" },
+ { 0x3f03, "Inquiry data has changed" },
+
+ { 0x4000, "Diagnostic failure on component (ASCQ)" },
+
+ { 0x4400, "Internal ATAPI CD-ROM drive failure" },
+
+ { 0x4e00, "Overlapped commands attempted" },
+
+ { 0x5300, "Media load or eject failed" },
+ { 0x5302, "Medium removal prevented" },
+
+ { 0x5700, "Unable to recover table of contents" },
+
+ { 0x5a00, "Operator request or state change input (unspecified)" },
+ { 0x5a01, "Operator medium removal request" },
+
+ { 0x5b00, "Threshold condition met" },
+
+ { 0x5c00, "Status change" },
+
+ { 0x6300, "End of user area encountered on this track" },
+
+ { 0x6400, "Illegal mode for this track" },
+
+ { 0xbf00, "Loss of streaming" },
+};
+#endif
+
+
+
+/****************************************************************************
+ * Generic packet command support and error handling routines.
+ */
+
+
+static
+void cdrom_analyze_sense_data (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf,
+ struct packet_command *failed_command)
+{
+ /* Don't print not ready or unit attention errors for READ_SUBCHANNEL.
+ Workman (and probably other programs) uses this command to poll
+ the drive, and we don't want to fill the syslog with useless errors. */
+ if (failed_command &&
+ failed_command->c[0] == SCMD_READ_SUBCHANNEL &&
+ (reqbuf->sense_key == NOT_READY || reqbuf->sense_key == UNIT_ATTENTION))
+ return;
+
+#if VERBOSE_IDE_CD_ERRORS
+ {
+ int i;
+ char *s;
+ char buf[80];
+
+ printk ("ATAPI device %s:\n", drive->name);
+
+ printk (" Error code: 0x%02x\n", reqbuf->error_code);
+
+ if (reqbuf->sense_key >= 0 &&
+ reqbuf->sense_key < ARY_LEN (sense_key_texts))
+ s = sense_key_texts[reqbuf->sense_key];
+ else
+ s = "(bad sense key)";
+
+ printk (" Sense key: 0x%02x - %s\n", reqbuf->sense_key, s);
+
+ if (reqbuf->asc == 0x40) {
+ sprintf (buf, "Diagnostic failure on component 0x%02x", reqbuf->ascq);
+ s = buf;
+ }
+
+ else {
+ int lo, hi;
+ int key = (reqbuf->asc << 8);
+ if ( ! (reqbuf->ascq >= 0x80 && reqbuf->ascq <= 0xdd) )
+ key |= reqbuf->ascq;
+
+ lo = 0;
+ hi = ARY_LEN (sense_data_texts);
+ s = NULL;
+
+ while (hi > lo) {
+ int mid = (lo + hi) / 2;
+ if (sense_data_texts[mid].asc_ascq == key) {
+ s = sense_data_texts[mid].text;
+ break;
+ }
+ else if (sense_data_texts[mid].asc_ascq > key)
+ hi = mid;
+ else
+ lo = mid+1;
+ }
+ }
+
+ if (s == NULL) {
+ if (reqbuf->asc > 0x80)
+ s = "(vendor-specific error)";
+ else
+ s = "(reserved error code)";
+ }
+
+ printk (" Additional sense data: 0x%02x, 0x%02x - %s\n",
+ reqbuf->asc, reqbuf->ascq, s);
+
+ if (failed_command != NULL) {
+ printk (" Failed packet command: ");
+ for (i=0; i<sizeof (failed_command->c); i++)
+ printk ("%02x ", failed_command->c[i]);
+ printk ("\n");
+ }
+
+ if (reqbuf->sense_key == ILLEGAL_REQUEST &&
+ (reqbuf->sense_key_specific[0] & 0x80) != 0)
+ {
+ printk (" Error in %s byte %d",
+ (reqbuf->sense_key_specific[0] & 0x40) != 0
+ ? "command packet"
+ : "command data",
+ (reqbuf->sense_key_specific[1] << 8) +
+ reqbuf->sense_key_specific[2]);
+
+ if ((reqbuf->sense_key_specific[0] & 0x40) != 0)
+ {
+ printk (" bit %d", reqbuf->sense_key_specific[0] & 0x07);
+ }
+
+ printk ("\n");
+ }
+ }
+
+#else
+
+ /* Suppress printing unit attention and `in progress of becoming ready'
+ errors when we're not being verbose. */
+
+ if (reqbuf->sense_key == UNIT_ATTENTION ||
+ (reqbuf->sense_key == NOT_READY && (reqbuf->asc == 4 ||
+ reqbuf->asc == 0x3a)))
+ return;
+
+ printk ("%s: code: 0x%02x key: 0x%02x asc: 0x%02x ascq: 0x%02x\n",
+ drive->name,
+ reqbuf->error_code, reqbuf->sense_key, reqbuf->asc, reqbuf->ascq);
+#endif
+}
+
+
+/* Fix up a possibly partially-processed request so that we can
+ start it over entirely, or even put it back on the request queue. */
+static void restore_request (struct request *rq)
+{
+ if (rq->buffer != rq->bh->b_data)
+ {
+ int n = (rq->buffer - rq->bh->b_data) / SECTOR_SIZE;
+ rq->buffer = rq->bh->b_data;
+ rq->nr_sectors += n;
+ rq->sector -= n;
+ }
+ rq->current_nr_sectors = rq->bh->b_size >> SECTOR_BITS;
+}
+
+
+static void cdrom_queue_request_sense (ide_drive_t *drive,
+ struct semaphore *sem,
+ struct atapi_request_sense *reqbuf,
+ struct packet_command *failed_command)
+{
+ struct request *rq;
+ struct packet_command *pc;
+ int len;
+
+ /* If the request didn't explicitly specify where to put the sense data,
+ use the statically allocated structure. */
+ if (reqbuf == NULL)
+ reqbuf = &drive->cdrom_info.sense_data;
+
+ /* Make up a new request to retrieve sense information. */
+
+ pc = &HWIF(drive)->request_sense_pc;
+ memset (pc, 0, sizeof (*pc));
+
+ /* The request_sense structure has an odd number of (16-bit) words,
+ which won't work well with 32-bit transfers. However, we don't care
+ about the last two bytes, so just truncate the structure down
+ to an even length. */
+ len = sizeof (*reqbuf) / 4;
+ len *= 4;
+
+ pc->c[0] = REQUEST_SENSE;
+ pc->c[4] = len;
+ pc->buffer = (char *)reqbuf;
+ pc->buflen = len;
+ pc->sense_data = (struct atapi_request_sense *)failed_command;
+
+ /* stuff the sense request in front of our current request */
+
+ rq = &HWIF(drive)->request_sense_request;
+ ide_init_drive_cmd (rq);
+ rq->cmd = REQUEST_SENSE_COMMAND;
+ rq->buffer = (char *)pc;
+ rq->sem = sem;
+ (void) ide_do_drive_cmd (drive, rq, ide_preempt);
+}
+
+
+static void cdrom_end_request (int uptodate, ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* The code in blk.h can screw us up on error recovery if the block
+ size is larger than 1k. Fix that up here. */
+ if (!uptodate && rq->bh != 0)
+ {
+ int adj = rq->current_nr_sectors - 1;
+ rq->current_nr_sectors -= adj;
+ rq->sector += adj;
+ }
+
+ if (rq->cmd == REQUEST_SENSE_COMMAND && uptodate)
+ {
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ cdrom_analyze_sense_data (drive,
+ (struct atapi_request_sense *)(pc->buffer - pc->c[4]),
+ (struct packet_command *)pc->sense_data);
+ }
+
+ ide_end_request (uptodate, HWGROUP(drive));
+}
+
+
+/* Mark that we've seen a media change, and invalidate our internal
+ buffers. */
+static void cdrom_saw_media_change (ide_drive_t *drive)
+{
+ CDROM_STATE_FLAGS (drive)->media_changed = 1;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ drive->cdrom_info.nsectors_buffered = 0;
+}
+
+
+/* Returns 0 if the request should be continued.
+ Returns 1 if the request was ended. */
+static int cdrom_decode_status (ide_drive_t *drive, int good_stat, int *stat_ret)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ int stat, err, sense_key, cmd;
+
+ /* Check for errors. */
+ stat = GET_STAT();
+ *stat_ret = stat;
+
+ if (OK_STAT (stat, good_stat, BAD_R_STAT))
+ return 0;
+
+ /* Got an error. */
+ err = IN_BYTE (IDE_ERROR_REG);
+ sense_key = err >> 4;
+
+ if (rq == NULL)
+ printk ("%s : missing request in cdrom_decode_status\n", drive->name);
+ else
+ {
+ cmd = rq->cmd;
+
+ if (cmd == REQUEST_SENSE_COMMAND)
+ {
+ /* We got an error trying to get sense info from the drive
+ (probably while trying to recover from a former error).
+ Just give up. */
+
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ ide_error (drive, "request sense failure", stat);
+ return 1;
+ }
+
+ else if (cmd == PACKET_COMMAND)
+ {
+ /* All other functions, except for READ. */
+
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+ struct semaphore *sem = NULL;
+
+ /* Check for tray open. */
+ if (sense_key == NOT_READY)
+ {
+ cdrom_saw_media_change (drive);
+
+ /* Print an error message to the syslog.
+ Exception: don't print anything if this is a read subchannel
+ command. This is because workman constantly polls the drive
+ with this command, and we don't want to uselessly fill up
+ the syslog. */
+ if (pc->c[0] != SCMD_READ_SUBCHANNEL)
+ printk ("%s : tray open or drive not ready\n", drive->name);
+ }
+
+ /* Check for media change. */
+ else if (sense_key == UNIT_ATTENTION)
+ {
+ cdrom_saw_media_change (drive);
+ printk ("%s: media changed\n", drive->name);
+ }
+
+ /* Otherwise, print an error. */
+ else
+ {
+ ide_dump_status (drive, "packet command error", stat);
+ }
+
+ /* Set the error flag and complete the request.
+ Then, if we have a CHECK CONDITION status, queue a request
+ sense command. We must be careful, though: we don't want
+ the thread in cdrom_queue_packet_command to wake up until
+ the request sense has completed. We do this by transferring
+ the semaphore from the packet command request to the
+ request sense request. */
+
+ if ((stat & ERR_STAT) != 0)
+ {
+ sem = rq->sem;
+ rq->sem = NULL;
+ }
+
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense (drive, sem, pc->sense_data, pc);
+ }
+
+ else
+ {
+ /* Handle errors from READ requests. */
+
+ /* Check for tray open. */
+ if (sense_key == NOT_READY)
+ {
+ cdrom_saw_media_change (drive);
+
+ /* Fail the request. */
+ printk ("%s : tray open\n", drive->name);
+ cdrom_end_request (0, drive);
+ }
+
+ /* Check for media change. */
+ else if (sense_key == UNIT_ATTENTION)
+ {
+ cdrom_saw_media_change (drive);
+
+ /* Arrange to retry the request.
+ But be sure to give up if we've retried too many times. */
+ if (++rq->errors > ERROR_MAX)
+ {
+ cdrom_end_request (0, drive);
+ }
+ }
+ /* No point in retrying after an illegal request or
+ data protect error.*/
+ else if (sense_key == ILLEGAL_REQUEST || sense_key == DATA_PROTECT)
+ {
+ ide_dump_status (drive, "command error", stat);
+ cdrom_end_request (0, drive);
+ }
+
+ /* If there were other errors, go to the default handler. */
+ else if ((err & ~ABRT_ERR) != 0)
+ {
+ ide_error (drive, "cdrom_decode_status", stat);
+ return 1;
+ }
+
+ /* Else, abort if we've racked up too many retries. */
+ else if ((++rq->errors > ERROR_MAX))
+ {
+ cdrom_end_request (0, drive);
+ }
+
+ /* If we got a CHECK_CONDITION status, queue a request sense
+ command. */
+ if ((stat & ERR_STAT) != 0)
+ cdrom_queue_request_sense (drive, NULL, NULL, NULL);
+ }
+ }
+
+ /* Retry, or handle the next request. */
+ return 1;
+}
+
+
+/* Set up the device registers for transferring a packet command on DEV,
+ expecting to later transfer XFERLEN bytes. HANDLER is the routine
+ which actually transfers the command to the drive. If this is a
+ drq_interrupt device, this routine will arrange for HANDLER to be
+ called when the interrupt from the drive arrives. Otherwise, HANDLER
+ will be called immediately after the drive is prepared for the transfer. */
+
+static int cdrom_start_packet_command (ide_drive_t *drive, int xferlen,
+ ide_handler_t *handler)
+{
+ /* Wait for the controller to be idle. */
+ if (ide_wait_stat (drive, 0, BUSY_STAT, WAIT_READY)) return 1;
+
+ /* Set up the controller registers. */
+ OUT_BYTE (0, IDE_FEATURE_REG);
+ OUT_BYTE (0, IDE_NSECTOR_REG);
+ OUT_BYTE (0, IDE_SECTOR_REG);
+
+ OUT_BYTE (xferlen & 0xff, IDE_LCYL_REG);
+ OUT_BYTE (xferlen >> 8 , IDE_HCYL_REG);
+ OUT_BYTE (drive->ctl, IDE_CONTROL_REG);
+
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
+ {
+ ide_set_handler (drive, handler, WAIT_CMD);
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ }
+ else
+ {
+ OUT_BYTE (WIN_PACKETCMD, IDE_COMMAND_REG); /* packet command */
+ (*handler) (drive);
+ }
+
+ return 0;
+}
+
+
+/* Send a packet command to DRIVE described by CMD_BUF and CMD_LEN.
+ The device registers must have already been prepared
+ by cdrom_start_packet_command.
+ HANDLER is the interrupt handler to call when the command completes
+ or there's data ready. */
+static int cdrom_transfer_packet_command (ide_drive_t *drive,
+ char *cmd_buf, int cmd_len,
+ ide_handler_t *handler)
+{
+ if (CDROM_CONFIG_FLAGS (drive)->drq_interrupt)
+ {
+ /* Here we should have been called after receiving an interrupt
+ from the device. DRQ should how be set. */
+ int stat_dum;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, DRQ_STAT, &stat_dum)) return 1;
+ }
+ else
+ {
+ /* Otherwise, we must wait for DRQ to get set. */
+ if (ide_wait_stat (drive, DRQ_STAT, BUSY_STAT, WAIT_READY)) return 1;
+ }
+
+ /* Arm the interrupt handler. */
+ ide_set_handler (drive, handler, WAIT_CMD);
+
+ /* Send the command to the device. */
+ cdrom_out_bytes (drive, cmd_buf, cmd_len);
+
+ return 0;
+}
+
+
+
+/****************************************************************************
+ * Block read functions.
+ */
+
+/*
+ * Buffer up to SECTORS_TO_TRANSFER sectors from the drive in our sector
+ * buffer. Once the first sector is added, any subsequent sectors are
+ * assumed to be continuous (until the buffer is cleared). For the first
+ * sector added, SECTOR is its sector number. (SECTOR is then ignored until
+ * the buffer is cleared.)
+ */
+static void cdrom_buffer_sectors (ide_drive_t *drive, unsigned long sector,
+ int sectors_to_transfer)
+{
+ struct cdrom_info *info = &drive->cdrom_info;
+
+ /* Number of sectors to read into the buffer. */
+ int sectors_to_buffer = MIN (sectors_to_transfer,
+ (SECTOR_BUFFER_SIZE >> SECTOR_BITS) -
+ info->nsectors_buffered);
+
+ char *dest;
+
+ /* If we don't yet have a sector buffer, try to allocate one.
+ If we can't get one atomically, it's not fatal -- we'll just throw
+ the data away rather than caching it. */
+ if (info->sector_buffer == NULL)
+ {
+ info->sector_buffer = (char *) kmalloc (SECTOR_BUFFER_SIZE, GFP_ATOMIC);
+
+ /* If we couldn't get a buffer, don't try to buffer anything... */
+ if (info->sector_buffer == NULL)
+ sectors_to_buffer = 0;
+ }
+
+ /* If this is the first sector in the buffer, remember its number. */
+ if (info->nsectors_buffered == 0)
+ info->sector_buffered = sector;
+
+ /* Read the data into the buffer. */
+ dest = info->sector_buffer + info->nsectors_buffered * SECTOR_SIZE;
+ while (sectors_to_buffer > 0)
+ {
+ cdrom_in_bytes (drive, dest, SECTOR_SIZE);
+ --sectors_to_buffer;
+ --sectors_to_transfer;
+ ++info->nsectors_buffered;
+ dest += SECTOR_SIZE;
+ }
+
+ /* Throw away any remaining data. */
+ while (sectors_to_transfer > 0)
+ {
+ char dum[SECTOR_SIZE];
+ cdrom_in_bytes (drive, dum, sizeof (dum));
+ --sectors_to_transfer;
+ }
+}
+
+
+/*
+ * Check the contents of the interrupt reason register from the cdrom
+ * and attempt to recover if there are problems. Returns 0 if everything's
+ * ok; nonzero if the request has been terminated.
+ */
+static inline
+int cdrom_read_check_ireason (ide_drive_t *drive, int len, int ireason)
+{
+ ireason &= 3;
+ if (ireason == 2) return 0;
+
+ if (ireason == 0)
+ {
+ /* Whoops... The drive is expecting to receive data from us! */
+ printk ("%s: cdrom_read_intr: "
+ "Drive wants to transfer data the wrong way!\n",
+ drive->name);
+
+ /* Throw some data at the drive so it doesn't hang
+ and quit this request. */
+ while (len > 0)
+ {
+ int dum = 0;
+ cdrom_out_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+ }
+
+ else
+ {
+ /* Drive wants a command packet, or invalid ireason... */
+ printk ("%s: cdrom_read_intr: bad interrupt reason %d\n",
+ drive->name, ireason);
+ }
+
+ cdrom_end_request (0, drive);
+ return -1;
+}
+
+
+/*
+ * Interrupt routine. Called when a read request has completed.
+ */
+static void cdrom_read_intr (ide_drive_t *drive)
+{
+ int stat;
+ int ireason, len, sectors_to_transfer, nskip;
+
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed. */
+ if ((stat & DRQ_STAT) == 0)
+ {
+ /* If we're not done filling the current buffer, complain.
+ Otherwise, complete the command normally. */
+ if (rq->current_nr_sectors > 0)
+ {
+ printk ("%s: cdrom_read_intr: data underrun (%ld blocks)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ }
+ else
+ cdrom_end_request (1, drive);
+
+ return;
+ }
+
+ /* Check that the drive is expecting to do the same thing that we are. */
+ if (cdrom_read_check_ireason (drive, len, ireason)) return;
+
+ /* Assume that the drive will always provide data in multiples of at least
+ SECTOR_SIZE, as it gets hairy to keep track of the transfers otherwise. */
+ if ((len % SECTOR_SIZE) != 0)
+ {
+ printk ("%s: cdrom_read_intr: Bad transfer size %d\n",
+ drive->name, len);
+ printk (" This drive is not supported by this version of the driver\n");
+ cdrom_end_request (0, drive);
+ return;
+ }
+
+ /* The number of sectors we need to read from the drive. */
+ sectors_to_transfer = len / SECTOR_SIZE;
+
+ /* First, figure out if we need to bit-bucket any of the leading sectors. */
+ nskip = MIN ((int)(rq->current_nr_sectors - (rq->bh->b_size >> SECTOR_BITS)),
+ sectors_to_transfer);
+
+ while (nskip > 0)
+ {
+ /* We need to throw away a sector. */
+ char dum[SECTOR_SIZE];
+ cdrom_in_bytes (drive, dum, sizeof (dum));
+
+ --rq->current_nr_sectors;
+ --nskip;
+ --sectors_to_transfer;
+ }
+
+ /* Now loop while we still have data to read from the drive. */
+ while (sectors_to_transfer > 0)
+ {
+ int this_transfer;
+
+ /* If we've filled the present buffer but there's another chained
+ buffer after it, move on. */
+ if (rq->current_nr_sectors == 0 &&
+ rq->nr_sectors > 0)
+ cdrom_end_request (1, drive);
+
+ /* If the buffers are full, cache the rest of the data in our
+ internal buffer. */
+ if (rq->current_nr_sectors == 0)
+ {
+ cdrom_buffer_sectors (drive, rq->sector, sectors_to_transfer);
+ sectors_to_transfer = 0;
+ }
+ else
+ {
+ /* Transfer data to the buffers.
+ Figure out how many sectors we can transfer
+ to the current buffer. */
+ this_transfer = MIN (sectors_to_transfer,
+ rq->current_nr_sectors);
+
+ /* Read this_transfer sectors into the current buffer. */
+ while (this_transfer > 0)
+ {
+ cdrom_in_bytes (drive, rq->buffer, SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ ++rq->sector;
+ --this_transfer;
+ --sectors_to_transfer;
+ }
+ }
+ }
+
+ /* Done moving data!
+ Wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_read_intr, WAIT_CMD);
+}
+
+
+/*
+ * Try to satisfy some of the current read request from our cached data.
+ * Returns nonzero if the request has been completed, zero otherwise.
+ */
+static int cdrom_read_from_buffer (ide_drive_t *drive)
+{
+ struct cdrom_info *info = &drive->cdrom_info;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ /* Can't do anything if there's no buffer. */
+ if (info->sector_buffer == NULL) return 0;
+
+ /* Loop while this request needs data and the next block is present
+ in our cache. */
+ while (rq->nr_sectors > 0 &&
+ rq->sector >= info->sector_buffered &&
+ rq->sector < info->sector_buffered + info->nsectors_buffered)
+ {
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ memcpy (rq->buffer,
+ info->sector_buffer +
+ (rq->sector - info->sector_buffered) * SECTOR_SIZE,
+ SECTOR_SIZE);
+ rq->buffer += SECTOR_SIZE;
+ --rq->current_nr_sectors;
+ --rq->nr_sectors;
+ ++rq->sector;
+ }
+
+ /* If we've satisfied the current request, terminate it successfully. */
+ if (rq->nr_sectors == 0)
+ {
+ cdrom_end_request (1, drive);
+ return -1;
+ }
+
+ /* Move on to the next buffer if needed. */
+ if (rq->current_nr_sectors == 0)
+ cdrom_end_request (1, drive);
+
+ /* If this condition does not hold, then the kluge i use to
+ represent the number of sectors to skip at the start of a transfer
+ will fail. I think that this will never happen, but let's be
+ paranoid and check. */
+ if (rq->current_nr_sectors < (rq->bh->b_size >> SECTOR_BITS) &&
+ (rq->sector % SECTORS_PER_FRAME) != 0)
+ {
+ printk ("%s: cdrom_read_from_buffer: buffer botch (%ld)\n",
+ drive->name, rq->sector);
+ cdrom_end_request (0, drive);
+ return -1;
+ }
+
+ return 0;
+}
+
+
+
+/*
+ * Routine to send a read packet command to the drive.
+ * This is usually called directly from cdrom_start_read.
+ * However, for drq_interrupt devices, it is called from an interrupt
+ * when the drive is ready to accept the command.
+ */
+static void cdrom_start_read_continuation (ide_drive_t *drive)
+{
+ struct packet_command pc;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ int nsect, sector, nframes, frame, nskip;
+
+ /* Number of sectors to transfer. */
+ nsect = rq->nr_sectors;
+
+ /* Starting sector. */
+ sector = rq->sector;
+
+ /* If the requested sector doesn't start on a cdrom block boundary,
+ we must adjust the start of the transfer so that it does,
+ and remember to skip the first few sectors. If the CURRENT_NR_SECTORS
+ field is larger than the size of the buffer, it will mean that
+ we're to skip a number of sectors equal to the amount by which
+ CURRENT_NR_SECTORS is larger than the buffer size. */
+ nskip = (sector % SECTORS_PER_FRAME);
+ if (nskip > 0)
+ {
+ /* Sanity check... */
+ if (rq->current_nr_sectors != (rq->bh->b_size >> SECTOR_BITS))
+ {
+ printk ("%s: cdrom_start_read_continuation: buffer botch (%ld)\n",
+ drive->name, rq->current_nr_sectors);
+ cdrom_end_request (0, drive);
+ return;
+ }
+
+ sector -= nskip;
+ nsect += nskip;
+ rq->current_nr_sectors += nskip;
+ }
+
+ /* Convert from sectors to cdrom blocks, rounding up the transfer
+ length if needed. */
+ nframes = (nsect + SECTORS_PER_FRAME-1) / SECTORS_PER_FRAME;
+ frame = sector / SECTORS_PER_FRAME;
+
+ /* Largest number of frames was can transfer at once is 64k-1. */
+ nframes = MIN (nframes, 65535);
+
+ /* Set up the command */
+ memset (&pc.c, 0, sizeof (pc.c));
+ pc.c[0] = READ_10;
+ pc.c[7] = (nframes >> 8);
+ pc.c[8] = (nframes & 0xff);
+#ifdef __alpha__
+ stl_u (htonl (frame), (unsigned int *) &pc.c[2]);
+#else
+ *(int *)(&pc.c[2]) = htonl (frame);
+#endif
+
+ /* Send the command to the drive and return. */
+ (void) cdrom_transfer_packet_command (drive, pc.c, sizeof (pc.c),
+ &cdrom_read_intr);
+}
+
+
+/*
+ * Start a read request from the CD-ROM.
+ */
+static void cdrom_start_read (ide_drive_t *drive, unsigned int block)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ int minor = MINOR (rq->rq_dev);
+
+ /* If the request is relative to a partition, fix it up to refer to the
+ absolute address. */
+ if ((minor & PARTN_MASK) != 0) {
+ rq->sector = block;
+ minor &= ~PARTN_MASK;
+ rq->rq_dev = MKDEV (MAJOR(rq->rq_dev), minor);
+ }
+
+ /* We may be retrying this request after an error.
+ Fix up any weirdness which might be present in the request packet. */
+ restore_request (rq);
+
+ /* Satisfy whatever we can of this request from our cached sector. */
+ if (cdrom_read_from_buffer (drive))
+ return;
+
+ /* Clear the local sector buffer. */
+ drive->cdrom_info.nsectors_buffered = 0;
+
+ /* Start sending the read request to the drive. */
+ cdrom_start_packet_command (drive, 32768, cdrom_start_read_continuation);
+}
+
+
+
+
+/****************************************************************************
+ * Execute all other packet commands.
+ */
+
+/* Forward declarations. */
+static int
+cdrom_lockdoor (ide_drive_t *drive, int lockflag,
+ struct atapi_request_sense *reqbuf);
+
+
+
+/* Interrupt routine for packet command completion. */
+static void cdrom_pc_intr (ide_drive_t *drive)
+{
+ int ireason, len, stat, thislen;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Check for errors. */
+ if (cdrom_decode_status (drive, 0, &stat)) return;
+
+ /* Read the interrupt reason and the transfer length. */
+ ireason = IN_BYTE (IDE_NSECTOR_REG);
+ len = IN_BYTE (IDE_LCYL_REG) + 256 * IN_BYTE (IDE_HCYL_REG);
+
+ /* If DRQ is clear, the command has completed.
+ Complain if we still have data left to transfer. */
+ if ((stat & DRQ_STAT) == 0)
+ {
+ /* Some of the trailing request sense fields are optional, and
+ some drives don't send them. Sigh. */
+ if (pc->c[0] == REQUEST_SENSE && pc->buflen > 0 && pc->buflen <= 5) {
+ while (pc->buflen > 0) {
+ *pc->buffer++ = 0;
+ --pc->buflen;
+ }
+ }
+
+ if (pc->buflen == 0)
+ cdrom_end_request (1, drive);
+ else
+ {
+ printk ("%s: cdrom_pc_intr: data underrun %d\n",
+ drive->name, pc->buflen);
+ pc->stat = 1;
+ cdrom_end_request (1, drive);
+ }
+ return;
+ }
+
+ /* Figure out how much data to transfer. */
+ thislen = pc->buflen;
+ if (thislen < 0) thislen = -thislen;
+ if (thislen > len) thislen = len;
+
+ /* The drive wants to be written to. */
+ if ((ireason & 3) == 0)
+ {
+ /* Check that we want to write. */
+ if (pc->buflen > 0)
+ {
+ printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
+ drive->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ cdrom_out_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen)
+ {
+ int dum = 0;
+ cdrom_out_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen += thislen;
+ }
+
+ /* Same drill for reading. */
+ else if ((ireason & 3) == 2)
+ {
+ /* Check that we want to read. */
+ if (pc->buflen < 0)
+ {
+ printk ("%s: cdrom_pc_intr: Drive wants to transfer data the wrong way!\n",
+ drive->name);
+ pc->stat = 1;
+ thislen = 0;
+ }
+
+ /* Transfer the data. */
+ cdrom_in_bytes (drive, pc->buffer, thislen);
+
+ /* If we haven't moved enough data to satisfy the drive,
+ add some padding. */
+ while (len > thislen)
+ {
+ int dum = 0;
+ cdrom_in_bytes (drive, &dum, sizeof (dum));
+ len -= sizeof (dum);
+ }
+
+ /* Keep count of how much data we've moved. */
+ pc->buffer += thislen;
+ pc->buflen -= thislen;
+ }
+
+ else
+ {
+ printk ("%s: cdrom_pc_intr: The drive appears confused (ireason = 0x%2x)\n",
+ drive->name, ireason);
+ pc->stat = 1;
+ }
+
+ /* Now we wait for another interrupt. */
+ ide_set_handler (drive, &cdrom_pc_intr, WAIT_CMD);
+}
+
+
+static void cdrom_do_pc_continuation (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ /* Send the command to the drive and return. */
+ cdrom_transfer_packet_command (drive, pc->c, sizeof (pc->c), &cdrom_pc_intr);
+}
+
+
+static void cdrom_do_packet_command (ide_drive_t *drive)
+{
+ int len;
+ struct request *rq = HWGROUP(drive)->rq;
+ struct packet_command *pc = (struct packet_command *)rq->buffer;
+
+ len = pc->buflen;
+ if (len < 0) len = -len;
+
+ pc->stat = 0;
+
+ /* Start sending the command to the drive. */
+ cdrom_start_packet_command (drive, len, cdrom_do_pc_continuation);
+}
+
+#ifndef MACH
+/* Sleep for TIME jiffies.
+ Not to be called from an interrupt handler. */
+static
+void cdrom_sleep (int time)
+{
+ current->state = TASK_INTERRUPTIBLE;
+ current->timeout = jiffies + time;
+ schedule ();
+}
+#endif
+
+static
+int cdrom_queue_packet_command (ide_drive_t *drive, struct packet_command *pc)
+{
+ struct atapi_request_sense my_reqbuf;
+ int retries = 10;
+ struct request req;
+
+ /* If our caller has not provided a place to stick any sense data,
+ use our own area. */
+ if (pc->sense_data == NULL)
+ pc->sense_data = &my_reqbuf;
+ pc->sense_data->sense_key = 0;
+
+ /* Start of retry loop. */
+ do {
+ ide_init_drive_cmd (&req);
+ req.cmd = PACKET_COMMAND;
+ req.buffer = (char *)pc;
+ (void) ide_do_drive_cmd (drive, &req, ide_wait);
+
+ if (pc->stat != 0)
+ {
+ /* The request failed. Retry if it was due to a unit attention status
+ (usually means media was changed). */
+ struct atapi_request_sense *reqbuf = pc->sense_data;
+
+ if (reqbuf->sense_key == UNIT_ATTENTION)
+ ;
+
+ /* Also retry if the drive is in the process of loading a disk.
+ This time, however, wait a little between retries to give
+ the drive time. */
+ else if (reqbuf->sense_key == NOT_READY && reqbuf->asc == 4)
+ {
+ cdrom_sleep (HZ);
+ }
+
+ /* Otherwise, don't retry. */
+ else
+ retries = 0;
+
+ --retries;
+ }
+
+ /* End of retry loop. */
+ } while (pc->stat != 0 && retries >= 0);
+
+
+ /* Return an error if the command failed. */
+ if (pc->stat != 0)
+ return -EIO;
+
+ else
+ {
+ /* The command succeeded. If it was anything other than a request sense,
+ eject, or door lock command, and we think that the door is presently
+ unlocked, lock it again. (The door was probably unlocked via
+ an explicit CDROMEJECT ioctl.) */
+ if (CDROM_STATE_FLAGS (drive)->door_locked == 0 &&
+ (pc->c[0] != REQUEST_SENSE &&
+ pc->c[0] != ALLOW_MEDIUM_REMOVAL &&
+ pc->c[0] != START_STOP))
+ {
+ (void) cdrom_lockdoor (drive, 1, NULL);
+ }
+ return 0;
+ }
+}
+
+
+
+/****************************************************************************
+ * drive_cmd handling.
+ *
+ * Most of the functions accessed via drive_cmd are not valid for ATAPI
+ * devices. Only attempt to execute those which actually should be valid.
+ */
+
+static
+void cdrom_do_drive_cmd (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ byte *args = rq->buffer;
+
+ if (args)
+ {
+#if 0 /* This bit isn't done yet... */
+ if (args[0] == WIN_SETFEATURES &&
+ (args[2] == 0x66 || args[2] == 0xcc || args[2] == 0x02 ||
+ args[2] == 0xdd || args[2] == 0x5d))
+ {
+ OUT_BYTE (args[2], io_base + IDE_FEATURE_OFFSET);
+ <send cmd>
+ }
+ else
+#endif
+ {
+ printk ("%s: Unsupported drive command %02x %02x %02x\n",
+ drive->name, args[0], args[1], args[2]);
+ rq->errors = 1;
+ }
+ }
+
+ cdrom_end_request (1, drive);
+}
+
+
+
+/****************************************************************************
+ * cdrom driver request routine.
+ */
+
+void ide_do_rw_cdrom (ide_drive_t *drive, unsigned long block)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+
+ if (rq -> cmd == PACKET_COMMAND || rq -> cmd == REQUEST_SENSE_COMMAND)
+ cdrom_do_packet_command (drive);
+
+ else if (rq -> cmd == RESET_DRIVE_COMMAND)
+ {
+ cdrom_end_request (1, drive);
+ ide_do_reset (drive);
+ return;
+ }
+
+ else if (rq -> cmd == IDE_DRIVE_CMD)
+ cdrom_do_drive_cmd (drive);
+
+ else if (rq -> cmd != READ)
+ {
+ printk ("ide-cd: bad cmd %d\n", rq -> cmd);
+ cdrom_end_request (0, drive);
+ }
+ else
+ cdrom_start_read (drive, block);
+}
+
+
+
+/****************************************************************************
+ * Ioctl handling.
+ *
+ * Routines which queue packet commands take as a final argument a pointer
+ * to an atapi_request_sense struct. If execution of the command results
+ * in an error with a CHECK CONDITION status, this structure will be filled
+ * with the results of the subsequent request sense command. The pointer
+ * can also be NULL, in which case no sense information is returned.
+ */
+
+#if ! STANDARD_ATAPI
+static
+int bin2bcd (int x)
+{
+ return (x%10) | ((x/10) << 4);
+}
+
+
+static
+int bcd2bin (int x)
+{
+ return (x >> 4) * 10 + (x & 0x0f);
+}
+#endif /* not STANDARD_ATAPI */
+
+
+static inline
+void lba_to_msf (int lba, byte *m, byte *s, byte *f)
+{
+ lba += CD_BLOCK_OFFSET;
+ lba &= 0xffffff; /* negative lbas use only 24 bits */
+ *m = lba / (CD_SECS * CD_FRAMES);
+ lba %= (CD_SECS * CD_FRAMES);
+ *s = lba / CD_FRAMES;
+ *f = lba % CD_FRAMES;
+}
+
+
+static inline
+int msf_to_lba (byte m, byte s, byte f)
+{
+ return (((m * CD_SECS) + s) * CD_FRAMES + f) - CD_BLOCK_OFFSET;
+}
+
+
+static int
+cdrom_check_status (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+
+ pc.sense_data = reqbuf;
+ pc.c[0] = TEST_UNIT_READY;
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Lock the door if LOCKFLAG is nonzero; unlock it otherwise. */
+static int
+cdrom_lockdoor (ide_drive_t *drive, int lockflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct atapi_request_sense my_reqbuf;
+ int stat;
+ struct packet_command pc;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ /* If the drive cannot lock the door, just pretend. */
+ if (CDROM_CONFIG_FLAGS (drive)->no_doorlock)
+ stat = 0;
+ else
+ {
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = ALLOW_MEDIUM_REMOVAL;
+ pc.c[4] = (lockflag != 0);
+ stat = cdrom_queue_packet_command (drive, &pc);
+ }
+
+ if (stat == 0)
+ CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+ else
+ {
+ /* If we got an illegal field error, the drive
+ probably cannot lock the door. */
+ if (reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x24)
+ {
+ printk ("%s: door locking not supported\n", drive->name);
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+ stat = 0;
+ CDROM_STATE_FLAGS (drive)->door_locked = lockflag;
+ }
+ }
+ return stat;
+}
+
+
+/* Eject the disk if EJECTFLAG is 0.
+ If EJECTFLAG is 1, try to reload the disk. */
+static int
+cdrom_eject (ide_drive_t *drive, int ejectflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = START_STOP;
+ pc.c[4] = 2 + (ejectflag != 0);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_pause (ide_drive_t *drive, int pauseflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PAUSE_RESUME;
+ pc.c[8] = !pauseflag;
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_startstop (ide_drive_t *drive, int startflag,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = START_STOP;
+ pc.c[1] = 1;
+ pc.c[4] = startflag;
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_read_capacity (ide_drive_t *drive, unsigned *capacity,
+ struct atapi_request_sense *reqbuf)
+{
+ struct {
+ unsigned lba;
+ unsigned blocklen;
+ } capbuf;
+
+ int stat;
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = READ_CAPACITY;
+ pc.buffer = (char *)&capbuf;
+ pc.buflen = sizeof (capbuf);
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+ if (stat == 0)
+ {
+ *capacity = ntohl (capbuf.lba);
+ }
+
+ return stat;
+}
+
+
+static int
+cdrom_read_tocentry (ide_drive_t *drive, int trackno, int msf_flag,
+ int format, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_TOC;
+ pc.c[6] = trackno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ pc.c[9] = (format << 6);
+ if (msf_flag) pc.c[1] = 2;
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* Try to read the entire TOC for the disk into our internal buffer. */
+static int
+cdrom_read_toc (ide_drive_t *drive,
+ struct atapi_request_sense *reqbuf)
+{
+ int msf_flag;
+ int stat, ntracks, i;
+ struct atapi_toc *toc = drive->cdrom_info.toc;
+ struct {
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent;
+ } ms_tmp;
+
+ if (toc == NULL)
+ {
+ /* Try to allocate space. */
+ toc = (struct atapi_toc *) kmalloc (sizeof (struct atapi_toc),
+ GFP_KERNEL);
+ drive->cdrom_info.toc = toc;
+ }
+
+ if (toc == NULL)
+ {
+ printk ("%s: No cdrom TOC buffer!\n", drive->name);
+ return -EIO;
+ }
+
+ /* Check to see if the existing data is still valid.
+ If it is, just return. */
+ if (CDROM_STATE_FLAGS (drive)->toc_valid)
+ (void) cdrom_check_status (drive, NULL);
+
+ if (CDROM_STATE_FLAGS (drive)->toc_valid) return 0;
+
+#if STANDARD_ATAPI
+ msf_flag = 0;
+#else /* not STANDARD_ATAPI */
+ /* Some drives can't return TOC data in LBA format. */
+ msf_flag = (CDROM_CONFIG_FLAGS (drive)->no_lba_toc);
+#endif /* not STANDARD_ATAPI */
+
+ /* First read just the header, so we know how long the TOC is. */
+ stat = cdrom_read_tocentry (drive, 0, msf_flag, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ sizeof (struct atapi_toc_entry),
+ reqbuf);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ /* hopefully the length is not BCD, too ;-| */
+ }
+#endif /* not STANDARD_ATAPI */
+
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (ntracks <= 0) return -EIO;
+ if (ntracks > MAX_TRACKS) ntracks = MAX_TRACKS;
+
+ /* Now read the whole schmeer. */
+ stat = cdrom_read_tocentry (drive, 0, msf_flag, 0, (char *)&toc->hdr,
+ sizeof (struct atapi_toc_header) +
+ (ntracks+1) * sizeof (struct atapi_toc_entry),
+ reqbuf);
+ if (stat) return stat;
+ toc->hdr.toc_length = ntohs (toc->hdr.toc_length);
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ toc->hdr.first_track = bcd2bin (toc->hdr.first_track);
+ toc->hdr.last_track = bcd2bin (toc->hdr.last_track);
+ /* hopefully the length is not BCD, too ;-| */
+ }
+#endif /* not STANDARD_ATAPI */
+
+ for (i=0; i<=ntracks; i++)
+ {
+#if ! STANDARD_ATAPI
+ if (msf_flag)
+ {
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ toc->ent[i].track = bcd2bin (toc->ent[i].track);
+ toc->ent[i].addr.msf.m = bcd2bin (toc->ent[i].addr.msf.m);
+ toc->ent[i].addr.msf.s = bcd2bin (toc->ent[i].addr.msf.s);
+ toc->ent[i].addr.msf.f = bcd2bin (toc->ent[i].addr.msf.f);
+ }
+ toc->ent[i].addr.lba = msf_to_lba (toc->ent[i].addr.msf.m,
+ toc->ent[i].addr.msf.s,
+ toc->ent[i].addr.msf.f);
+ }
+ else
+#endif /* not STANDARD_ATAPI */
+ toc->ent[i].addr.lba = ntohl (toc->ent[i].addr.lba);
+ }
+
+ /* Read the multisession information. */
+ stat = cdrom_read_tocentry (drive, 0, msf_flag, 1,
+ (char *)&ms_tmp, sizeof (ms_tmp),
+ reqbuf);
+ if (stat) return stat;
+#if ! STANDARD_ATAPI
+ if (msf_flag)
+ toc->last_session_lba = msf_to_lba (ms_tmp.ent.addr.msf.m,
+ ms_tmp.ent.addr.msf.s,
+ ms_tmp.ent.addr.msf.f);
+ else
+#endif /* not STANDARD_ATAPI */
+ toc->last_session_lba = ntohl (ms_tmp.ent.addr.lba);
+
+ toc->xa_flag = (ms_tmp.hdr.first_track != ms_tmp.hdr.last_track);
+
+ /* Now try to get the total cdrom capacity. */
+ stat = cdrom_read_capacity (drive, &toc->capacity, reqbuf);
+ if (stat) toc->capacity = 0x1fffff;
+
+ HWIF(drive)->gd->sizes[drive->select.b.unit << PARTN_BITS]
+ = toc->capacity * SECTORS_PER_FRAME;
+ drive->part[0].nr_sects = toc->capacity * SECTORS_PER_FRAME;
+
+ /* Remember that we've read this stuff. */
+ CDROM_STATE_FLAGS (drive)->toc_valid = 1;
+
+ return 0;
+}
+
+
+static int
+cdrom_read_subchannel (ide_drive_t *drive,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = SCMD_READ_SUBCHANNEL;
+ pc.c[2] = 0x40; /* request subQ data */
+ pc.c[3] = 0x01; /* Format 1: current position */
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+/* modeflag: 0 = current, 1 = changeable mask, 2 = default, 3 = saved */
+static int
+cdrom_mode_sense (ide_drive_t *drive, int pageno, int modeflag,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+ pc.c[0] = MODE_SENSE_10;
+ pc.c[2] = pageno | (modeflag << 6);
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_mode_select (ide_drive_t *drive, int pageno, char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = - buflen;
+ pc.c[0] = MODE_SELECT_10;
+ pc.c[1] = 0x10;
+ pc.c[2] = pageno;
+ pc.c[7] = (buflen >> 8);
+ pc.c[8] = (buflen & 0xff);
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+static int
+cdrom_play_lba_range_play12 (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PLAYAUDIO12;
+#ifdef __alpha__
+ stq_u(((long) htonl (lba_end - lba_start) << 32) | htonl(lba_start),
+ (unsigned long *) &pc.c[2]);
+#else
+ *(int *)(&pc.c[2]) = htonl (lba_start);
+ *(int *)(&pc.c[6]) = htonl (lba_end - lba_start);
+#endif
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+
+
+#if ! STANDARD_ATAPI
+static int
+cdrom_play_lba_range_msf (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.c[0] = SCMD_PLAYAUDIO_MSF;
+ lba_to_msf (lba_start, &pc.c[3], &pc.c[4], &pc.c[5]);
+ lba_to_msf (lba_end-1, &pc.c[6], &pc.c[7], &pc.c[8]);
+
+ if (CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd)
+ {
+ pc.c[3] = bin2bcd (pc.c[3]);
+ pc.c[4] = bin2bcd (pc.c[4]);
+ pc.c[5] = bin2bcd (pc.c[5]);
+ pc.c[6] = bin2bcd (pc.c[6]);
+ pc.c[7] = bin2bcd (pc.c[7]);
+ pc.c[8] = bin2bcd (pc.c[8]);
+ }
+
+ return cdrom_queue_packet_command (drive, &pc);
+}
+#endif /* not STANDARD_ATAPI */
+
+
+static int
+cdrom_play_lba_range_1 (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ /* This is rather annoying.
+ My NEC-260 won't recognize group 5 commands such as PLAYAUDIO12;
+ the only way to get it to play more than 64k of blocks at once
+ seems to be the PLAYAUDIO_MSF command. However, the parameters
+ the NEC 260 wants for the PLAYMSF command are incompatible with
+ the new version of the spec.
+
+ So what i'll try is this. First try for PLAYAUDIO12. If it works,
+ great. Otherwise, if the drive reports an illegal command code,
+ try PLAYAUDIO_MSF using the NEC 260-style bcd parameters. */
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->no_playaudio12)
+ return cdrom_play_lba_range_msf (drive, lba_start, lba_end, reqbuf);
+ else
+#endif /* not STANDARD_ATAPI */
+ {
+ int stat;
+ struct atapi_request_sense my_reqbuf;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ stat = cdrom_play_lba_range_play12 (drive, lba_start, lba_end, reqbuf);
+ if (stat == 0) return 0;
+
+#if ! STANDARD_ATAPI
+ /* It failed. Try to find out why. */
+ if (reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x20)
+ {
+ /* The drive didn't recognize the command.
+ Retry with the MSF variant. */
+ printk ("%s: Drive does not support PLAYAUDIO12; "
+ "trying PLAYAUDIO_MSF\n", drive->name);
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 1;
+ return cdrom_play_lba_range_msf (drive, lba_start, lba_end, reqbuf);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ /* Failed for some other reason. Give up. */
+ return stat;
+ }
+}
+
+
+/* Play audio starting at LBA LBA_START and finishing with the
+ LBA before LBA_END. */
+static int
+cdrom_play_lba_range (ide_drive_t *drive, int lba_start, int lba_end,
+ struct atapi_request_sense *reqbuf)
+{
+ int i, stat;
+ struct atapi_request_sense my_reqbuf;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ /* Some drives, will, for certain audio cds,
+ give an error if you ask them to play the entire cd using the
+ values which are returned in the TOC. The play will succeed, however,
+ if the ending address is adjusted downwards by a few frames. */
+ for (i=0; i<75; i++)
+ {
+ stat = cdrom_play_lba_range_1 (drive, lba_start, lba_end, reqbuf);
+
+ if (stat == 0 ||
+ !(reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x24))
+ return stat;
+
+ --lba_end;
+ if (lba_end <= lba_start) break;
+ }
+
+ return stat;
+}
+
+
+static
+int cdrom_get_toc_entry (ide_drive_t *drive, int track,
+ struct atapi_toc_entry **ent,
+ struct atapi_request_sense *reqbuf)
+{
+ int stat, ntracks;
+ struct atapi_toc *toc;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (drive, reqbuf);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ /* Check validity of requested track number. */
+ ntracks = toc->hdr.last_track - toc->hdr.first_track + 1;
+ if (track == CDROM_LEADOUT)
+ *ent = &toc->ent[ntracks];
+ else if (track < toc->hdr.first_track ||
+ track > toc->hdr.last_track)
+ return -EINVAL;
+ else
+ *ent = &toc->ent[track - toc->hdr.first_track];
+
+ return 0;
+}
+
+
+static int
+cdrom_read_block (ide_drive_t *drive, int format, int lba,
+ char *buf, int buflen,
+ struct atapi_request_sense *reqbuf)
+{
+ struct packet_command pc;
+ struct atapi_request_sense my_reqbuf;
+ int stat;
+
+ if (reqbuf == NULL)
+ reqbuf = &my_reqbuf;
+
+ memset (&pc, 0, sizeof (pc));
+ pc.sense_data = reqbuf;
+
+ pc.buffer = buf;
+ pc.buflen = buflen;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->old_readcd)
+ pc.c[0] = 0xd4;
+ else
+#endif /* not STANDARD_ATAPI */
+ pc.c[0] = READ_CD;
+
+ pc.c[1] = (format << 2);
+#ifdef __alpha__
+ stl_u(htonl (lba), (unsigned int *) &pc.c[2]);
+#else
+ *(int *)(&pc.c[2]) = htonl (lba);
+#endif
+ pc.c[8] = 1; /* one block */
+ pc.c[9] = 0x10;
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+
+#if ! STANDARD_ATAPI
+ /* If the drive doesn't recognize the READ CD opcode, retry the command
+ with an older opcode for that command. */
+ if (stat && reqbuf->sense_key == ILLEGAL_REQUEST && reqbuf->asc == 0x20 &&
+ CDROM_CONFIG_FLAGS (drive)->old_readcd == 0)
+ {
+ printk ("%s: Drive does not recognize READ_CD; trying opcode 0xd4\n",
+ drive->name);
+ CDROM_CONFIG_FLAGS (drive)->old_readcd = 1;
+ return cdrom_read_block (drive, format, lba, buf, buflen, reqbuf);
+ }
+#endif /* not STANDARD_ATAPI */
+
+ return stat;
+}
+
+
+int ide_cdrom_ioctl (ide_drive_t *drive, struct inode *inode,
+ struct file *file, unsigned int cmd, unsigned long arg)
+{
+ switch (cmd)
+ {
+ case CDROMEJECT:
+ {
+ int stat;
+
+ if (drive->usage > 1)
+ return -EBUSY;
+
+ stat = cdrom_lockdoor (drive, 0, NULL);
+ if (stat) return stat;
+
+ return cdrom_eject (drive, 0, NULL);
+ }
+
+ case CDROMEJECT_SW:
+ {
+ CDROM_STATE_FLAGS (drive)->eject_on_close = arg;
+ return 0;
+ }
+
+ case CDROMPAUSE:
+ return cdrom_pause (drive, 1, NULL);
+
+ case CDROMRESUME:
+ return cdrom_pause (drive, 0, NULL);
+
+ case CDROMSTART:
+ return cdrom_startstop (drive, 1, NULL);
+
+ case CDROMSTOP:
+ {
+ int stat;
+
+ stat = cdrom_startstop (drive, 0, NULL);
+ if (stat) return stat;
+ /* pit says the Dolphin needs this. */
+ return cdrom_eject (drive, 1, NULL);
+ }
+
+ case CDROMPLAYMSF:
+ {
+ struct cdrom_msf msf;
+ int stat, lba_start, lba_end;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (msf));
+ if (stat) return stat;
+
+ memcpy_fromfs (&msf, (void *) arg, sizeof(msf));
+
+ lba_start = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0,
+ msf.cdmsf_frame0);
+ lba_end = msf_to_lba (msf.cdmsf_min1, msf.cdmsf_sec1,
+ msf.cdmsf_frame1) + 1;
+
+ if (lba_end <= lba_start) return -EINVAL;
+
+ return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+ }
+
+ /* Like just about every other Linux cdrom driver, we ignore the
+ index part of the request here. */
+ case CDROMPLAYTRKIND:
+ {
+ int stat, lba_start, lba_end;
+ struct cdrom_ti ti;
+ struct atapi_toc_entry *first_toc, *last_toc;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ti));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ti, (void *) arg, sizeof(ti));
+
+ stat = cdrom_get_toc_entry (drive, ti.cdti_trk0, &first_toc, NULL);
+ if (stat) return stat;
+ stat = cdrom_get_toc_entry (drive, ti.cdti_trk1, &last_toc, NULL);
+ if (stat) return stat;
+
+ if (ti.cdti_trk1 != CDROM_LEADOUT) ++last_toc;
+ lba_start = first_toc->addr.lba;
+ lba_end = last_toc->addr.lba;
+
+ if (lba_end <= lba_start) return -EINVAL;
+
+ return cdrom_play_lba_range (drive, lba_start, lba_end, NULL);
+ }
+
+ case CDROMREADTOCHDR:
+ {
+ int stat;
+ struct cdrom_tochdr tochdr;
+ struct atapi_toc *toc;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tochdr));
+ if (stat) return stat;
+
+ /* Make sure our saved TOC is valid. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+ tochdr.cdth_trk0 = toc->hdr.first_track;
+ tochdr.cdth_trk1 = toc->hdr.last_track;
+
+ memcpy_tofs ((void *) arg, &tochdr, sizeof (tochdr));
+
+ return stat;
+ }
+
+ case CDROMREADTOCENTRY:
+ {
+ int stat;
+ struct cdrom_tocentry tocentry;
+ struct atapi_toc_entry *toce;
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (tocentry));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (tocentry));
+ if (stat) return stat;
+
+ memcpy_fromfs (&tocentry, (void *) arg, sizeof (tocentry));
+
+ stat = cdrom_get_toc_entry (drive, tocentry.cdte_track, &toce, NULL);
+ if (stat) return stat;
+
+ tocentry.cdte_ctrl = toce->control;
+ tocentry.cdte_adr = toce->adr;
+
+ if (tocentry.cdte_format == CDROM_MSF)
+ {
+ /* convert to MSF */
+ lba_to_msf (toce->addr.lba,
+ &tocentry.cdte_addr.msf.minute,
+ &tocentry.cdte_addr.msf.second,
+ &tocentry.cdte_addr.msf.frame);
+ }
+ else
+ tocentry.cdte_addr.lba = toce->addr.lba;
+
+ memcpy_tofs ((void *) arg, &tocentry, sizeof (tocentry));
+
+ return stat;
+ }
+
+ case CDROMSUBCHNL:
+ {
+ struct atapi_cdrom_subchnl scbuf;
+ int stat, abs_lba, rel_lba;
+ struct cdrom_subchnl subchnl;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (subchnl));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (subchnl));
+ if (stat) return stat;
+
+ memcpy_fromfs (&subchnl, (void *) arg, sizeof (subchnl));
+
+ stat = cdrom_read_subchannel (drive, (char *)&scbuf, sizeof (scbuf),
+ NULL);
+ if (stat) return stat;
+
+#if ! STANDARD_ATAPI
+ if (CDROM_CONFIG_FLAGS (drive)->vertos_lossage)
+ {
+ abs_lba = msf_to_lba (bcd2bin (scbuf.acdsc_absaddr.msf.minute),
+ bcd2bin (scbuf.acdsc_absaddr.msf.second),
+ bcd2bin (scbuf.acdsc_absaddr.msf.frame));
+ rel_lba = msf_to_lba (bcd2bin (scbuf.acdsc_reladdr.msf.minute),
+ bcd2bin (scbuf.acdsc_reladdr.msf.second),
+ bcd2bin (scbuf.acdsc_reladdr.msf.frame));
+ scbuf.acdsc_trk = bcd2bin (scbuf.acdsc_trk);
+ }
+ else
+#endif /* not STANDARD_ATAPI */
+ {
+ abs_lba = ntohl (scbuf.acdsc_absaddr.lba);
+ rel_lba = ntohl (scbuf.acdsc_reladdr.lba);
+ }
+
+ if (subchnl.cdsc_format == CDROM_MSF)
+ {
+ lba_to_msf (abs_lba,
+ &subchnl.cdsc_absaddr.msf.minute,
+ &subchnl.cdsc_absaddr.msf.second,
+ &subchnl.cdsc_absaddr.msf.frame);
+ lba_to_msf (rel_lba,
+ &subchnl.cdsc_reladdr.msf.minute,
+ &subchnl.cdsc_reladdr.msf.second,
+ &subchnl.cdsc_reladdr.msf.frame);
+ }
+ else
+ {
+ subchnl.cdsc_absaddr.lba = abs_lba;
+ subchnl.cdsc_reladdr.lba = rel_lba;
+ }
+
+ subchnl.cdsc_audiostatus = scbuf.acdsc_audiostatus;
+ subchnl.cdsc_ctrl = scbuf.acdsc_ctrl;
+ subchnl.cdsc_trk = scbuf.acdsc_trk;
+ subchnl.cdsc_ind = scbuf.acdsc_ind;
+
+ memcpy_tofs ((void *) arg, &subchnl, sizeof (subchnl));
+
+ return stat;
+ }
+
+ case CDROMVOLCTRL:
+ {
+ struct cdrom_volctrl volctrl;
+ char buffer[24], mask[24];
+ int stat;
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (volctrl));
+ if (stat) return stat;
+ memcpy_fromfs (&volctrl, (void *) arg, sizeof (volctrl));
+
+ stat = cdrom_mode_sense (drive, 0x0e, 0, buffer, sizeof (buffer),NULL);
+ if (stat) return stat;
+ stat = cdrom_mode_sense (drive, 0x0e, 1, mask , sizeof (buffer),NULL);
+ if (stat) return stat;
+
+ buffer[1] = buffer[2] = 0;
+
+ buffer[17] = volctrl.channel0 & mask[17];
+ buffer[19] = volctrl.channel1 & mask[19];
+ buffer[21] = volctrl.channel2 & mask[21];
+ buffer[23] = volctrl.channel3 & mask[23];
+
+ return cdrom_mode_select (drive, 0x0e, buffer, sizeof (buffer), NULL);
+ }
+
+ case CDROMVOLREAD:
+ {
+ struct cdrom_volctrl volctrl;
+ char buffer[24];
+ int stat;
+
+ stat = verify_area (VERIFY_WRITE, (void *) arg, sizeof (volctrl));
+ if (stat) return stat;
+
+ stat = cdrom_mode_sense (drive, 0x0e, 0, buffer, sizeof (buffer), NULL);
+ if (stat) return stat;
+
+ volctrl.channel0 = buffer[17];
+ volctrl.channel1 = buffer[19];
+ volctrl.channel2 = buffer[21];
+ volctrl.channel3 = buffer[23];
+
+ memcpy_tofs ((void *) arg, &volctrl, sizeof (volctrl));
+
+ return 0;
+ }
+
+ case CDROMMULTISESSION:
+ {
+ struct cdrom_multisession ms_info;
+ struct atapi_toc *toc;
+ int stat;
+
+ stat = verify_area (VERIFY_READ, (void *)arg, sizeof (ms_info));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (void *)arg, sizeof (ms_info));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ms_info, (void *)arg, sizeof (ms_info));
+
+ /* Make sure the TOC information is valid. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ if (ms_info.addr_format == CDROM_MSF)
+ lba_to_msf (toc->last_session_lba,
+ &ms_info.addr.msf.minute,
+ &ms_info.addr.msf.second,
+ &ms_info.addr.msf.frame);
+
+ else if (ms_info.addr_format == CDROM_LBA)
+ ms_info.addr.lba = toc->last_session_lba;
+
+ else
+ return -EINVAL;
+
+ ms_info.xa_flag = toc->xa_flag;
+
+ memcpy_tofs ((void *)arg, &ms_info, sizeof (ms_info));
+
+ return 0;
+ }
+
+ /* Read 2352 byte blocks from audio tracks. */
+ case CDROMREADAUDIO:
+ {
+ int stat, lba;
+ struct atapi_toc *toc;
+ struct cdrom_read_audio ra;
+ char buf[CD_FRAMESIZE_RAW];
+
+ /* Make sure the TOC is up to date. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ stat = verify_area (VERIFY_READ, (char *)arg, sizeof (ra));
+ if (stat) return stat;
+
+ memcpy_fromfs (&ra, (void *)arg, sizeof (ra));
+
+ if (ra.nframes < 0 || ra.nframes > toc->capacity)
+ return -EINVAL;
+ else if (ra.nframes == 0)
+ return 0;
+
+ stat = verify_area (VERIFY_WRITE, (char *)ra.buf,
+ ra.nframes * CD_FRAMESIZE_RAW);
+ if (stat) return stat;
+
+ if (ra.addr_format == CDROM_MSF)
+ lba = msf_to_lba (ra.addr.msf.minute, ra.addr.msf.second,
+ ra.addr.msf.frame);
+
+ else if (ra.addr_format == CDROM_LBA)
+ lba = ra.addr.lba;
+
+ else
+ return -EINVAL;
+
+ if (lba < 0 || lba >= toc->capacity)
+ return -EINVAL;
+
+ while (ra.nframes > 0)
+ {
+ stat = cdrom_read_block (drive, 1, lba, buf,
+ CD_FRAMESIZE_RAW, NULL);
+ if (stat) return stat;
+ memcpy_tofs (ra.buf, buf, CD_FRAMESIZE_RAW);
+ ra.buf += CD_FRAMESIZE_RAW;
+ --ra.nframes;
+ ++lba;
+ }
+
+ return 0;
+ }
+
+ case CDROMREADMODE1:
+ case CDROMREADMODE2:
+ {
+ struct cdrom_msf msf;
+ int blocksize, format, stat, lba;
+ struct atapi_toc *toc;
+ char buf[CD_FRAMESIZE_RAW0];
+
+ if (cmd == CDROMREADMODE1)
+ {
+ blocksize = CD_FRAMESIZE;
+ format = 2;
+ }
+ else
+ {
+ blocksize = CD_FRAMESIZE_RAW0;
+ format = 3;
+ }
+
+ stat = verify_area (VERIFY_READ, (char *)arg, sizeof (msf));
+ if (stat) return stat;
+ stat = verify_area (VERIFY_WRITE, (char *)arg, blocksize);
+ if (stat) return stat;
+
+ memcpy_fromfs (&msf, (void *)arg, sizeof (msf));
+
+ lba = msf_to_lba (msf.cdmsf_min0, msf.cdmsf_sec0, msf.cdmsf_frame0);
+
+ /* Make sure the TOC is up to date. */
+ stat = cdrom_read_toc (drive, NULL);
+ if (stat) return stat;
+
+ toc = drive->cdrom_info.toc;
+
+ if (lba < 0 || lba >= toc->capacity)
+ return -EINVAL;
+
+ stat = cdrom_read_block (drive, format, lba, buf, blocksize, NULL);
+ if (stat) return stat;
+
+ memcpy_tofs ((char *)arg, buf, blocksize);
+ return 0;
+ }
+
+#if 0 /* Doesn't work reliably yet. */
+ case CDROMRESET:
+ {
+ struct request req;
+ ide_init_drive_cmd (&req);
+ req.cmd = RESET_DRIVE_COMMAND;
+ return ide_do_drive_cmd (drive, &req, ide_wait);
+ }
+#endif
+
+
+#ifdef TEST
+ case 0x1234:
+ {
+ int stat;
+ struct packet_command pc;
+ int len, lena;
+
+ memset (&pc, 0, sizeof (pc));
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (pc.c));
+ if (stat) return stat;
+ memcpy_fromfs (&pc.c, (void *) arg, sizeof (pc.c));
+ arg += sizeof (pc.c);
+
+ stat = verify_area (VERIFY_READ, (void *) arg, sizeof (len));
+ if (stat) return stat;
+ memcpy_fromfs (&len, (void *) arg , sizeof (len));
+ arg += sizeof (len);
+
+ if (len > 0) {
+ stat = verify_area (VERIFY_WRITE, (void *) arg, len);
+ if (stat) return stat;
+ }
+
+ lena = len;
+ if (lena < 0) lena = 0;
+
+ {
+ char buf[lena];
+ if (len > 0) {
+ pc.buflen = len;
+ pc.buffer = buf;
+ }
+
+ stat = cdrom_queue_packet_command (drive, &pc);
+
+ if (len > 0)
+ memcpy_tofs ((void *)arg, buf, len);
+ }
+
+ return stat;
+ }
+#endif
+
+ default:
+ return -EPERM;
+ }
+
+}
+
+
+
+/****************************************************************************
+ * Other driver requests (open, close, check media change).
+ */
+
+int ide_cdrom_check_media_change (ide_drive_t *drive)
+{
+ int retval;
+
+ (void) cdrom_check_status (drive, NULL);
+
+ retval = CDROM_STATE_FLAGS (drive)->media_changed;
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+
+ return retval;
+}
+
+
+int ide_cdrom_open (struct inode *ip, struct file *fp, ide_drive_t *drive)
+{
+ /* no write access */
+ if (fp->f_mode & 2)
+ {
+ --drive->usage;
+ return -EROFS;
+ }
+
+ /* If this is the first open, check the drive status. */
+ if (drive->usage == 1)
+ {
+ int stat;
+ struct atapi_request_sense my_reqbuf;
+ my_reqbuf.sense_key = 0;
+
+ /* Get the drive status. */
+ stat = cdrom_check_status (drive, &my_reqbuf);
+
+ /* If the tray is open, try to close it. */
+ if (stat && my_reqbuf.sense_key == NOT_READY)
+ {
+ cdrom_eject (drive, 1, &my_reqbuf);
+ stat = cdrom_check_status (drive, &my_reqbuf);
+ }
+
+ /* Return an error if there are still problems. */
+ if (stat && my_reqbuf.sense_key != UNIT_ATTENTION)
+ {
+ --drive->usage;
+ return -ENXIO;
+ }
+
+ /* Now lock the door. */
+ (void) cdrom_lockdoor (drive, 1, &my_reqbuf);
+
+ /* And try to read the TOC information now. */
+ (void) cdrom_read_toc (drive, &my_reqbuf);
+ }
+
+ return 0;
+}
+
+
+/*
+ * Close down the device. Invalidate all cached blocks.
+ */
+
+void ide_cdrom_release (struct inode *inode, struct file *file, ide_drive_t *drive)
+{
+ if (drive->usage == 0)
+ {
+ invalidate_buffers (inode->i_rdev);
+
+ /* Unlock the door. */
+ (void) cdrom_lockdoor (drive, 0, NULL);
+
+ /* Do an eject if we were requested to do so. */
+ if (CDROM_STATE_FLAGS (drive)->eject_on_close)
+ (void) cdrom_eject (drive, 0, NULL);
+ }
+}
+
+
+
+/****************************************************************************
+ * Device initialization.
+ */
+
+void ide_cdrom_setup (ide_drive_t *drive)
+{
+ blksize_size[HWIF(drive)->major][drive->select.b.unit << PARTN_BITS] = CD_FRAMESIZE;
+
+ drive->special.all = 0;
+ drive->ready_stat = 0;
+
+ CDROM_STATE_FLAGS (drive)->media_changed = 0;
+ CDROM_STATE_FLAGS (drive)->toc_valid = 0;
+ CDROM_STATE_FLAGS (drive)->door_locked = 0;
+
+ /* Turn this off by default, since many people don't like it. */
+ CDROM_STATE_FLAGS (drive)->eject_on_close= 0;
+
+#if NO_DOOR_LOCKING
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 1;
+#else
+ CDROM_CONFIG_FLAGS (drive)->no_doorlock = 0;
+#endif
+
+ if (drive->id != NULL) {
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt =
+ ((drive->id->config & 0x0060) == 0x20);
+ } else {
+ CDROM_CONFIG_FLAGS (drive)->drq_interrupt = 0;
+ }
+
+#if ! STANDARD_ATAPI
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 0;
+ CDROM_CONFIG_FLAGS (drive)->old_readcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 0;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 0;
+ CDROM_CONFIG_FLAGS (drive)->vertos_lossage = 0;
+
+ if (drive->id != NULL) {
+ /* Accommodate some broken drives... */
+ if (strcmp (drive->id->model, "CD220E") == 0 ||
+ strcmp (drive->id->model, "CD") == 0) /* Creative Labs */
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ else if (strcmp (drive->id->model, "TO-ICSLYAL") == 0 || /* Acer CD525E */
+ strcmp (drive->id->model, "OTI-SCYLLA") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ /* I don't know who makes this.
+ Francesco Messineo <sidera@ccii.unipi.it> says this one's broken too. */
+ else if (strcmp (drive->id->model, "DCI-2S10") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ else if (strcmp (drive->id->model, "CDA26803I SE") == 0) /* Aztech */
+ {
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ /* This drive _also_ does not implement PLAYAUDIO12 correctly. */
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+ }
+
+ /* Vertos 300.
+ There seem to be at least two different, incompatible versions
+ of this drive floating around. Luckily, they appear to return their
+ id strings with different byte orderings. */
+ else if (strcmp (drive->id->model, "V003S0DS") == 0)
+ {
+ CDROM_CONFIG_FLAGS (drive)->vertos_lossage = 1;
+ CDROM_CONFIG_FLAGS (drive)->playmsf_uses_bcd = 1;
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+ }
+ else if (strcmp (drive->id->model, "0V300SSD") == 0 ||
+ strcmp (drive->id->model, "V003M0DP") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ /* Vertos 400. */
+ else if (strcmp (drive->id->model, "V004E0DT") == 0 ||
+ strcmp (drive->id->model, "0V400ETD") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_lba_toc = 1;
+
+ else if ( strcmp (drive->id->model, "CD-ROM CDU55D") == 0) /*sony cdu55d */
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+
+ else if (strcmp (drive->id->model, "CD-ROM CDU55E") == 0)
+ CDROM_CONFIG_FLAGS (drive)->no_playaudio12 = 1;
+ } /* drive-id != NULL */
+#endif /* not STANDARD_ATAPI */
+
+ drive->cdrom_info.toc = NULL;
+ drive->cdrom_info.sector_buffer = NULL;
+ drive->cdrom_info.sector_buffered = 0;
+ drive->cdrom_info.nsectors_buffered = 0;
+}
+
+
+
+/*
+ * TODO:
+ * CDROM_GET_UPC
+ * CDROMRESET
+ * Lock the door when a read request completes successfully and the
+ * door is not already locked. Also try to reorganize to reduce
+ * duplicated functionality between read and ioctl paths?
+ * Establish interfaces for an IDE port driver, and break out the cdrom
+ * code into a loadable module.
+ * Support changers.
+ * Write some real documentation.
+ */
diff --git a/i386/i386at/gpl/linux/block/ide.c b/i386/i386at/gpl/linux/block/ide.c
new file mode 100644
index 00000000..4b7c5e95
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide.c
@@ -0,0 +1,3087 @@
+/*
+ * linux/drivers/block/ide.c Version 5.28 Feb 11, 1996
+ *
+ * Copyright (C) 1994-1996 Linus Torvalds & authors (see below)
+ */
+#define _IDE_C /* needed by <linux/blk.h> */
+
+/*
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ *
+ * It is easy to extend ide.c to handle more than four interfaces:
+ *
+ * Change the MAX_HWIFS constant in ide.h.
+ *
+ * Define some new major numbers (in major.h), and insert them into
+ * the ide_hwif_to_major table in ide.c.
+ *
+ * Fill in the extra values for the new interfaces into the two tables
+ * inside ide.c: default_io_base[] and default_irqs[].
+ *
+ * Create the new request handlers by cloning "do_ide3_request()"
+ * for each new interface, and add them to the switch statement
+ * in the ide_init() function in ide.c.
+ *
+ * Recompile, create the new /dev/ entries, and it will probably work.
+ *
+ * From hd.c:
+ * |
+ * | It traverses the request-list, using interrupts to jump between functions.
+ * | As nearly all functions can be called within interrupts, we may not sleep.
+ * | Special care is recommended. Have Fun!
+ * |
+ * | modified by Drew Eckhardt to check nr of hd's from the CMOS.
+ * |
+ * | Thanks to Branko Lankester, lankeste@fwi.uva.nl, who found a bug
+ * | in the early extended-partition checks and added DM partitions.
+ * |
+ * | Early work on error handling by Mika Liljeberg (liljeber@cs.Helsinki.FI).
+ * |
+ * | IRQ-unmask, drive-id, multiple-mode, support for ">16 heads",
+ * | and general streamlining by Mark Lord (mlord@bnr.ca).
+ *
+ * October, 1994 -- Complete line-by-line overhaul for linux 1.1.x, by:
+ *
+ * Mark Lord (mlord@bnr.ca) (IDE Perf.Pkg)
+ * Delman Lee (delman@mipg.upenn.edu) ("Mr. atdisk2")
+ * Petri Mattila (ptjmatti@kruuna.helsinki.fi) (EIDE stuff)
+ * Scott Snyder (snyder@fnald0.fnal.gov) (ATAPI IDE cd-rom)
+ *
+ * Maintained by Mark Lord (mlord@bnr.ca): ide.c, ide.h, triton.c, hd.c, ..
+ *
+ * This was a rewrite of just about everything from hd.c, though some original
+ * code is still sprinkled about. Think of it as a major evolution, with
+ * inspiration from lots of linux users, esp. hamish@zot.apana.org.au
+ *
+ * Version 1.0 ALPHA initial code, primary i/f working okay
+ * Version 1.3 BETA dual i/f on shared irq tested & working!
+ * Version 1.4 BETA added auto probing for irq(s)
+ * Version 1.5 BETA added ALPHA (untested) support for IDE cd-roms,
+ * ...
+ * Version 3.5 correct the bios_cyl field if it's too small
+ * (linux 1.1.76) (to help fdisk with brain-dead BIOSs)
+ * Version 3.6 cosmetic corrections to comments and stuff
+ * (linux 1.1.77) reorganise probing code to make it understandable
+ * added halfway retry to probing for drive identification
+ * added "hdx=noprobe" command line option
+ * allow setting multmode even when identification fails
+ * Version 3.7 move set_geometry=1 from do_identify() to ide_init()
+ * increase DRQ_WAIT to eliminate nuisance messages
+ * wait for DRQ_STAT instead of DATA_READY during probing
+ * (courtesy of Gary Thomas gary@efland.UU.NET)
+ * Version 3.8 fixed byte-swapping for confused Mitsumi cdrom drives
+ * update of ide-cd.c from Scott, allows blocksize=1024
+ * cdrom probe fixes, inspired by jprang@uni-duisburg.de
+ * Version 3.9 don't use LBA if lba_capacity looks funny
+ * correct the drive capacity calculations
+ * fix probing for old Seagates without IDE_ALTSTATUS_REG
+ * fix byte-ordering for some NEC cdrom drives
+ * Version 3.10 disable multiple mode by default; was causing trouble
+ * Version 3.11 fix mis-identification of old WD disks as cdroms
+ * Version 3,12 simplify logic for selecting initial mult_count
+ * (fixes problems with buggy WD drives)
+ * Version 3.13 remove excess "multiple mode disabled" messages
+ * Version 3.14 fix ide_error() handling of BUSY_STAT
+ * fix byte-swapped cdrom strings (again.. arghh!)
+ * ignore INDEX bit when checking the ALTSTATUS reg
+ * Version 3.15 add SINGLE_THREADED flag for use with dual-CMD i/f
+ * ignore WRERR_STAT for non-write operations
+ * added vlb_sync support for DC-2000A & others,
+ * (incl. some Promise chips), courtesy of Frank Gockel
+ * Version 3.16 convert vlb_32bit and vlb_sync into runtime flags
+ * add ioctls to get/set VLB flags (HDIO_[SG]ET_CHIPSET)
+ * rename SINGLE_THREADED to SUPPORT_SERIALIZE,
+ * add boot flag to "serialize" operation for CMD i/f
+ * add optional support for DTC2278 interfaces,
+ * courtesy of andy@cercle.cts.com (Dyan Wile).
+ * add boot flag to enable "dtc2278" probe
+ * add probe to avoid EATA (SCSI) interfaces,
+ * courtesy of neuffer@goofy.zdv.uni-mainz.de.
+ * Version 4.00 tidy up verify_area() calls - heiko@colossus.escape.de
+ * add flag to ignore WRERR_STAT for some drives
+ * courtesy of David.H.West@um.cc.umich.edu
+ * assembly syntax tweak to vlb_sync
+ * removeable drive support from scuba@cs.tu-berlin.de
+ * add transparent support for DiskManager-6.0x "Dynamic
+ * Disk Overlay" (DDO), most of this is in genhd.c
+ * eliminate "multiple mode turned off" message at boot
+ * Version 4.10 fix bug in ioctl for "hdparm -c3"
+ * fix DM6:DDO support -- now works with LILO, fdisk, ...
+ * don't treat some naughty WD drives as removeable
+ * Version 4.11 updated DM6 support using info provided by OnTrack
+ * Version 5.00 major overhaul, multmode setting fixed, vlb_sync fixed
+ * added support for 3rd/4th/alternative IDE ports
+ * created ide.h; ide-cd.c now compiles separate from ide.c
+ * hopefully fixed infinite "unexpected_intr" from cdroms
+ * zillions of other changes and restructuring
+ * somehow reduced overall memory usage by several kB
+ * probably slowed things down slightly, but worth it
+ * Version 5.01 AT LAST!! Finally understood why "unexpected_intr"
+ * was happening at various times/places: whenever the
+ * ide-interface's ctl_port was used to "mask" the irq,
+ * it also would trigger an edge in the process of masking
+ * which would result in a self-inflicted interrupt!!
+ * (such a stupid way to build a hardware interrupt mask).
+ * This is now fixed (after a year of head-scratching).
+ * Version 5.02 got rid of need for {enable,disable}_irq_list()
+ * Version 5.03 tune-ups, comments, remove "busy wait" from drive resets
+ * removed PROBE_FOR_IRQS option -- no longer needed
+ * OOOPS! fixed "bad access" bug for 2nd drive on an i/f
+ * Version 5.04 changed "ira %d" to "irq %d" in DEBUG message
+ * added more comments, cleaned up unexpected_intr()
+ * OOOPS! fixed null pointer problem in ide reset code
+ * added autodetect for Triton chipset -- no effect yet
+ * Version 5.05 OOOPS! fixed bug in revalidate_disk()
+ * OOOPS! fixed bug in ide_do_request()
+ * added ATAPI reset sequence for cdroms
+ * Version 5.10 added Bus-Mastered DMA support for Triton Chipset
+ * some (mostly) cosmetic changes
+ * Version 5.11 added ht6560b support by malafoss@snakemail.hut.fi
+ * reworked PCI scanning code
+ * added automatic RZ1000 detection/support
+ * added automatic PCI CMD640 detection/support
+ * added option for VLB CMD640 support
+ * tweaked probe to find cdrom on hdb with disks on hda,hdc
+ * Version 5.12 some performance tuning
+ * added message to alert user to bad /dev/hd[cd] entries
+ * OOOPS! fixed bug in atapi reset
+ * driver now forces "serialize" again for all cmd640 chips
+ * noticed REALLY_SLOW_IO had no effect, moved it to ide.c
+ * made do_drive_cmd() into public ide_do_drive_cmd()
+ * Version 5.13 fixed typo ('B'), thanks to houston@boyd.geog.mcgill.ca
+ * fixed ht6560b support
+ * Version 5.13b (sss) fix problem in calling ide_cdrom_setup()
+ * don't bother invalidating nonexistent partitions
+ * Version 5.14 fixes to cmd640 support.. maybe it works now(?)
+ * added & tested full EZ-DRIVE support -- don't use LILO!
+ * don't enable 2nd CMD640 PCI port during init - conflict
+ * Version 5.15 bug fix in init_cmd640_vlb()
+ * bug fix in interrupt sharing code
+ * Version 5.16 ugh.. fix "serialize" support, broken in 5.15
+ * remove "Huh?" from cmd640 code
+ * added qd6580 interface speed select from Colten Edwards
+ * Version 5.17 kludge around bug in BIOS32 on Intel triton motherboards
+ * Version 5.18 new CMD640 code, moved to cmd640.c, #include'd for now
+ * new UMC8672 code, moved to umc8672.c, #include'd for now
+ * disallow turning on DMA when h/w not capable of DMA
+ * Version 5.19 fix potential infinite timeout on resets
+ * extend reset poll into a general purpose polling scheme
+ * add atapi tape drive support from Gadi Oxman
+ * simplify exit from _intr routines -- no IDE_DO_REQUEST
+ * Version 5.20 leave current rq on blkdev request list during I/O
+ * generalized ide_do_drive_cmd() for tape/cdrom driver use
+ * Version 5.21 fix nasty cdrom/tape bug (ide_preempt was messed up)
+ * Version 5.22 fix ide_xlate_1024() to work with/without drive->id
+ * Version 5.23 miscellaneous touch-ups
+ * Version 5.24 fix #if's for SUPPORT_CMD640
+ * Version 5.25 more touch-ups, fix cdrom resets, ...
+ * cmd640.c now configs/compiles separate from ide.c
+ * Version 5.26 keep_settings now maintains the using_dma flag
+ * fix [EZD] remap message to only output at boot time
+ * fix "bad /dev/ entry" message to say hdc, not hdc0
+ * fix ide_xlate_1024() to respect user specified CHS
+ * use CHS from partn table if it looks translated
+ * re-merged flags chipset,vlb_32bit,vlb_sync into io_32bit
+ * keep track of interface chipset type, when known
+ * add generic PIO mode "tuneproc" mechanism
+ * fix cmd640_vlb option
+ * fix ht6560b support (was completely broken)
+ * umc8672.c now configures/compiles separate from ide.c
+ * move dtc2278 support to dtc2278.c
+ * move ht6560b support to ht6560b.c
+ * move qd6580 support to qd6580.c
+ * add ali14xx support in ali14xx.c
+ * Version 5.27 add [no]autotune parameters to help cmd640
+ * move rz1000 support to rz1000.c
+ * Version 5.28 #include "ide_modes.h"
+ * fix disallow_unmask: now per-interface "no_unmask" bit
+ * force io_32bit to be the same on drive pairs of dtc2278
+ * improved IDE tape error handling, and tape DMA support
+ * bugfix in ide_do_drive_cmd() for cdroms + serialize
+ *
+ * Some additional driver compile-time options are in ide.h
+ *
+ * To do, in likely order of completion:
+ * - add Promise DC4030VL support from peterd@pnd-pc.demon.co.uk
+ * - modify kernel to obtain BIOS geometry for drives on 2nd/3rd/4th i/f
+*/
+
+#if defined (MACH) && !defined (LINUX_IDE_DEBUG)
+#undef DEBUG
+#endif
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/major.h>
+#include <linux/blkdev.h>
+#include <linux/errno.h>
+#include <linux/hdreg.h>
+#include <linux/genhd.h>
+#include <linux/malloc.h>
+
+#include <asm/byteorder.h>
+#include <asm/irq.h>
+#include <asm/segment.h>
+#include <asm/io.h>
+
+#ifdef CONFIG_PCI
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#endif /* CONFIG_PCI */
+
+#include "ide.h"
+#include "ide_modes.h"
+
+static ide_hwgroup_t *irq_to_hwgroup [NR_IRQS];
+static const byte ide_hwif_to_major[MAX_HWIFS] = {IDE0_MAJOR, IDE1_MAJOR, IDE2_MAJOR, IDE3_MAJOR};
+
+static const unsigned short default_io_base[MAX_HWIFS] = {0x1f0, 0x170, 0x1e8, 0x168};
+static const byte default_irqs[MAX_HWIFS] = {14, 15, 11, 10};
+
+#if (DISK_RECOVERY_TIME > 0)
+/*
+ * For really screwy hardware (hey, at least it *can* be used with Linux)
+ * we can enforce a minimum delay time between successive operations.
+ */
+static unsigned long read_timer(void)
+{
+ unsigned long t, flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+ t = jiffies * 11932;
+ outb_p(0, 0x43);
+ i = inb_p(0x40);
+ i |= inb(0x40) << 8;
+ restore_flags(flags);
+ return (t - i);
+}
+
+static void set_recovery_timer (ide_hwif_t *hwif)
+{
+ hwif->last_time = read_timer();
+}
+#define SET_RECOVERY_TIMER(drive) set_recovery_timer (drive)
+
+#else
+
+#define SET_RECOVERY_TIMER(drive)
+
+#endif /* DISK_RECOVERY_TIME */
+
+/*
+ * init_ide_data() sets reasonable default values into all fields
+ * of all instances of the hwifs and drives, but only on the first call.
+ * Subsequent calls have no effect (they don't wipe out anything).
+ *
+ * This routine is normally called at driver initialization time,
+ * but may also be called MUCH earlier during kernel "command-line"
+ * parameter processing. As such, we cannot depend on any other parts
+ * of the kernel (such as memory allocation) to be functioning yet.
+ *
+ * This is too bad, as otherwise we could dynamically allocate the
+ * ide_drive_t structs as needed, rather than always consuming memory
+ * for the max possible number (MAX_HWIFS * MAX_DRIVES) of them.
+ */
+#define MAGIC_COOKIE 0x12345678
+static void init_ide_data (void)
+{
+ byte *p;
+ unsigned int h, unit;
+ static unsigned long magic_cookie = MAGIC_COOKIE;
+
+ if (magic_cookie != MAGIC_COOKIE)
+ return; /* already initialized */
+ magic_cookie = 0;
+
+ for (h = 0; h < NR_IRQS; ++h)
+ irq_to_hwgroup[h] = NULL;
+
+ /* bulk initialize hwif & drive info with zeros */
+ p = ((byte *) ide_hwifs) + sizeof(ide_hwifs);
+ do {
+ *--p = 0;
+ } while (p > (byte *) ide_hwifs);
+
+ /* fill in any non-zero initial values */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+
+ hwif->index = h;
+ hwif->noprobe = (h > 1);
+ hwif->io_base = default_io_base[h];
+ hwif->ctl_port = hwif->io_base ? hwif->io_base+0x206 : 0x000;
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->io_base == HD_DATA)
+ hwif->noprobe = 1; /* may be overriden by ide_setup() */
+#endif /* CONFIG_BLK_DEV_HD */
+ hwif->major = ide_hwif_to_major[h];
+ hwif->name[0] = 'i';
+ hwif->name[1] = 'd';
+ hwif->name[2] = 'e';
+ hwif->name[3] = '0' + h;
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ hwif->tape_drive = NULL;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+
+ drive->select.all = (unit<<4)|0xa0;
+ drive->hwif = hwif;
+ drive->ctl = 0x08;
+ drive->ready_stat = READY_STAT;
+ drive->bad_wstat = BAD_W_STAT;
+ drive->special.b.recalibrate = 1;
+ drive->special.b.set_geometry = 1;
+ drive->name[0] = 'h';
+ drive->name[1] = 'd';
+ drive->name[2] = 'a' + (h * MAX_DRIVES) + unit;
+ }
+ }
+}
+
+#if SUPPORT_VLB_SYNC
+/*
+ * Some localbus EIDE interfaces require a special access sequence
+ * when using 32-bit I/O instructions to transfer data. We call this
+ * the "vlb_sync" sequence, which consists of three successive reads
+ * of the sector count register location, with interrupts disabled
+ * to ensure that the reads all happen together.
+ */
+static inline void do_vlb_sync (unsigned short port) {
+ (void) inb (port);
+ (void) inb (port);
+ (void) inb (port);
+}
+#endif /* SUPPORT_VLB_SYNC */
+
+/*
+ * This is used for most PIO data transfers *from* the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ unsigned short io_base = HWIF(drive)->io_base;
+ unsigned short data_reg = io_base+IDE_DATA_OFFSET;
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (io_32bit & 2) {
+ cli();
+ do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
+ insl(data_reg, buffer, wcount);
+ if (drive->unmask)
+ sti();
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ insl(data_reg, buffer, wcount);
+ } else
+ insw(data_reg, buffer, wcount<<1);
+}
+
+/*
+ * This is used for most PIO data transfers *to* the IDE interface
+ */
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount)
+{
+ unsigned short io_base = HWIF(drive)->io_base;
+ unsigned short data_reg = io_base+IDE_DATA_OFFSET;
+ byte io_32bit = drive->io_32bit;
+
+ if (io_32bit) {
+#if SUPPORT_VLB_SYNC
+ if (io_32bit & 2) {
+ cli();
+ do_vlb_sync(io_base+IDE_NSECTOR_OFFSET);
+ outsl(data_reg, buffer, wcount);
+ if (drive->unmask)
+ sti();
+ } else
+#endif /* SUPPORT_VLB_SYNC */
+ outsl(data_reg, buffer, wcount);
+ } else
+ outsw(data_reg, buffer, wcount<<1);
+}
+
+/*
+ * This should get invoked any time we exit the driver to
+ * wait for an interrupt response from a drive. handler() points
+ * at the appropriate code to handle the next interrupt, and a
+ * timer is started to prevent us from waiting forever in case
+ * something goes wrong (see the timer_expiry() handler later on).
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+#ifdef DEBUG
+ if (hwgroup->handler != NULL) {
+ printk("%s: ide_set_handler: handler not null; old=%p, new=%p\n",
+ drive->name, hwgroup->handler, handler);
+ }
+#endif
+ hwgroup->handler = handler;
+ hwgroup->timer.expires = jiffies + timeout;
+ add_timer(&(hwgroup->timer));
+}
+
+/*
+ * lba_capacity_is_ok() performs a sanity check on the claimed "lba_capacity"
+ * value for this drive (from its reported identification information).
+ *
+ * Returns: 1 if lba_capacity looks sensible
+ * 0 otherwise
+ */
+static int lba_capacity_is_ok (struct hd_driveid *id)
+{
+ unsigned long lba_sects = id->lba_capacity;
+ unsigned long chs_sects = id->cyls * id->heads * id->sectors;
+ unsigned long _10_percent = chs_sects / 10;
+
+ /* perform a rough sanity check on lba_sects: within 10% is "okay" */
+ if ((lba_sects - chs_sects) < _10_percent)
+ return 1; /* lba_capacity is good */
+
+ /* some drives have the word order reversed */
+ lba_sects = (lba_sects << 16) | (lba_sects >> 16);
+ if ((lba_sects - chs_sects) < _10_percent) {
+ id->lba_capacity = lba_sects; /* fix it */
+ return 1; /* lba_capacity is (now) good */
+ }
+ return 0; /* lba_capacity value is bad */
+}
+
+/*
+ * current_capacity() returns the capacity (in sectors) of a drive
+ * according to its current geometry/LBA settings.
+ */
+static unsigned long current_capacity (ide_drive_t *drive)
+{
+ struct hd_driveid *id = drive->id;
+ unsigned long capacity;
+
+ if (!drive->present)
+ return 0;
+ if (drive->media != ide_disk)
+ return 0x7fffffff; /* cdrom or tape */
+ /* Determine capacity, and use LBA if the drive properly supports it */
+ if (id != NULL && (id->capability & 2) && lba_capacity_is_ok(id)) {
+ drive->select.b.lba = 1;
+ capacity = id->lba_capacity;
+ } else {
+ drive->select.b.lba = 0;
+ capacity = drive->cyl * drive->head * drive->sect;
+ }
+ return (capacity - drive->sect0);
+}
+
+/*
+ * ide_geninit() is called exactly *once* for each major, from genhd.c,
+ * at the beginning of the initial partition check for the drives.
+ */
+static void ide_geninit (struct gendisk *gd)
+{
+ unsigned int unit;
+ ide_hwif_t *hwif = gd->real_devices;
+
+ for (unit = 0; unit < gd->nr_real; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->present && drive->media == ide_cdrom)
+ ide_cdrom_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->present && drive->media == ide_tape)
+ idetape_setup(drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ drive->part[0].nr_sects = current_capacity(drive);
+ if (!drive->present || drive->media != ide_disk) {
+ drive->part[0].start_sect = -1; /* skip partition check */
+ }
+ }
+ /*
+ * The partition check in genhd.c needs this string to identify
+ * our minor devices by name for display purposes.
+ * Note that doing this will prevent us from working correctly
+ * if ever called a second time for this major (never happens).
+ */
+ gd->real_devices = hwif->drives[0].name; /* name of first drive */
+}
+
+/*
+ * init_gendisk() (as opposed to ide_geninit) is called for each major device,
+ * after probing for drives, to allocate partition tables and other data
+ * structures needed for the routines in genhd.c. ide_geninit() gets called
+ * somewhat later, during the partition check.
+ */
+static void init_gendisk (ide_hwif_t *hwif)
+{
+ struct gendisk *gd;
+ unsigned int unit, units, minors;
+ int *bs;
+
+ /* figure out maximum drive number on the interface */
+ for (units = MAX_DRIVES; units > 0; --units) {
+ if (hwif->drives[units-1].present)
+ break;
+ }
+ minors = units * (1<<PARTN_BITS);
+ gd = kmalloc (sizeof(struct gendisk), GFP_KERNEL);
+ gd->sizes = kmalloc (minors * sizeof(int), GFP_KERNEL);
+ gd->part = kmalloc (minors * sizeof(struct hd_struct), GFP_KERNEL);
+ bs = kmalloc (minors*sizeof(int), GFP_KERNEL);
+
+ /* cdroms and msdos f/s are examples of non-1024 blocksizes */
+ blksize_size[hwif->major] = bs;
+ for (unit = 0; unit < minors; ++unit)
+ *bs++ = BLOCK_SIZE;
+
+ for (unit = 0; unit < units; ++unit)
+ hwif->drives[unit].part = &gd->part[unit << PARTN_BITS];
+
+ gd->major = hwif->major; /* our major device number */
+ gd->major_name = IDE_MAJOR_NAME; /* treated special in genhd.c */
+ gd->minor_shift = PARTN_BITS; /* num bits for partitions */
+ gd->max_p = 1<<PARTN_BITS; /* 1 + max partitions / drive */
+ gd->max_nr = units; /* max num real drives */
+ gd->nr_real = units; /* current num real drives */
+ gd->init = ide_geninit; /* initialization function */
+ gd->real_devices= hwif; /* ptr to internal data */
+
+ gd->next = gendisk_head; /* link new major into list */
+ hwif->gd = gendisk_head = gd;
+}
+
+static void do_reset1 (ide_drive_t *, int); /* needed below */
+
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+/*
+ * atapi_reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an atapi drive reset operation. If the drive has not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static void atapi_reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ byte stat;
+
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ udelay (10);
+
+ if (OK_STAT(stat=GET_STAT(), 0, BUSY_STAT)) {
+ printk("%s: ATAPI reset complete\n", drive->name);
+ } else {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+ return; /* continue polling */
+ }
+ hwgroup->poll_timeout = 0; /* end of polling */
+ printk("%s: ATAPI reset timed-out, status=0x%02x\n", drive->name, stat);
+ do_reset1 (drive, 1); /* do it the old fashioned way */
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+}
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+
+/*
+ * reset_pollfunc() gets invoked to poll the interface for completion every 50ms
+ * during an ide reset operation. If the drives have not yet responded,
+ * and we have not yet hit our maximum waiting time, then the timer is restarted
+ * for another 50ms.
+ */
+static void reset_pollfunc (ide_drive_t *drive)
+{
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ ide_hwif_t *hwif = HWIF(drive);
+ byte tmp;
+
+ if (!OK_STAT(tmp=GET_STAT(), 0, BUSY_STAT)) {
+ if (jiffies < hwgroup->poll_timeout) {
+ ide_set_handler (drive, &reset_pollfunc, HZ/20);
+ return; /* continue polling */
+ }
+ printk("%s: reset timed-out, status=0x%02x\n", hwif->name, tmp);
+ } else {
+ printk("%s: reset: ", hwif->name);
+ if ((tmp = GET_ERR()) == 1)
+ printk("success\n");
+ else {
+ printk("master: ");
+ switch (tmp & 0x7f) {
+ case 1: printk("passed");
+ break;
+ case 2: printk("formatter device error");
+ break;
+ case 3: printk("sector buffer error");
+ break;
+ case 4: printk("ECC circuitry error");
+ break;
+ case 5: printk("controlling MPU error");
+ break;
+ default:printk("error (0x%02x?)", tmp);
+ }
+ if (tmp & 0x80)
+ printk("; slave: failed");
+ printk("\n");
+ }
+ }
+ hwgroup->poll_timeout = 0; /* done polling */
+}
+
+/*
+ * do_reset1() attempts to recover a confused drive by resetting it.
+ * Unfortunately, resetting a disk drive actually resets all devices on
+ * the same interface, so it can really be thought of as resetting the
+ * interface rather than resetting the drive.
+ *
+ * ATAPI devices have their own reset mechanism which allows them to be
+ * individually reset without clobbering other devices on the same interface.
+ *
+ * Unfortunately, the IDE interface does not generate an interrupt to let
+ * us know when the reset operation has finished, so we must poll for this.
+ * Equally poor, though, is the fact that this may a very long time to complete,
+ * (up to 30 seconds worstcase). So, instead of busy-waiting here for it,
+ * we set a timer to poll at 50ms intervals.
+ */
+static void do_reset1 (ide_drive_t *drive, int do_not_try_atapi)
+{
+ unsigned int unit;
+ unsigned long flags;
+ ide_hwif_t *hwif = HWIF(drive);
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+
+ save_flags(flags);
+ cli(); /* Why ? */
+
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ /* For an ATAPI device, first try an ATAPI SRST. */
+ if (drive->media != ide_disk) {
+ if (!do_not_try_atapi) {
+ if (!drive->keep_settings)
+ drive->unmask = 0;
+ OUT_BYTE (drive->select.all, IDE_SELECT_REG);
+ udelay (20);
+ OUT_BYTE (WIN_SRST, IDE_COMMAND_REG);
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &atapi_reset_pollfunc, HZ/20);
+ restore_flags (flags);
+ return;
+ }
+ }
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+
+ /*
+ * First, reset any device state data we were maintaining
+ * for any of the drives on this interface.
+ */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *rdrive = &hwif->drives[unit];
+ rdrive->special.all = 0;
+ rdrive->special.b.set_geometry = 1;
+ rdrive->special.b.recalibrate = 1;
+ if (OK_TO_RESET_CONTROLLER)
+ rdrive->mult_count = 0;
+ if (!rdrive->keep_settings) {
+ rdrive->using_dma = 0;
+ rdrive->mult_req = 0;
+ rdrive->unmask = 0;
+ }
+ if (rdrive->mult_req != rdrive->mult_count)
+ rdrive->special.b.set_multmode = 1;
+ }
+
+#if OK_TO_RESET_CONTROLLER
+ /*
+ * Note that we also set nIEN while resetting the device,
+ * to mask unwanted interrupts from the interface during the reset.
+ * However, due to the design of PC hardware, this will cause an
+ * immediate interrupt due to the edge transition it produces.
+ * This single interrupt gives us a "fast poll" for drives that
+ * recover from reset very quickly, saving us the first 50ms wait time.
+ */
+ OUT_BYTE(drive->ctl|6,IDE_CONTROL_REG); /* set SRST and nIEN */
+ udelay(5); /* more than enough time */
+ OUT_BYTE(drive->ctl|2,IDE_CONTROL_REG); /* clear SRST, leave nIEN */
+ hwgroup->poll_timeout = jiffies + WAIT_WORSTCASE;
+ ide_set_handler (drive, &reset_pollfunc, HZ/20);
+#endif /* OK_TO_RESET_CONTROLLER */
+
+ restore_flags (flags);
+}
+
+/*
+ * ide_do_reset() is the entry point to the drive/interface reset code.
+ */
+void ide_do_reset (ide_drive_t *drive)
+{
+ do_reset1 (drive, 0);
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ drive->tape.reset_issued=1;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+}
+
+/*
+ * Clean up after success/failure of an explicit drive cmd
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err)
+{
+ unsigned long flags;
+ struct request *rq = HWGROUP(drive)->rq;
+
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = (byte *) rq->buffer;
+ rq->errors = !OK_STAT(stat,READY_STAT,BAD_STAT);
+ if (args) {
+ args[0] = stat;
+ args[1] = err;
+ args[2] = IN_BYTE(IDE_NSECTOR_REG);
+ }
+ }
+ save_flags(flags);
+ cli();
+ blk_dev[MAJOR(rq->rq_dev)].current_request = rq->next;
+ HWGROUP(drive)->rq = NULL;
+ rq->rq_status = RQ_INACTIVE;
+ if (rq->sem != NULL)
+ up(rq->sem);
+ restore_flags(flags);
+}
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat)
+{
+ unsigned long flags;
+ byte err = 0;
+
+ save_flags (flags);
+ sti();
+ printk("%s: %s: status=0x%02x", drive->name, msg, stat);
+#if FANCY_STATUS_DUMPS
+ if (drive->media == ide_disk) {
+ printk(" { ");
+ if (stat & BUSY_STAT)
+ printk("Busy ");
+ else {
+ if (stat & READY_STAT) printk("DriveReady ");
+ if (stat & WRERR_STAT) printk("DeviceFault ");
+ if (stat & SEEK_STAT) printk("SeekComplete ");
+ if (stat & DRQ_STAT) printk("DataRequest ");
+ if (stat & ECC_STAT) printk("CorrectedError ");
+ if (stat & INDEX_STAT) printk("Index ");
+ if (stat & ERR_STAT) printk("Error ");
+ }
+ printk("}");
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ if ((stat & (BUSY_STAT|ERR_STAT)) == ERR_STAT) {
+ err = GET_ERR();
+ printk("%s: %s: error=0x%02x", drive->name, msg, err);
+#if FANCY_STATUS_DUMPS
+ if (drive->media == ide_disk) {
+ printk(" { ");
+ if (err & BBD_ERR) printk("BadSector ");
+ if (err & ECC_ERR) printk("UncorrectableError ");
+ if (err & ID_ERR) printk("SectorIdNotFound ");
+ if (err & ABRT_ERR) printk("DriveStatusError ");
+ if (err & TRK0_ERR) printk("TrackZeroNotFound ");
+ if (err & MARK_ERR) printk("AddrMarkNotFound ");
+ printk("}");
+ if (err & (BBD_ERR|ECC_ERR|ID_ERR|MARK_ERR)) {
+ byte cur = IN_BYTE(IDE_SELECT_REG);
+ if (cur & 0x40) { /* using LBA? */
+ printk(", LBAsect=%ld", (unsigned long)
+ ((cur&0xf)<<24)
+ |(IN_BYTE(IDE_HCYL_REG)<<16)
+ |(IN_BYTE(IDE_LCYL_REG)<<8)
+ | IN_BYTE(IDE_SECTOR_REG));
+ } else {
+ printk(", CHS=%d/%d/%d",
+ (IN_BYTE(IDE_HCYL_REG)<<8) +
+ IN_BYTE(IDE_LCYL_REG),
+ cur & 0xf,
+ IN_BYTE(IDE_SECTOR_REG));
+ }
+ if (HWGROUP(drive)->rq)
+ printk(", sector=%ld", HWGROUP(drive)->rq->sector);
+ }
+ }
+#endif /* FANCY_STATUS_DUMPS */
+ printk("\n");
+ }
+ restore_flags (flags);
+ return err;
+}
+
+/*
+ * try_to_flush_leftover_data() is invoked in response to a drive
+ * unexpectedly having its DRQ_STAT bit set. As an alternative to
+ * resetting the drive, this routine tries to clear the condition
+ * by read a sector's worth of data from the drive. Of course,
+ * this may not help if the drive is *waiting* for data from *us*.
+ */
+static void try_to_flush_leftover_data (ide_drive_t *drive)
+{
+ int i = (drive->mult_count ? drive->mult_count : 1) * SECTOR_WORDS;
+
+ while (i > 0) {
+ unsigned long buffer[16];
+ unsigned int wcount = (i > 16) ? 16 : i;
+ i -= wcount;
+ ide_input_data (drive, buffer, wcount);
+ }
+}
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ */
+void ide_error (ide_drive_t *drive, const char *msg, byte stat)
+{
+ struct request *rq;
+ byte err;
+
+ err = ide_dump_status(drive, msg, stat);
+ if ((rq = HWGROUP(drive)->rq) == NULL || drive == NULL)
+ return;
+ /* retry only "normal" I/O: */
+ if (rq->cmd == IDE_DRIVE_CMD || (rq->cmd != READ && rq->cmd != WRITE && drive->media == ide_disk))
+ {
+ rq->errors = 1;
+ ide_end_drive_cmd(drive, stat, err);
+ return;
+ }
+ if (stat & BUSY_STAT) { /* other bits are useless when BUSY */
+ rq->errors |= ERROR_RESET;
+ } else {
+ if (drive->media == ide_disk && (stat & ERR_STAT)) {
+ /* err has different meaning on cdrom and tape */
+ if (err & (BBD_ERR | ECC_ERR)) /* retries won't help these */
+ rq->errors = ERROR_MAX;
+ else if (err & TRK0_ERR) /* help it find track zero */
+ rq->errors |= ERROR_RECAL;
+ }
+ if ((stat & DRQ_STAT) && rq->cmd != WRITE)
+ try_to_flush_leftover_data(drive);
+ }
+ if (GET_STAT() & (BUSY_STAT|DRQ_STAT))
+ rq->errors |= ERROR_RESET; /* Mmmm.. timing problem */
+
+ if (rq->errors >= ERROR_MAX) {
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape) {
+ rq->errors = 0;
+ idetape_end_request(0, HWGROUP(drive));
+ }
+ else
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ ide_end_request(0, HWGROUP(drive));
+ }
+ else {
+ if ((rq->errors & ERROR_RESET) == ERROR_RESET) {
+ ++rq->errors;
+ ide_do_reset(drive);
+ return;
+ } else if ((rq->errors & ERROR_RECAL) == ERROR_RECAL)
+ drive->special.b.recalibrate = 1;
+ ++rq->errors;
+ }
+}
+
+/*
+ * read_intr() is the handler for disk read/multread interrupts
+ */
+static void read_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ unsigned int msect, nsect;
+ struct request *rq;
+
+ if (!OK_STAT(stat=GET_STAT(),DATA_READY,BAD_R_STAT)) {
+ ide_error(drive, "read_intr", stat);
+ return;
+ }
+ msect = drive->mult_count;
+read_next:
+ rq = HWGROUP(drive)->rq;
+ if (msect) {
+ if ((nsect = rq->current_nr_sectors) > msect)
+ nsect = msect;
+ msect -= nsect;
+ } else
+ nsect = 1;
+ ide_input_data(drive, rq->buffer, nsect * SECTOR_WORDS);
+#ifdef DEBUG
+ printk("%s: read: sectors(%ld-%ld), buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, rq->sector+nsect-1,
+ (unsigned long) rq->buffer+(nsect<<9), rq->nr_sectors-nsect);
+#endif
+ rq->sector += nsect;
+ rq->buffer += nsect<<9;
+ rq->errors = 0;
+ i = (rq->nr_sectors -= nsect);
+ if ((rq->current_nr_sectors -= nsect) <= 0)
+ ide_end_request(1, HWGROUP(drive));
+ if (i > 0) {
+ if (msect)
+ goto read_next;
+ ide_set_handler (drive, &read_intr, WAIT_CMD);
+ }
+}
+
+/*
+ * write_intr() is the handler for disk write interrupts
+ */
+static void write_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = hwgroup->rq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+#ifdef DEBUG
+ printk("%s: write: sector %ld, buffer=0x%08lx, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ rq->nr_sectors-1);
+#endif
+ if ((rq->nr_sectors == 1) ^ ((stat & DRQ_STAT) != 0)) {
+ rq->sector++;
+ rq->buffer += 512;
+ rq->errors = 0;
+ i = --rq->nr_sectors;
+ --rq->current_nr_sectors;
+ if (rq->current_nr_sectors <= 0)
+ ide_end_request(1, hwgroup);
+ if (i > 0) {
+ ide_output_data (drive, rq->buffer, SECTOR_WORDS);
+ ide_set_handler (drive, &write_intr, WAIT_CMD);
+ }
+ return;
+ }
+ }
+ ide_error(drive, "write_intr", stat);
+}
+
+/*
+ * multwrite() transfers a block of one or more sectors of data to a drive
+ * as part of a disk multwrite operation.
+ */
+static void multwrite (ide_drive_t *drive)
+{
+ struct request *rq = &HWGROUP(drive)->wrq;
+ unsigned int mcount = drive->mult_count;
+
+ do {
+ unsigned int nsect = rq->current_nr_sectors;
+ if (nsect > mcount)
+ nsect = mcount;
+ mcount -= nsect;
+
+ ide_output_data(drive, rq->buffer, nsect<<7);
+#ifdef DEBUG
+ printk("%s: multwrite: sector %ld, buffer=0x%08lx, count=%d, remaining=%ld\n",
+ drive->name, rq->sector, (unsigned long) rq->buffer,
+ nsect, rq->nr_sectors - nsect);
+#endif
+ if ((rq->nr_sectors -= nsect) <= 0)
+ break;
+ if ((rq->current_nr_sectors -= nsect) == 0) {
+ if ((rq->bh = rq->bh->b_reqnext) != NULL) {
+ rq->current_nr_sectors = rq->bh->b_size>>9;
+ rq->buffer = rq->bh->b_data;
+ } else {
+ panic("%s: buffer list corrupted\n", drive->name);
+ break;
+ }
+ } else {
+ rq->buffer += nsect << 9;
+ }
+ } while (mcount);
+}
+
+/*
+ * multwrite_intr() is the handler for disk multwrite interrupts
+ */
+static void multwrite_intr (ide_drive_t *drive)
+{
+ byte stat;
+ int i;
+ ide_hwgroup_t *hwgroup = HWGROUP(drive);
+ struct request *rq = &hwgroup->wrq;
+
+ if (OK_STAT(stat=GET_STAT(),DRIVE_READY,drive->bad_wstat)) {
+ if (stat & DRQ_STAT) {
+ if (rq->nr_sectors) {
+ multwrite(drive);
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
+ return;
+ }
+ } else {
+ if (!rq->nr_sectors) { /* all done? */
+ rq = hwgroup->rq;
+ for (i = rq->nr_sectors; i > 0;){
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, hwgroup);
+ }
+ return;
+ }
+ }
+ }
+ ide_error(drive, "multwrite_intr", stat);
+}
+
+/*
+ * Issue a simple drive command
+ * The drive must be selected beforehand.
+ */
+static void ide_cmd(ide_drive_t *drive, byte cmd, byte nsect, ide_handler_t *handler)
+{
+ ide_set_handler (drive, handler, WAIT_CMD);
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(nsect,IDE_NSECTOR_REG);
+ OUT_BYTE(cmd,IDE_COMMAND_REG);
+}
+
+/*
+ * set_multmode_intr() is invoked on completion of a WIN_SETMULT cmd.
+ */
+static void set_multmode_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (OK_STAT(stat,READY_STAT,BAD_STAT)) {
+ drive->mult_count = drive->mult_req;
+ } else {
+ drive->mult_req = drive->mult_count = 0;
+ drive->special.b.recalibrate = 1;
+ (void) ide_dump_status(drive, "set_multmode", stat);
+ }
+}
+
+/*
+ * set_geometry_intr() is invoked on completion of a WIN_SPECIFY cmd.
+ */
+static void set_geometry_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(drive, "set_geometry_intr", stat);
+}
+
+/*
+ * recal_intr() is invoked on completion of a WIN_RESTORE (recalibrate) cmd.
+ */
+static void recal_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (!OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_error(drive, "recal_intr", stat);
+}
+
+/*
+ * drive_cmd_intr() is invoked on completion of a special DRIVE_CMD.
+ */
+static void drive_cmd_intr (ide_drive_t *drive)
+{
+ byte stat = GET_STAT();
+
+ sti();
+ if (OK_STAT(stat,READY_STAT,BAD_STAT))
+ ide_end_drive_cmd (drive, stat, GET_ERR());
+ else
+ ide_error(drive, "drive_cmd", stat); /* calls ide_end_drive_cmd */
+}
+
+/*
+ * do_special() is used to issue WIN_SPECIFY, WIN_RESTORE, and WIN_SETMULT
+ * commands to a drive. It used to do much more, but has been scaled back.
+ */
+static inline void do_special (ide_drive_t *drive)
+{
+ special_t *s = &drive->special;
+next:
+#ifdef DEBUG
+ printk("%s: do_special: 0x%02x\n", drive->name, s->all);
+#endif
+ if (s->b.set_geometry) {
+ s->b.set_geometry = 0;
+ if (drive->media == ide_disk) {
+ OUT_BYTE(drive->sect,IDE_SECTOR_REG);
+ OUT_BYTE(drive->cyl,IDE_LCYL_REG);
+ OUT_BYTE(drive->cyl>>8,IDE_HCYL_REG);
+ OUT_BYTE(((drive->head-1)|drive->select.all)&0xBF,IDE_SELECT_REG);
+ ide_cmd(drive, WIN_SPECIFY, drive->sect, &set_geometry_intr);
+ }
+ } else if (s->b.recalibrate) {
+ s->b.recalibrate = 0;
+ if (drive->media == ide_disk) {
+ ide_cmd(drive, WIN_RESTORE, drive->sect, &recal_intr);
+ }
+ } else if (s->b.set_pio) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ s->b.set_pio = 0;
+ if (tuneproc != NULL)
+ tuneproc(drive, drive->pio_req);
+ goto next;
+ } else if (s->b.set_multmode) {
+ s->b.set_multmode = 0;
+ if (drive->media == ide_disk) {
+ if (drive->id && drive->mult_req > drive->id->max_multsect)
+ drive->mult_req = drive->id->max_multsect;
+ ide_cmd(drive, WIN_SETMULT, drive->mult_req, &set_multmode_intr);
+ } else
+ drive->mult_req = 0;
+ } else if (s->all) {
+ s->all = 0;
+ printk("%s: bad special flag: 0x%02x\n", drive->name, s->all);
+ }
+}
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should just return.
+ *
+ * This routine should get fixed to not hog the cpu during extra long waits..
+ * That could be done by busy-waiting for the first jiffy or two, and then
+ * setting a timer to wake up at half second intervals thereafter,
+ * until timeout is achieved, before timing out.
+ */
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout)
+{
+ byte stat;
+ unsigned long flags;
+
+test:
+ udelay(1); /* spec allows drive 400ns to change "BUSY" */
+ if (OK_STAT((stat = GET_STAT()), good, bad))
+ return 0; /* fast exit for most frequent case */
+ if (!(stat & BUSY_STAT)) {
+ ide_error(drive, "status error", stat);
+ return 1;
+ }
+
+ save_flags(flags);
+ sti();
+ timeout += jiffies;
+ do {
+ if (!((stat = GET_STAT()) & BUSY_STAT)) {
+ restore_flags(flags);
+ goto test;
+ }
+ } while (jiffies <= timeout);
+
+ restore_flags(flags);
+ ide_error(drive, "status timeout", GET_STAT());
+ return 1;
+}
+
+/*
+ * do_rw_disk() issues WIN_{MULT}READ and WIN_{MULT}WRITE commands to a disk,
+ * using LBA if supported, or CHS otherwise, to address sectors. It also takes
+ * care of issuing special DRIVE_CMDs.
+ */
+static inline void do_rw_disk (ide_drive_t *drive, struct request *rq, unsigned long block)
+{
+ unsigned short io_base = HWIF(drive)->io_base;
+
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG);
+ OUT_BYTE(rq->nr_sectors,io_base+IDE_NSECTOR_OFFSET);
+ if (drive->select.b.lba) {
+#ifdef DEBUG
+ printk("%s: %sing: LBAsect=%ld, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ",
+ block, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ OUT_BYTE(block,io_base+IDE_SECTOR_OFFSET);
+ OUT_BYTE(block>>=8,io_base+IDE_LCYL_OFFSET);
+ OUT_BYTE(block>>=8,io_base+IDE_HCYL_OFFSET);
+ OUT_BYTE(((block>>8)&0x0f)|drive->select.all,io_base+IDE_SELECT_OFFSET);
+ } else {
+ unsigned int sect,head,cyl,track;
+ track = block / drive->sect;
+ sect = block % drive->sect + 1;
+ OUT_BYTE(sect,io_base+IDE_SECTOR_OFFSET);
+ head = track % drive->head;
+ cyl = track / drive->head;
+ OUT_BYTE(cyl,io_base+IDE_LCYL_OFFSET);
+ OUT_BYTE(cyl>>8,io_base+IDE_HCYL_OFFSET);
+ OUT_BYTE(head|drive->select.all,io_base+IDE_SELECT_OFFSET);
+#ifdef DEBUG
+ printk("%s: %sing: CHS=%d/%d/%d, sectors=%ld, buffer=0x%08lx\n",
+ drive->name, (rq->cmd==READ)?"read":"writ", cyl,
+ head, sect, rq->nr_sectors, (unsigned long) rq->buffer);
+#endif
+ }
+ if (rq->cmd == READ) {
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_read, drive)))
+ return;
+#endif /* CONFIG_BLK_DEV_TRITON */
+ ide_set_handler(drive, &read_intr, WAIT_CMD);
+ OUT_BYTE(drive->mult_count ? WIN_MULTREAD : WIN_READ, io_base+IDE_COMMAND_OFFSET);
+ return;
+ }
+ if (rq->cmd == WRITE) {
+#ifdef CONFIG_BLK_DEV_TRITON
+ if (drive->using_dma && !(HWIF(drive)->dmaproc(ide_dma_write, drive)))
+ return;
+#endif /* CONFIG_BLK_DEV_TRITON */
+ OUT_BYTE(drive->mult_count ? WIN_MULTWRITE : WIN_WRITE, io_base+IDE_COMMAND_OFFSET);
+ if (ide_wait_stat(drive, DATA_READY, drive->bad_wstat, WAIT_DRQ)) {
+ printk("%s: no DRQ after issuing %s\n", drive->name,
+ drive->mult_count ? "MULTWRITE" : "WRITE");
+ return;
+ }
+ if (!drive->unmask)
+ cli();
+ if (drive->mult_count) {
+ HWGROUP(drive)->wrq = *rq; /* scratchpad */
+ ide_set_handler (drive, &multwrite_intr, WAIT_CMD);
+ multwrite(drive);
+ } else {
+ ide_set_handler (drive, &write_intr, WAIT_CMD);
+ ide_output_data(drive, rq->buffer, SECTOR_WORDS);
+ }
+ return;
+ }
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = rq->buffer;
+ if (args) {
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD cmd=0x%02x sc=0x%02x fr=0x%02x\n",
+ drive->name, args[0], args[1], args[2]);
+#endif
+ OUT_BYTE(args[2],io_base+IDE_FEATURE_OFFSET);
+ ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+ return;
+ } else {
+ /*
+ * NULL is actually a valid way of waiting for
+ * all current requests to be flushed from the queue.
+ */
+#ifdef DEBUG
+ printk("%s: DRIVE_CMD (null)\n", drive->name);
+#endif
+ ide_end_drive_cmd(drive, GET_STAT(), GET_ERR());
+ return;
+ }
+ }
+ printk("%s: bad command: %d\n", drive->name, rq->cmd);
+ ide_end_request(0, HWGROUP(drive));
+}
+
+/*
+ * do_request() initiates handling of a new I/O request
+ */
+static inline void do_request (ide_hwif_t *hwif, struct request *rq)
+{
+ unsigned int minor, unit;
+ unsigned long block, blockend;
+ ide_drive_t *drive;
+
+ sti();
+#ifdef DEBUG
+ printk("%s: do_request: current=0x%08lx\n", hwif->name, (unsigned long) rq);
+#endif
+ minor = MINOR(rq->rq_dev);
+ unit = minor >> PARTN_BITS;
+ if (MAJOR(rq->rq_dev) != hwif->major || unit >= MAX_DRIVES) {
+ printk("%s: bad device number: %s\n",
+ hwif->name, kdevname(rq->rq_dev));
+ goto kill_rq;
+ }
+ drive = &hwif->drives[unit];
+#ifdef DEBUG
+ if (rq->bh && !buffer_locked(rq->bh)) {
+ printk("%s: block not locked\n", drive->name);
+ goto kill_rq;
+ }
+#endif
+ block = rq->sector;
+ blockend = block + rq->nr_sectors;
+ if ((blockend < block) || (blockend > drive->part[minor&PARTN_MASK].nr_sects)) {
+ printk("%s%c: bad access: block=%ld, count=%ld\n", drive->name,
+ (minor&PARTN_MASK)?'0'+(minor&PARTN_MASK):' ', block, rq->nr_sectors);
+ goto kill_rq;
+ }
+ block += drive->part[minor&PARTN_MASK].start_sect + drive->sect0;
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (block == 0 && drive->remap_0_to_1)
+ block = 1; /* redirect MBR access to EZ-Drive partn table */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ ((ide_hwgroup_t *)hwif->hwgroup)->drive = drive;
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (hwif->selectproc)
+ hwif->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if (DISK_RECOVERY_TIME > 0)
+ while ((read_timer() - hwif->last_time) < DISK_RECOVERY_TIME);
+#endif
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ POLL_HWIF_TAPE_DRIVE; /* macro from ide-tape.h */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG);
+ if (ide_wait_stat(drive, drive->ready_stat, BUSY_STAT|DRQ_STAT, WAIT_READY)) {
+ printk("%s: drive not ready for command\n", drive->name);
+ return;
+ }
+
+ if (!drive->special.all) {
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ switch (drive->media) {
+ case ide_disk:
+ do_rw_disk (drive, rq, block);
+ return;
+#ifdef CONFIG_BLK_DEV_IDECD
+ case ide_cdrom:
+ ide_do_rw_cdrom (drive, block);
+ return;
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ case ide_tape:
+ if (rq->cmd == IDE_DRIVE_CMD) {
+ byte *args = (byte *) rq->buffer;
+ OUT_BYTE(args[2],IDE_FEATURE_REG);
+ ide_cmd(drive, args[0], args[1], &drive_cmd_intr);
+ return;
+ }
+ idetape_do_request (drive, rq, block);
+ return;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ default:
+ printk("%s: media type %d not supported\n",
+ drive->name, drive->media);
+ goto kill_rq;
+ }
+#else
+ do_rw_disk (drive, rq, block); /* simpler and faster */
+ return;
+#endif /* CONFIG_BLK_DEV_IDEATAPI */;
+ }
+ do_special(drive);
+ return;
+kill_rq:
+ ide_end_request(0, hwif->hwgroup);
+}
+
+/*
+ * The driver enables interrupts as much as possible. In order to do this,
+ * (a) the device-interrupt is always masked before entry, and
+ * (b) the timeout-interrupt is always disabled before entry.
+ *
+ * If we enter here from, say irq14, and then start a new request for irq15,
+ * (possible with "serialize" option) then we cannot ensure that we exit
+ * before the irq15 hits us. So, we must be careful not to let this bother us.
+ *
+ * Interrupts are still masked (by default) whenever we are exchanging
+ * data/cmds with a drive, because some drives seem to have very poor
+ * tolerance for latency during I/O. For devices which don't suffer from
+ * this problem (most don't), the unmask flag can be set using the "hdparm"
+ * utility, to permit other interrupts during data/cmd transfers.
+ */
+void ide_do_request (ide_hwgroup_t *hwgroup)
+{
+ cli(); /* paranoia */
+ if (hwgroup->handler != NULL) {
+ printk("%s: EEeekk!! handler not NULL in ide_do_request()\n", hwgroup->hwif->name);
+ return;
+ }
+ do {
+ ide_hwif_t *hwif = hwgroup->hwif;
+ struct request *rq;
+ if ((rq = hwgroup->rq) == NULL) {
+ do {
+ rq = blk_dev[hwif->major].current_request;
+ if (rq != NULL && rq->rq_status != RQ_INACTIVE)
+ goto got_rq;
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+ return; /* no work left for this hwgroup */
+ }
+ got_rq:
+ do_request(hwgroup->hwif = hwif, hwgroup->rq = rq);
+ cli();
+ } while (hwgroup->handler == NULL);
+}
+
+/*
+ * do_hwgroup_request() invokes ide_do_request() after first masking
+ * all possible interrupts for the current hwgroup. This prevents race
+ * conditions in the event that an unexpected interrupt occurs while
+ * we are in the driver.
+ *
+ * Note that when an interrupt is used to reenter the driver, the first level
+ * handler will already have masked the irq that triggered, but any other ones
+ * for the hwgroup will still be unmasked. The driver tries to be careful
+ * about such things.
+ */
+static void do_hwgroup_request (ide_hwgroup_t *hwgroup)
+{
+ if (hwgroup->handler == NULL) {
+ ide_hwif_t *hgif = hwgroup->hwif;
+ ide_hwif_t *hwif = hgif;
+ do {
+ disable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hgif);
+ ide_do_request (hwgroup);
+ do {
+ enable_irq(hwif->irq);
+ } while ((hwif = hwif->next) != hgif);
+ }
+}
+
+static void do_ide0_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[0].hwgroup);
+}
+
+static void do_ide1_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[1].hwgroup);
+}
+
+static void do_ide2_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[2].hwgroup);
+}
+
+static void do_ide3_request (void) /* invoked with cli() */
+{
+ do_hwgroup_request (ide_hwifs[3].hwgroup);
+}
+
+static void timer_expiry (unsigned long data)
+{
+ ide_hwgroup_t *hwgroup = (ide_hwgroup_t *) data;
+ ide_drive_t *drive = hwgroup->drive;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ if (hwgroup->poll_timeout != 0) { /* polling in progress? */
+ ide_handler_t *handler = hwgroup->handler;
+ hwgroup->handler = NULL;
+ handler(drive);
+ } else if (hwgroup->handler == NULL) { /* not waiting for anything? */
+ sti(); /* drive must have responded just as the timer expired */
+ printk("%s: marginal timeout\n", drive->name);
+ } else {
+ hwgroup->handler = NULL; /* abort the operation */
+ if (hwgroup->hwif->dmaproc)
+ (void) hwgroup->hwif->dmaproc (ide_dma_abort, drive);
+ ide_error(drive, "irq timeout", GET_STAT());
+ }
+ if (hwgroup->handler == NULL)
+ do_hwgroup_request (hwgroup);
+ restore_flags(flags);
+}
+
+/*
+ * There's nothing really useful we can do with an unexpected interrupt,
+ * other than reading the status register (to clear it), and logging it.
+ * There should be no way that an irq can happen before we're ready for it,
+ * so we needn't worry much about losing an "important" interrupt here.
+ *
+ * On laptops (and "green" PCs), an unexpected interrupt occurs whenever the
+ * drive enters "idle", "standby", or "sleep" mode, so if the status looks
+ * "good", we just ignore the interrupt completely.
+ *
+ * This routine assumes cli() is in effect when called.
+ *
+ * If an unexpected interrupt happens on irq15 while we are handling irq14
+ * and if the two interfaces are "serialized" (CMD640B), then it looks like
+ * we could screw up by interfering with a new request being set up for irq15.
+ *
+ * In reality, this is a non-issue. The new command is not sent unless the
+ * drive is ready to accept one, in which case we know the drive is not
+ * trying to interrupt us. And ide_set_handler() is always invoked before
+ * completing the issuance of any new drive command, so we will not be
+ * accidently invoked as a result of any valid command completion interrupt.
+ *
+ */
+static void unexpected_intr (int irq, ide_hwgroup_t *hwgroup)
+{
+ byte stat;
+ unsigned int unit;
+ ide_hwif_t *hwif = hwgroup->hwif;
+
+ /*
+ * handle the unexpected interrupt
+ */
+ do {
+ if (hwif->irq == irq) {
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (!drive->present)
+ continue;
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (hwif->selectproc)
+ hwif->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+ if (!OK_STAT(stat=GET_STAT(), drive->ready_stat, BAD_STAT))
+ (void) ide_dump_status(drive, "unexpected_intr", stat);
+ if ((stat & DRQ_STAT))
+ try_to_flush_leftover_data(drive);
+ }
+ }
+ } while ((hwif = hwif->next) != hwgroup->hwif);
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (hwif->selectproc)
+ hwif->selectproc (hwgroup->drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+}
+
+/*
+ * entry point for all interrupts, caller does cli() for us
+ */
+static void ide_intr (int irq, struct pt_regs *regs)
+{
+ ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq];
+ ide_handler_t *handler;
+
+ if (irq == hwgroup->hwif->irq && (handler = hwgroup->handler) != NULL) {
+ ide_drive_t *drive = hwgroup->drive;
+ hwgroup->handler = NULL;
+ del_timer(&(hwgroup->timer));
+ if (drive->unmask)
+ sti();
+ handler(drive);
+ cli(); /* this is necessary, as next rq may be different irq */
+ if (hwgroup->handler == NULL) {
+ SET_RECOVERY_TIMER(HWIF(drive));
+ ide_do_request(hwgroup);
+ }
+ } else {
+ unexpected_intr(irq, hwgroup);
+ }
+ cli();
+}
+
+/*
+ * get_info_ptr() returns the (ide_drive_t *) for a given device number.
+ * It returns NULL if the given device number does not match any present drives.
+ */
+static ide_drive_t *get_info_ptr (kdev_t i_rdev)
+{
+ int major = MAJOR(i_rdev);
+ unsigned int h;
+
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (hwif->present && major == hwif->major) {
+ unsigned unit = DEVICE_NR(i_rdev);
+ if (unit < MAX_DRIVES) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present)
+ return drive;
+ } else if (major == IDE0_MAJOR && unit < 4) {
+ printk("ide: probable bad entry for /dev/hd%c\n", 'a'+unit);
+ printk("ide: to fix it, run: /usr/src/linux/drivers/block/MAKEDEV.ide\n");
+ }
+ break;
+ }
+ }
+ return NULL;
+}
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq)
+{
+ rq->buffer = NULL;
+ rq->cmd = IDE_DRIVE_CMD;
+ rq->sector = 0;
+ rq->nr_sectors = 0;
+ rq->current_nr_sectors = 0;
+ rq->sem = NULL;
+ rq->bh = NULL;
+ rq->bhtail = NULL;
+ rq->next = NULL;
+
+#if 0 /* these are done each time through ide_do_drive_cmd() */
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->rq_dev = ????;
+#endif
+}
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed. This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
+ * when operating in the pipelined operation mode).
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action)
+{
+ unsigned long flags;
+ unsigned int major = HWIF(drive)->major;
+ struct request *cur_rq;
+ struct blk_dev_struct *bdev = &blk_dev[major];
+ struct semaphore sem = MUTEX_LOCKED;
+
+ rq->errors = 0;
+ rq->rq_status = RQ_ACTIVE;
+ rq->rq_dev = MKDEV(major,(drive->select.b.unit)<<PARTN_BITS);
+ if (action == ide_wait)
+ rq->sem = &sem;
+
+ save_flags(flags);
+ cli();
+ cur_rq = bdev->current_request;
+
+ if (cur_rq == NULL || action == ide_preempt) {
+ rq->next = cur_rq;
+ bdev->current_request = rq;
+ if (action == ide_preempt) {
+ HWGROUP(drive)->rq = NULL;
+ } else
+ if (HWGROUP(drive)->rq == NULL) { /* is this necessary (?) */
+ bdev->request_fn();
+ cli();
+ }
+ } else {
+ if (action == ide_wait || action == ide_end) {
+ while (cur_rq->next != NULL) /* find end of list */
+ cur_rq = cur_rq->next;
+ }
+ rq->next = cur_rq->next;
+ cur_rq->next = rq;
+ }
+ if (action == ide_wait && rq->rq_status != RQ_INACTIVE)
+ down(&sem); /* wait for it to be serviced */
+ restore_flags(flags);
+ return rq->errors ? -EIO : 0; /* return -EIO if errors */
+}
+
+static int ide_open(struct inode * inode, struct file * filp)
+{
+ ide_drive_t *drive;
+ unsigned long flags;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+ save_flags(flags);
+ cli();
+ while (drive->busy)
+ sleep_on(&drive->wqueue);
+ drive->usage++;
+ restore_flags(flags);
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return ide_cdrom_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ return idetape_blkdev_open (inode, filp, drive);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ if (drive->removeable) {
+ byte door_lock[] = {WIN_DOORLOCK,0,0,0};
+ struct request rq;
+ check_disk_change(inode->i_rdev);
+ ide_init_drive_cmd (&rq);
+ rq.buffer = door_lock;
+ /*
+ * Ignore the return code from door_lock,
+ * since the open() has already succeeded,
+ * and the door_lock is irrelevant at this point.
+ */
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ }
+ return 0;
+}
+
+/*
+ * Releasing a block device means we sync() it, so that it can safely
+ * be forgotten about...
+ */
+static void ide_release(struct inode * inode, struct file * file)
+{
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(inode->i_rdev)) != NULL) {
+ sync_dev(inode->i_rdev);
+ drive->usage--;
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom) {
+ ide_cdrom_release (inode, file, drive);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape) {
+ idetape_blkdev_release (inode, file, drive);
+ return;
+ }
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ if (drive->removeable) {
+ byte door_unlock[] = {WIN_DOORUNLOCK,0,0,0};
+ struct request rq;
+ invalidate_buffers(inode->i_rdev);
+ ide_init_drive_cmd (&rq);
+ rq.buffer = door_unlock;
+ (void) ide_do_drive_cmd(drive, &rq, ide_wait);
+ }
+ }
+}
+
+/*
+ * This routine is called to flush all partitions and partition tables
+ * for a changed disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+static int revalidate_disk(kdev_t i_rdev)
+{
+ ide_drive_t *drive;
+ unsigned int p, major, minor;
+ long flags;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+
+ major = MAJOR(i_rdev);
+ minor = drive->select.b.unit << PARTN_BITS;
+ save_flags(flags);
+ cli();
+ if (drive->busy || (drive->usage > 1)) {
+ restore_flags(flags);
+ return -EBUSY;
+ };
+ drive->busy = 1;
+ restore_flags(flags);
+
+ for (p = 0; p < (1<<PARTN_BITS); ++p) {
+ if (drive->part[p].nr_sects > 0) {
+ kdev_t devp = MKDEV(major, minor+p);
+ sync_dev (devp);
+ invalidate_inodes (devp);
+ invalidate_buffers (devp);
+ }
+ drive->part[p].start_sect = 0;
+ drive->part[p].nr_sects = 0;
+ };
+
+ drive->part[0].nr_sects = current_capacity(drive);
+ if (drive->media == ide_disk)
+ resetup_one_dev(HWIF(drive)->gd, drive->select.b.unit);
+
+ drive->busy = 0;
+ wake_up(&drive->wqueue);
+ return 0;
+}
+
+static int write_fs_long (unsigned long useraddr, long value)
+{
+ int err;
+
+ if (NULL == (long *)useraddr)
+ return -EINVAL;
+ if ((err = verify_area(VERIFY_WRITE, (long *)useraddr, sizeof(long))))
+ return err;
+ put_user((unsigned)value, (long *) useraddr);
+ return 0;
+}
+
+static int ide_ioctl (struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+ int err;
+ ide_drive_t *drive;
+ unsigned long flags;
+ struct request rq;
+
+ ide_init_drive_cmd (&rq);
+ if (!inode || !(inode->i_rdev))
+ return -EINVAL;
+ if ((drive = get_info_ptr(inode->i_rdev)) == NULL)
+ return -ENODEV;
+ switch (cmd) {
+ case HDIO_GETGEO:
+ if (!loc || drive->media != ide_disk) return -EINVAL;
+ err = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (err) return err;
+ put_user(drive->bios_head, (byte *) &loc->heads);
+ put_user(drive->bios_sect, (byte *) &loc->sectors);
+ put_user(drive->bios_cyl, (unsigned short *) &loc->cylinders);
+ put_user((unsigned)drive->part[MINOR(inode->i_rdev)&PARTN_MASK].start_sect,
+ (unsigned long *) &loc->start);
+ return 0;
+
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+
+ case BLKRAGET:
+ return write_fs_long(arg, read_ahead[MAJOR(inode->i_rdev)]);
+
+ case BLKGETSIZE: /* Return device size */
+ return write_fs_long(arg, drive->part[MINOR(inode->i_rdev)&PARTN_MASK].nr_sects);
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_disk(inode->i_rdev);
+
+ case HDIO_GET_KEEPSETTINGS:
+ return write_fs_long(arg, drive->keep_settings);
+
+ case HDIO_GET_UNMASKINTR:
+ return write_fs_long(arg, drive->unmask);
+
+ case HDIO_GET_DMA:
+ return write_fs_long(arg, drive->using_dma);
+
+ case HDIO_GET_32BIT:
+ return write_fs_long(arg, drive->io_32bit);
+
+ case HDIO_GET_MULTCOUNT:
+ return write_fs_long(arg, drive->mult_count);
+
+ case HDIO_GET_IDENTITY:
+ if (!arg || (MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ if (drive->id == NULL)
+ return -ENOMSG;
+ err = verify_area(VERIFY_WRITE, (char *)arg, sizeof(*drive->id));
+ if (!err)
+ memcpy_tofs((char *)arg, (char *)drive->id, sizeof(*drive->id));
+ return err;
+
+ case HDIO_GET_NOWERR:
+ return write_fs_long(arg, drive->bad_wstat == BAD_R_STAT);
+
+ case HDIO_SET_DMA:
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return -EPERM;
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (!drive->id || !(drive->id->capability & 1) || !HWIF(drive)->dmaproc)
+ return -EPERM;
+ case HDIO_SET_KEEPSETTINGS:
+ case HDIO_SET_UNMASKINTR:
+ case HDIO_SET_NOWERR:
+ if (arg > 1)
+ return -EINVAL;
+ case HDIO_SET_32BIT:
+ if (!suser())
+ return -EACCES;
+ if ((MINOR(inode->i_rdev) & PARTN_MASK))
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ switch (cmd) {
+ case HDIO_SET_DMA:
+ if (!(HWIF(drive)->dmaproc)) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->using_dma = arg;
+ break;
+ case HDIO_SET_KEEPSETTINGS:
+ drive->keep_settings = arg;
+ break;
+ case HDIO_SET_UNMASKINTR:
+ if (arg && HWIF(drive)->no_unmask) {
+ restore_flags(flags);
+ return -EPERM;
+ }
+ drive->unmask = arg;
+ break;
+ case HDIO_SET_NOWERR:
+ drive->bad_wstat = arg ? BAD_R_STAT : BAD_W_STAT;
+ break;
+ case HDIO_SET_32BIT:
+ if (arg > (1 + (SUPPORT_VLB_SYNC<<1)))
+ return -EINVAL;
+ drive->io_32bit = arg;
+#ifdef CONFIG_BLK_DEV_DTC2278
+ if (HWIF(drive)->chipset == ide_dtc2278)
+ HWIF(drive)->drives[!drive->select.b.unit].io_32bit = arg;
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+ break;
+ }
+ restore_flags(flags);
+ return 0;
+
+ case HDIO_SET_MULTCOUNT:
+ if (!suser())
+ return -EACCES;
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if (drive->id && arg > drive->id->max_multsect)
+ return -EINVAL;
+ save_flags(flags);
+ cli();
+ if (drive->special.b.set_multmode) {
+ restore_flags(flags);
+ return -EBUSY;
+ }
+ drive->mult_req = arg;
+ drive->special.b.set_multmode = 1;
+ restore_flags(flags);
+ (void) ide_do_drive_cmd (drive, &rq, ide_wait);
+ return (drive->mult_count == arg) ? 0 : -EIO;
+
+ case HDIO_DRIVE_CMD:
+ {
+ unsigned long args;
+
+ if (NULL == (long *) arg)
+ err = ide_do_drive_cmd(drive, &rq, ide_wait);
+ else {
+ if (!(err = verify_area(VERIFY_READ,(long *)arg,sizeof(long))))
+ {
+ args = get_user((long *)arg);
+ if (!(err = verify_area(VERIFY_WRITE,(long *)arg,sizeof(long)))) {
+ rq.buffer = (char *) &args;
+ err = ide_do_drive_cmd(drive, &rq, ide_wait);
+ put_user(args,(long *)arg);
+ }
+ }
+ }
+ return err;
+ }
+ case HDIO_SET_PIO_MODE:
+ if (!suser())
+ return -EACCES;
+ if (MINOR(inode->i_rdev) & PARTN_MASK)
+ return -EINVAL;
+ if (!HWIF(drive)->tuneproc)
+ return -ENOSYS;
+ save_flags(flags);
+ cli();
+ drive->pio_req = (int) arg;
+ drive->special.b.set_pio = 1;
+ restore_flags(flags);
+ return 0;
+
+ RO_IOCTLS(inode->i_rdev, arg);
+
+ default:
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return ide_cdrom_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ return idetape_blkdev_ioctl(drive, inode, file, cmd, arg);
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ return -EPERM;
+ }
+}
+
+static int ide_check_media_change (kdev_t i_rdev)
+{
+ ide_drive_t *drive;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL)
+ return -ENODEV;
+#ifdef CONFIG_BLK_DEV_IDECD
+ if (drive->media == ide_cdrom)
+ return ide_cdrom_check_media_change (drive);
+#endif /* CONFIG_BLK_DEV_IDECD */
+ if (drive->removeable) /* for disks */
+ return 1; /* always assume it was changed */
+ return 0;
+}
+
+void ide_fixstring (byte *s, const int bytecount, const int byteswap)
+{
+ byte *p = s, *end = &s[bytecount & ~1]; /* bytecount must be even */
+
+ if (byteswap) {
+ /* convert from big-endian to host byte order */
+ for (p = end ; p != s;) {
+ unsigned short *pp = (unsigned short *) (p -= 2);
+ *pp = ntohs(*pp);
+ }
+ }
+
+ /* strip leading blanks */
+ while (s != end && *s == ' ')
+ ++s;
+
+ /* compress internal blanks and strip trailing blanks */
+ while (s != end && *s) {
+ if (*s++ != ' ' || (s != end && *s && *s != ' '))
+ *p++ = *(s-1);
+ }
+
+ /* wipe out trailing garbage */
+ while (p != end)
+ *p++ = '\0';
+}
+
+static inline void do_identify (ide_drive_t *drive, byte cmd)
+{
+ int bswap;
+ struct hd_driveid *id;
+ unsigned long capacity, check;
+
+ id = drive->id = kmalloc (SECTOR_WORDS*4, GFP_KERNEL);
+ ide_input_data(drive, id, SECTOR_WORDS); /* read 512 bytes of id info */
+ sti();
+
+ /*
+ * EATA SCSI controllers do a hardware ATA emulation: ignore them
+ */
+ if ((id->model[0] == 'P' && id->model[1] == 'M')
+ || (id->model[0] == 'S' && id->model[1] == 'K')) {
+ printk("%s: EATA SCSI HBA %.10s\n", drive->name, id->model);
+ drive->present = 0;
+ return;
+ }
+
+ /*
+ * WIN_IDENTIFY returns little-endian info,
+ * WIN_PIDENTIFY *usually* returns little-endian info.
+ */
+ bswap = 1;
+ if (cmd == WIN_PIDENTIFY) {
+ if ((id->model[0] == 'N' && id->model[1] == 'E') /* NEC */
+ || (id->model[0] == 'F' && id->model[1] == 'X') /* Mitsumi */
+ || (id->model[0] == 'P' && id->model[1] == 'i'))/* Pioneer */
+ bswap = 0; /* Vertos drives may still be weird */
+ }
+ ide_fixstring (id->model, sizeof(id->model), bswap);
+ ide_fixstring (id->fw_rev, sizeof(id->fw_rev), bswap);
+ ide_fixstring (id->serial_no, sizeof(id->serial_no), bswap);
+
+ /*
+ * Check for an ATAPI device
+ */
+
+ if (cmd == WIN_PIDENTIFY) {
+ byte type = (id->config >> 8) & 0x1f;
+ printk("%s: %s, ATAPI ", drive->name, id->model);
+ switch (type) {
+ case 0: /* Early cdrom models used zero */
+ case 5:
+#ifdef CONFIG_BLK_DEV_IDECD
+ printk ("CDROM drive\n");
+ drive->media = ide_cdrom;
+ drive->present = 1;
+ drive->removeable = 1;
+ return;
+#else
+ printk ("CDROM ");
+ break;
+#endif /* CONFIG_BLK_DEV_IDECD */
+ case 1:
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ printk ("TAPE drive");
+ if (idetape_identify_device (drive,id)) {
+ drive->media = ide_tape;
+ drive->present = 1;
+ drive->removeable = 1;
+ if (HWIF(drive)->dmaproc != NULL &&
+ !HWIF(drive)->dmaproc(ide_dma_check, drive))
+ printk(", DMA");
+ printk("\n");
+ }
+ else {
+ drive->present = 0;
+ printk ("\nide-tape: the tape is not supported by this version of the driver\n");
+ }
+ return;
+#else
+ printk ("TAPE ");
+ break;
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ default:
+ drive->present = 0;
+ printk("Type %d - Unknown device\n", type);
+ return;
+ }
+ drive->present = 0;
+ printk("- not supported by this kernel\n");
+ return;
+ }
+
+ /* check for removeable disks (eg. SYQUEST), ignore 'WD' drives */
+ if (id->config & (1<<7)) { /* removeable disk ? */
+ if (id->model[0] != 'W' || id->model[1] != 'D')
+ drive->removeable = 1;
+ }
+
+ drive->media = ide_disk;
+ /* Extract geometry if we did not already have one for the drive */
+ if (!drive->present) {
+ drive->present = 1;
+ drive->cyl = drive->bios_cyl = id->cyls;
+ drive->head = drive->bios_head = id->heads;
+ drive->sect = drive->bios_sect = id->sectors;
+ }
+ /* Handle logical geometry translation by the drive */
+ if ((id->field_valid & 1) && id->cur_cyls && id->cur_heads
+ && (id->cur_heads <= 16) && id->cur_sectors)
+ {
+ /*
+ * Extract the physical drive geometry for our use.
+ * Note that we purposely do *not* update the bios info.
+ * This way, programs that use it (like fdisk) will
+ * still have the same logical view as the BIOS does,
+ * which keeps the partition table from being screwed.
+ *
+ * An exception to this is the cylinder count,
+ * which we reexamine later on to correct for 1024 limitations.
+ */
+ drive->cyl = id->cur_cyls;
+ drive->head = id->cur_heads;
+ drive->sect = id->cur_sectors;
+
+ /* check for word-swapped "capacity" field in id information */
+ capacity = drive->cyl * drive->head * drive->sect;
+ check = (id->cur_capacity0 << 16) | id->cur_capacity1;
+ if (check == capacity) { /* was it swapped? */
+ /* yes, bring it into little-endian order: */
+ id->cur_capacity0 = (capacity >> 0) & 0xffff;
+ id->cur_capacity1 = (capacity >> 16) & 0xffff;
+ }
+ }
+ /* Use physical geometry if what we have still makes no sense */
+ if ((!drive->head || drive->head > 16) && id->heads && id->heads <= 16) {
+ drive->cyl = id->cyls;
+ drive->head = id->heads;
+ drive->sect = id->sectors;
+ }
+ /* Correct the number of cyls if the bios value is too small */
+ if (drive->sect == drive->bios_sect && drive->head == drive->bios_head) {
+ if (drive->cyl > drive->bios_cyl)
+ drive->bios_cyl = drive->cyl;
+ }
+
+ (void) current_capacity (drive); /* initialize LBA selection */
+
+ printk ("%s: %.40s, %ldMB w/%dKB Cache, %sCHS=%d/%d/%d",
+ drive->name, id->model, current_capacity(drive)/2048L, id->buf_size/2,
+ drive->select.b.lba ? "LBA, " : "",
+ drive->bios_cyl, drive->bios_head, drive->bios_sect);
+
+ drive->mult_count = 0;
+ if (id->max_multsect) {
+ drive->mult_req = INITIAL_MULT_COUNT;
+ if (drive->mult_req > id->max_multsect)
+ drive->mult_req = id->max_multsect;
+ if (drive->mult_req || ((id->multsect_valid & 1) && id->multsect))
+ drive->special.b.set_multmode = 1;
+ }
+ if (HWIF(drive)->dmaproc != NULL) { /* hwif supports DMA? */
+ if (!(HWIF(drive)->dmaproc(ide_dma_check, drive)))
+ printk(", DMA");
+ }
+ printk("\n");
+}
+
+/*
+ * Delay for *at least* 10ms. As we don't know how much time is left
+ * until the next tick occurs, we wait an extra tick to be safe.
+ * This is used only during the probing/polling for drives at boot time.
+ */
+static void delay_10ms (void)
+{
+ unsigned long timer = jiffies + (HZ + 99)/100 + 1;
+ while (timer > jiffies);
+}
+
+/*
+ * try_to_identify() sends an ATA(PI) IDENTIFY request to a drive
+ * and waits for a response. It also monitors irqs while this is
+ * happening, in hope of automatically determining which one is
+ * being used by the interface.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ */
+static int try_to_identify (ide_drive_t *drive, byte cmd)
+{
+ int hd_status, rc;
+ unsigned long timeout;
+ int irqs = 0;
+
+ if (!HWIF(drive)->irq) { /* already got an IRQ? */
+ probe_irq_off(probe_irq_on()); /* clear dangling irqs */
+ irqs = probe_irq_on(); /* start monitoring irqs */
+ OUT_BYTE(drive->ctl,IDE_CONTROL_REG); /* enable device irq */
+ }
+
+ delay_10ms(); /* take a deep breath */
+ if ((IN_BYTE(IDE_ALTSTATUS_REG) ^ IN_BYTE(IDE_STATUS_REG)) & ~INDEX_STAT) {
+ printk("%s: probing with STATUS instead of ALTSTATUS\n", drive->name);
+ hd_status = IDE_STATUS_REG; /* ancient Seagate drives */
+ } else
+ hd_status = IDE_ALTSTATUS_REG; /* use non-intrusive polling */
+
+ OUT_BYTE(cmd,IDE_COMMAND_REG); /* ask drive for ID */
+ timeout = ((cmd == WIN_IDENTIFY) ? WAIT_WORSTCASE : WAIT_PIDENTIFY) / 2;
+ timeout += jiffies;
+ do {
+ if (jiffies > timeout) {
+ if (!HWIF(drive)->irq)
+ (void) probe_irq_off(irqs);
+ return 1; /* drive timed-out */
+ }
+ delay_10ms(); /* give drive a breather */
+ } while (IN_BYTE(hd_status) & BUSY_STAT);
+
+ delay_10ms(); /* wait for IRQ and DRQ_STAT */
+ if (OK_STAT(GET_STAT(),DRQ_STAT,BAD_R_STAT)) {
+ cli(); /* some systems need this */
+ do_identify(drive, cmd); /* drive returned ID */
+ if (drive->present && drive->media != ide_tape) {
+ ide_tuneproc_t *tuneproc = HWIF(drive)->tuneproc;
+ if (tuneproc != NULL && drive->autotune == 1)
+ tuneproc(drive, 255); /* auto-tune PIO mode */
+ }
+ rc = 0; /* drive responded with ID */
+ } else
+ rc = 2; /* drive refused ID */
+ if (!HWIF(drive)->irq) {
+ irqs = probe_irq_off(irqs); /* get irq number */
+ if (irqs > 0)
+ HWIF(drive)->irq = irqs;
+ else /* Mmmm.. multiple IRQs */
+ printk("%s: IRQ probe failed (%d)\n", drive->name, irqs);
+ }
+ return rc;
+}
+
+/*
+ * do_probe() has the difficult job of finding a drive if it exists,
+ * without getting hung up if it doesn't exist, without trampling on
+ * ethernet cards, and without leaving any IRQs dangling to haunt us later.
+ *
+ * If a drive is "known" to exist (from CMOS or kernel parameters),
+ * but does not respond right away, the probe will "hang in there"
+ * for the maximum wait time (about 30 seconds), otherwise it will
+ * exit much more quickly.
+ *
+ * Returns: 0 device was identified
+ * 1 device timed-out (no response to identify request)
+ * 2 device aborted the command (refused to identify itself)
+ * 3 bad status from device (possible for ATAPI drives)
+ * 4 probe was not attempted because failure was obvious
+ */
+static int do_probe (ide_drive_t *drive, byte cmd)
+{
+ int rc;
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ if (drive->present) { /* avoid waiting for inappropriate probes */
+ if ((drive->media != ide_disk) && (cmd == WIN_IDENTIFY))
+ return 4;
+ }
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+#ifdef DEBUG
+ printk("probing for %s: present=%d, media=%d, probetype=%s\n",
+ drive->name, drive->present, drive->media,
+ (cmd == WIN_IDENTIFY) ? "ATA" : "ATAPI");
+#endif
+#ifdef CONFIG_BLK_DEV_HT6560B
+ if (HWIF(drive)->selectproc)
+ HWIF(drive)->selectproc (drive);
+#endif /* CONFIG_BLK_DEV_HT6560B */
+ OUT_BYTE(drive->select.all,IDE_SELECT_REG); /* select target drive */
+ delay_10ms(); /* wait for BUSY_STAT */
+ if (IN_BYTE(IDE_SELECT_REG) != drive->select.all && !drive->present) {
+ OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ return 3; /* no i/f present: avoid killing ethernet cards */
+ }
+
+ if (OK_STAT(GET_STAT(),READY_STAT,BUSY_STAT)
+ || drive->present || cmd == WIN_PIDENTIFY)
+ {
+ if ((rc = try_to_identify(drive,cmd))) /* send cmd and wait */
+ rc = try_to_identify(drive,cmd); /* failed: try again */
+ if (rc == 1)
+ printk("%s: no response (status = 0x%02x)\n", drive->name, GET_STAT());
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ } else {
+ rc = 3; /* not present or maybe ATAPI */
+ }
+ if (drive->select.b.unit != 0) {
+ OUT_BYTE(0xa0,IDE_SELECT_REG); /* exit with drive0 selected */
+ delay_10ms();
+ (void) GET_STAT(); /* ensure drive irq is clear */
+ }
+ return rc;
+}
+
+/*
+ * probe_for_drive() tests for existance of a given drive using do_probe().
+ *
+ * Returns: 0 no device was found
+ * 1 device was found (note: drive->present might still be 0)
+ */
+static inline byte probe_for_drive (ide_drive_t *drive)
+{
+ if (drive->noprobe) /* skip probing? */
+ return drive->present;
+ if (do_probe(drive, WIN_IDENTIFY) >= 2) { /* if !(success||timed-out) */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ (void) do_probe(drive, WIN_PIDENTIFY); /* look for ATAPI device */
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+ }
+ if (!drive->present)
+ return 0; /* drive not found */
+ if (drive->id == NULL) { /* identification failed? */
+ if (drive->media == ide_disk) {
+ printk ("%s: non-IDE drive, CHS=%d/%d/%d\n",
+ drive->name, drive->cyl, drive->head, drive->sect);
+ }
+#ifdef CONFIG_BLK_DEV_IDECD
+ else if (drive->media == ide_cdrom) {
+ printk("%s: ATAPI cdrom (?)\n", drive->name);
+ }
+#endif /* CONFIG_BLK_DEV_IDECD */
+ else {
+ drive->present = 0; /* nuke it */
+ return 1; /* drive was found */
+ }
+ }
+ if (drive->media == ide_disk && !drive->select.b.lba) {
+ if (!drive->head || drive->head > 16) {
+ printk("%s: INVALID GEOMETRY: %d PHYSICAL HEADS?\n",
+ drive->name, drive->head);
+ drive->present = 0;
+ }
+ }
+ return 1; /* drive was found */
+}
+
+/*
+ * This routine only knows how to look for drive units 0 and 1
+ * on an interface, so any setting of MAX_DRIVES > 2 won't work here.
+ */
+static void probe_for_drives (ide_hwif_t *hwif)
+{
+ unsigned int unit;
+
+ if (check_region(hwif->io_base,8) || check_region(hwif->ctl_port,1)) {
+ int msgout = 0;
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ drive->present = 0;
+ printk("%s: ERROR, PORTS ALREADY IN USE\n", drive->name);
+ msgout = 1;
+ }
+ }
+ if (!msgout)
+ printk("%s: ports already in use, skipping probe\n", hwif->name);
+ } else {
+ unsigned long flags;
+ save_flags(flags);
+
+#if (MAX_DRIVES > 2)
+ printk("%s: probing for first 2 of %d possible drives\n", hwif->name, MAX_DRIVES);
+#endif
+ sti(); /* needed for jiffies and irq probing */
+ /*
+ * Second drive should only exist if first drive was found,
+ * but a lot of cdrom drives seem to be configured as slave-only
+ */
+ for (unit = 0; unit < 2; ++unit) { /* note the hardcoded '2' */
+ ide_drive_t *drive = &hwif->drives[unit];
+ (void) probe_for_drive (drive);
+ }
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if (drive->present) {
+ hwif->present = 1;
+ request_region(hwif->io_base, 8, hwif->name);
+ request_region(hwif->ctl_port, 1, hwif->name);
+ break;
+ }
+ }
+ restore_flags(flags);
+ }
+}
+
+/*
+ * stridx() returns the offset of c within s,
+ * or -1 if c is '\0' or not found within s.
+ */
+static int stridx (const char *s, char c)
+{
+ char *i = strchr(s, c);
+ return (i && c) ? i - s : -1;
+}
+
+/*
+ * match_parm() does parsing for ide_setup():
+ *
+ * 1. the first char of s must be '='.
+ * 2. if the remainder matches one of the supplied keywords,
+ * the index (1 based) of the keyword is negated and returned.
+ * 3. if the remainder is a series of no more than max_vals numbers
+ * separated by commas, the numbers are saved in vals[] and a
+ * count of how many were saved is returned. Base10 is assumed,
+ * and base16 is allowed when prefixed with "0x".
+ * 4. otherwise, zero is returned.
+ */
+static int match_parm (char *s, const char *keywords[], int vals[], int max_vals)
+{
+ static const char *decimal = "0123456789";
+ static const char *hex = "0123456789abcdef";
+ int i, n;
+
+ if (*s++ == '=') {
+ /*
+ * Try matching against the supplied keywords,
+ * and return -(index+1) if we match one
+ */
+ for (i = 0; *keywords != NULL; ++i) {
+ if (!strcmp(s, *keywords++))
+ return -(i+1);
+ }
+ /*
+ * Look for a series of no more than "max_vals"
+ * numeric values separated by commas, in base10,
+ * or base16 when prefixed with "0x".
+ * Return a count of how many were found.
+ */
+ for (n = 0; (i = stridx(decimal, *s)) >= 0;) {
+ vals[n] = i;
+ while ((i = stridx(decimal, *++s)) >= 0)
+ vals[n] = (vals[n] * 10) + i;
+ if (*s == 'x' && !vals[n]) {
+ while ((i = stridx(hex, *++s)) >= 0)
+ vals[n] = (vals[n] * 0x10) + i;
+ }
+ if (++n == max_vals)
+ break;
+ if (*s == ',')
+ ++s;
+ }
+ if (!*s)
+ return n;
+ }
+ return 0; /* zero = nothing matched */
+}
+
+/*
+ * ide_setup() gets called VERY EARLY during initialization,
+ * to handle kernel "command line" strings beginning with "hdx="
+ * or "ide". Here is the complete set currently supported:
+ *
+ * "hdx=" is recognized for all "x" from "a" to "h", such as "hdc".
+ * "idex=" is recognized for all "x" from "0" to "3", such as "ide1".
+ *
+ * "hdx=noprobe" : drive may be present, but do not probe for it
+ * "hdx=nowerr" : ignore the WRERR_STAT bit on this drive
+ * "hdx=cdrom" : drive is present, and is a cdrom drive
+ * "hdx=cyl,head,sect" : disk drive is present, with specified geometry
+ * "hdx=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * if possible for this drive only.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ *
+ * "idex=noprobe" : do not attempt to access/use this interface
+ * "idex=base" : probe for an interface at the addr specified,
+ * where "base" is usually 0x1f0 or 0x170
+ * and "ctl" is assumed to be "base"+0x206
+ * "idex=base,ctl" : specify both base and ctl
+ * "idex=base,ctl,irq" : specify base, ctl, and irq number
+ * "idex=autotune" : driver will attempt to tune interface speed
+ * to the fastest PIO mode supported,
+ * for all drives on this interface.
+ * Not fully supported by all chipset types,
+ * and quite likely to cause trouble with
+ * older/odd IDE drives.
+ * "idex=noautotune" : driver will NOT attempt to tune interface speed
+ * This is the default for most chipsets,
+ * except the cmd640.
+ *
+ * The following two are valid ONLY on ide0,
+ * and the defaults for the base,ctl ports must not be altered.
+ *
+ * "ide0=serialize" : do not overlap operations on ide0 and ide1.
+ * "ide0=dtc2278" : probe/support DTC2278 interface
+ * "ide0=ht6560b" : probe/support HT6560B interface
+ * "ide0=cmd640_vlb" : *REQUIRED* for VLB cards with the CMD640 chip
+ * (not for PCI -- automatically detected)
+ * "ide0=qd6580" : probe/support qd6580 interface
+ * "ide0=ali14xx" : probe/support ali14xx chipsets (ALI M1439, M1443, M1445)
+ * "ide0=umc8672" : probe/support umc8672 chipsets
+ */
+void ide_setup (char *s)
+{
+ int i, vals[3];
+ ide_hwif_t *hwif;
+ ide_drive_t *drive;
+ unsigned int hw, unit;
+ const char max_drive = 'a' + ((MAX_HWIFS * MAX_DRIVES) - 1);
+ const char max_hwif = '0' + (MAX_HWIFS - 1);
+
+ printk("ide_setup: %s", s);
+ init_ide_data ();
+
+ /*
+ * Look for drive options: "hdx="
+ */
+ if (s[0] == 'h' && s[1] == 'd' && s[2] >= 'a' && s[2] <= max_drive) {
+ const char *hd_words[] = {"noprobe", "nowerr", "cdrom", "serialize",
+ "autotune", "noautotune", NULL};
+ unit = s[2] - 'a';
+ hw = unit / MAX_DRIVES;
+ unit = unit % MAX_DRIVES;
+ hwif = &ide_hwifs[hw];
+ drive = &hwif->drives[unit];
+ switch (match_parm(&s[3], hd_words, vals, 3)) {
+ case -1: /* "noprobe" */
+ drive->noprobe = 1;
+ goto done;
+ case -2: /* "nowerr" */
+ drive->bad_wstat = BAD_R_STAT;
+ hwif->noprobe = 0;
+ goto done;
+ case -3: /* "cdrom" */
+ drive->present = 1;
+ drive->media = ide_cdrom;
+ hwif->noprobe = 0;
+ goto done;
+ case -4: /* "serialize" */
+ printk(" -- USE \"ide%c=serialize\" INSTEAD", '0'+hw);
+ goto do_serialize;
+ case -5: /* "autotune" */
+ drive->autotune = 1;
+ goto done;
+ case -6: /* "noautotune" */
+ drive->autotune = 2;
+ goto done;
+ case 3: /* cyl,head,sect */
+ drive->media = ide_disk;
+ drive->cyl = drive->bios_cyl = vals[0];
+ drive->head = drive->bios_head = vals[1];
+ drive->sect = drive->bios_sect = vals[2];
+ drive->present = 1;
+ drive->forced_geom = 1;
+ hwif->noprobe = 0;
+ goto done;
+ default:
+ goto bad_option;
+ }
+ }
+ /*
+ * Look for interface options: "idex="
+ */
+ if (s[0] == 'i' && s[1] == 'd' && s[2] == 'e' && s[3] >= '0' && s[3] <= max_hwif) {
+ /*
+ * Be VERY CAREFUL changing this: note hardcoded indexes below
+ */
+ const char *ide_words[] = {"noprobe", "serialize", "autotune", "noautotune",
+ "qd6580", "ht6560b", "cmd640_vlb", "dtc2278", "umc8672", "ali14xx", NULL};
+ hw = s[3] - '0';
+ hwif = &ide_hwifs[hw];
+ i = match_parm(&s[4], ide_words, vals, 3);
+
+ /*
+ * Cryptic check to ensure chipset not already set for hwif:
+ */
+ if (i != -1 && i != -2) {
+ if (hwif->chipset != ide_unknown)
+ goto bad_option;
+ if (i < 0 && ide_hwifs[1].chipset != ide_unknown)
+ goto bad_option;
+ }
+ /*
+ * Interface keywords work only for ide0:
+ */
+ if (i <= -6 && hw != 0)
+ goto bad_hwif;
+
+ switch (i) {
+#ifdef CONFIG_BLK_DEV_ALI14XX
+ case -10: /* "ali14xx" */
+ {
+ extern void init_ali14xx (void);
+ init_ali14xx();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_ALI14XX */
+#ifdef CONFIG_BLK_DEV_UMC8672
+ case -9: /* "umc8672" */
+ {
+ extern void init_umc8672 (void);
+ init_umc8672();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_UMC8672 */
+#ifdef CONFIG_BLK_DEV_DTC2278
+ case -8: /* "dtc2278" */
+ {
+ extern void init_dtc2278 (void);
+ init_dtc2278();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_DTC2278 */
+#ifdef CONFIG_BLK_DEV_CMD640
+ case -7: /* "cmd640_vlb" */
+ {
+ extern int cmd640_vlb; /* flag for cmd640.c */
+ cmd640_vlb = 1;
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_CMD640 */
+#ifdef CONFIG_BLK_DEV_HT6560B
+ case -6: /* "ht6560b" */
+ {
+ extern void init_ht6560b (void);
+ init_ht6560b();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_HT6560B */
+#if CONFIG_BLK_DEV_QD6580
+ case -5: /* "qd6580" (no secondary i/f) */
+ {
+ extern void init_qd6580 (void);
+ init_qd6580();
+ goto done;
+ }
+#endif /* CONFIG_BLK_DEV_QD6580 */
+ case -4: /* "noautotune" */
+ hwif->drives[0].autotune = 2;
+ hwif->drives[1].autotune = 2;
+ goto done;
+ case -3: /* "autotune" */
+ hwif->drives[0].autotune = 1;
+ hwif->drives[1].autotune = 1;
+ goto done;
+ case -2: /* "serialize" */
+ do_serialize:
+ if (hw > 1) goto bad_hwif;
+ ide_hwifs[0].serialized = 1;
+ goto done;
+
+ case -1: /* "noprobe" */
+ hwif->noprobe = 1;
+ goto done;
+
+ case 1: /* base */
+ vals[1] = vals[0] + 0x206; /* default ctl */
+ case 2: /* base,ctl */
+ vals[2] = 0; /* default irq = probe for it */
+ case 3: /* base,ctl,irq */
+ hwif->io_base = vals[0];
+ hwif->ctl_port = vals[1];
+ hwif->irq = vals[2];
+ hwif->noprobe = 0;
+ hwif->chipset = ide_generic;
+ goto done;
+
+ case 0: goto bad_option;
+ default:
+ printk(" -- SUPPORT NOT CONFIGURED IN THIS KERNEL\n");
+ return;
+ }
+ }
+bad_option:
+ printk(" -- BAD OPTION\n");
+ return;
+bad_hwif:
+ printk("-- NOT SUPPORTED ON ide%d", hw);
+done:
+ printk("\n");
+}
+
+/*
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t i_rdev, int xparm, const char *msg)
+{
+ ide_drive_t *drive;
+ static const byte head_vals[] = {4, 8, 16, 32, 64, 128, 255, 0};
+ const byte *heads = head_vals;
+ unsigned long tracks;
+
+ if ((drive = get_info_ptr(i_rdev)) == NULL || drive->forced_geom)
+ return 0;
+
+ if (xparm > 1 && xparm <= drive->bios_head && drive->bios_sect == 63)
+ return 0; /* we already have a translation */
+
+ printk("%s ", msg);
+
+ if (drive->id) {
+ drive->cyl = drive->id->cyls;
+ drive->head = drive->id->heads;
+ drive->sect = drive->id->sectors;
+ }
+ drive->bios_cyl = drive->cyl;
+ drive->bios_head = drive->head;
+ drive->bios_sect = drive->sect;
+ drive->special.b.set_geometry = 1;
+
+ tracks = drive->bios_cyl * drive->bios_head * drive->bios_sect / 63;
+ drive->bios_sect = 63;
+ if (xparm > 1) {
+ drive->bios_head = xparm;
+ drive->bios_cyl = tracks / drive->bios_head;
+ } else {
+ while (drive->bios_cyl >= 1024) {
+ drive->bios_head = *heads;
+ drive->bios_cyl = tracks / drive->bios_head;
+ if (0 == *++heads)
+ break;
+ }
+#if FAKE_FDISK_FOR_EZDRIVE
+ if (xparm == -1) {
+ drive->remap_0_to_1 = 1;
+ msg = "0->1";
+ } else
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ if (xparm == 1) {
+ drive->sect0 = 63;
+ drive->bios_cyl = (tracks - 1) / drive->bios_head;
+ msg = "+63";
+ }
+ printk("[remap %s] ", msg);
+ }
+ drive->part[0].nr_sects = current_capacity(drive);
+ printk("[%d/%d/%d]", drive->bios_cyl, drive->bios_head, drive->bios_sect);
+ return 1;
+}
+
+/*
+ * We query CMOS about hard disks : it could be that we have a SCSI/ESDI/etc
+ * controller that is BIOS compatible with ST-506, and thus showing up in our
+ * BIOS table, but not register compatible, and therefore not present in CMOS.
+ *
+ * Furthermore, we will assume that our ST-506 drives <if any> are the primary
+ * drives in the system -- the ones reflected as drive 1 or 2. The first
+ * drive is stored in the high nibble of CMOS byte 0x12, the second in the low
+ * nibble. This will be either a 4 bit drive type or 0xf indicating use byte
+ * 0x19 for an 8 bit type, drive 1, 0x1a for drive 2 in CMOS. A non-zero value
+ * means we have an AT controller hard disk for that drive.
+ *
+ * Of course, there is no guarantee that either drive is actually on the
+ * "primary" IDE interface, but we don't bother trying to sort that out here.
+ * If a drive is not actually on the primary interface, then these parameters
+ * will be ignored. This results in the user having to supply the logical
+ * drive geometry as a boot parameter for each drive not on the primary i/f.
+ *
+ * The only "perfect" way to handle this would be to modify the setup.[cS] code
+ * to do BIOS calls Int13h/Fn08h and Int13h/Fn48h to get all of the drive info
+ * for us during initialization. I have the necessary docs -- any takers? -ml
+ */
+
+static void probe_cmos_for_drives (ide_hwif_t *hwif)
+{
+#ifdef __i386__
+ extern struct drive_info_struct drive_info;
+ byte cmos_disks, *BIOS = (byte *) &drive_info;
+ int unit;
+
+ outb_p(0x12,0x70); /* specify CMOS address 0x12 */
+ cmos_disks = inb_p(0x71); /* read the data from 0x12 */
+ /* Extract drive geometry from CMOS+BIOS if not already setup */
+ for (unit = 0; unit < MAX_DRIVES; ++unit) {
+ ide_drive_t *drive = &hwif->drives[unit];
+ if ((cmos_disks & (0xf0 >> (unit*4))) && !drive->present) {
+ drive->cyl = drive->bios_cyl = *(unsigned short *)BIOS;
+ drive->head = drive->bios_head = *(BIOS+2);
+ drive->sect = drive->bios_sect = *(BIOS+14);
+ drive->ctl = *(BIOS+8);
+ drive->present = 1;
+ }
+ BIOS += 16;
+ }
+#endif
+}
+
+/*
+ * This routine sets up the irq for an ide interface, and creates a new
+ * hwgroup for the irq/hwif if none was previously assigned.
+ *
+ * The SA_INTERRUPT in sa_flags means ide_intr() is always entered with
+ * interrupts completely disabled. This can be bad for interrupt latency,
+ * but anything else has led to problems on some machines. We re-enable
+ * interrupts as much as we can safely do in most places.
+ */
+static int init_irq (ide_hwif_t *hwif)
+{
+ unsigned long flags;
+ int irq = hwif->irq;
+ ide_hwgroup_t *hwgroup = irq_to_hwgroup[irq];
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Grab the irq if we don't already have it from a previous hwif
+ */
+ if (hwgroup == NULL) {
+ if (request_irq(irq, ide_intr, SA_INTERRUPT|SA_SAMPLE_RANDOM, hwif->name)) {
+ restore_flags(flags);
+ printk(" -- FAILED!");
+ return 1;
+ }
+ }
+ /*
+ * Check for serialization with ide1.
+ * This code depends on us having already taken care of ide1.
+ */
+ if (hwif->serialized && hwif->name[3] == '0' && ide_hwifs[1].present)
+ hwgroup = ide_hwifs[1].hwgroup;
+ /*
+ * If this is the first interface in a group,
+ * then we need to create the hwgroup structure
+ */
+ if (hwgroup == NULL) {
+ hwgroup = kmalloc (sizeof(ide_hwgroup_t), GFP_KERNEL);
+ hwgroup->hwif = hwif->next = hwif;
+ hwgroup->rq = NULL;
+ hwgroup->handler = NULL;
+ hwgroup->drive = &hwif->drives[0];
+ hwgroup->poll_timeout = 0;
+ init_timer(&hwgroup->timer);
+ hwgroup->timer.function = &timer_expiry;
+ hwgroup->timer.data = (unsigned long) hwgroup;
+ } else {
+ hwif->next = hwgroup->hwif->next;
+ hwgroup->hwif->next = hwif;
+ }
+ hwif->hwgroup = hwgroup;
+ irq_to_hwgroup[irq] = hwgroup;
+
+ restore_flags(flags); /* safe now that hwif->hwgroup is set up */
+
+ printk("%s at 0x%03x-0x%03x,0x%03x on irq %d", hwif->name,
+ hwif->io_base, hwif->io_base+7, hwif->ctl_port, irq);
+ if (hwgroup->hwif != hwif)
+ printk(" (serialized with %s)", hwgroup->hwif->name);
+ printk("\n");
+ return 0;
+}
+
+static struct file_operations ide_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ ide_ioctl, /* ioctl */
+ NULL, /* mmap */
+ ide_open, /* open */
+ ide_release, /* release */
+ block_fsync /* fsync */
+ ,NULL, /* fasync */
+ ide_check_media_change, /* check_media_change */
+ revalidate_disk /* revalidate */
+};
+
+#ifdef CONFIG_PCI
+#if defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON)
+
+typedef void (ide_pci_init_proc_t)(byte, byte);
+
+/*
+ * ide_probe_pci() scans PCI for a specific vendor/device function,
+ * and invokes the supplied init routine for each instance detected.
+ */
+static void ide_probe_pci (unsigned short vendor, unsigned short device, ide_pci_init_proc_t *init, int func_adj)
+{
+ unsigned long flags;
+ unsigned index;
+ byte fn, bus;
+
+ save_flags(flags);
+ cli();
+ for (index = 0; !pcibios_find_device (vendor, device, index, &bus, &fn); ++index) {
+ init (bus, fn + func_adj);
+ }
+ restore_flags(flags);
+}
+
+#endif /* defined(CONFIG_BLK_DEV_RZ1000) || defined(CONFIG_BLK_DEV_TRITON) */
+#endif /* CONFIG_PCI */
+
+/*
+ * ide_init_pci() finds/initializes "known" PCI IDE interfaces
+ *
+ * This routine should ideally be using pcibios_find_class() to find
+ * all IDE interfaces, but that function causes some systems to "go weird".
+ */
+static void probe_for_hwifs (void)
+{
+#ifdef CONFIG_PCI
+ /*
+ * Find/initialize PCI IDE interfaces
+ */
+ if (pcibios_present()) {
+#ifdef CONFIG_BLK_DEV_RZ1000
+ ide_pci_init_proc_t init_rz1000;
+ ide_probe_pci (PCI_VENDOR_ID_PCTECH, PCI_DEVICE_ID_PCTECH_RZ1000, &init_rz1000, 0);
+#endif /* CONFIG_BLK_DEV_RZ1000 */
+#ifdef CONFIG_BLK_DEV_TRITON
+ /*
+ * Apparently the BIOS32 services on Intel motherboards are
+ * buggy and won't find the PCI_DEVICE_ID_INTEL_82371_1 for us.
+ * So instead, we search for PCI_DEVICE_ID_INTEL_82371_0,
+ * and then add 1.
+ */
+ ide_probe_pci (PCI_VENDOR_ID_INTEL, PCI_DEVICE_ID_INTEL_82371_0, &ide_init_triton, 1);
+#endif /* CONFIG_BLK_DEV_TRITON */
+ }
+#endif /* CONFIG_PCI */
+#ifdef CONFIG_BLK_DEV_CMD640
+ {
+ extern void ide_probe_for_cmd640x (void);
+ ide_probe_for_cmd640x();
+ }
+#endif
+}
+
+/*
+ * This is gets invoked once during initialization, to set *everything* up
+ */
+int ide_init (void)
+{
+ int h;
+
+ init_ide_data ();
+ /*
+ * Probe for special "known" interface chipsets
+ */
+ probe_for_hwifs ();
+
+ /*
+ * Probe for drives in the usual way.. CMOS/BIOS, then poke at ports
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (!hwif->noprobe) {
+ if (hwif->io_base == HD_DATA)
+ probe_cmos_for_drives (hwif);
+ probe_for_drives (hwif);
+ }
+ if (hwif->present) {
+ if (!hwif->irq) {
+ if (!(hwif->irq = default_irqs[h])) {
+ printk("%s: DISABLED, NO IRQ\n", hwif->name);
+ hwif->present = 0;
+ continue;
+ }
+ }
+#ifdef CONFIG_BLK_DEV_HD
+ if (hwif->irq == HD_IRQ && hwif->io_base != HD_DATA) {
+ printk("%s: CANNOT SHARE IRQ WITH OLD HARDDISK DRIVER (hd.c)\n", hwif->name);
+ hwif->present = 0;
+ }
+#endif /* CONFIG_BLK_DEV_HD */
+ }
+ }
+
+ /*
+ * Now we try to set up irqs and major devices for what was found
+ */
+ for (h = MAX_HWIFS-1; h >= 0; --h) {
+ void (*rfn)(void);
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ if (!hwif->present)
+ continue;
+ hwif->present = 0; /* we set it back to 1 if all is ok below */
+ switch (hwif->major) {
+ case IDE0_MAJOR: rfn = &do_ide0_request; break;
+ case IDE1_MAJOR: rfn = &do_ide1_request; break;
+ case IDE2_MAJOR: rfn = &do_ide2_request; break;
+ case IDE3_MAJOR: rfn = &do_ide3_request; break;
+ default:
+ printk("%s: request_fn NOT DEFINED\n", hwif->name);
+ continue;
+ }
+ if (register_blkdev (hwif->major, hwif->name, &ide_fops)) {
+ printk("%s: UNABLE TO GET MAJOR NUMBER %d\n", hwif->name, hwif->major);
+ } else if (init_irq (hwif)) {
+ printk("%s: UNABLE TO GET IRQ %d\n", hwif->name, hwif->irq);
+ (void) unregister_blkdev (hwif->major, hwif->name);
+ } else {
+ init_gendisk(hwif);
+ blk_dev[hwif->major].request_fn = rfn;
+ read_ahead[hwif->major] = 8; /* (4kB) */
+ hwif->present = 1; /* success */
+ }
+ }
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ idetape_register_chrdev(); /* Register character device interface to the ide tape */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ return 0;
+}
diff --git a/i386/i386at/gpl/linux/block/ide.h b/i386/i386at/gpl/linux/block/ide.h
new file mode 100644
index 00000000..b1ebc4c3
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide.h
@@ -0,0 +1,655 @@
+/*
+ * linux/drivers/block/ide.h
+ *
+ * Copyright (C) 1994, 1995 Linus Torvalds & authors
+ */
+
+#include <linux/config.h>
+
+/*
+ * This is the multiple IDE interface driver, as evolved from hd.c.
+ * It supports up to four IDE interfaces, on one or more IRQs (usually 14 & 15).
+ * There can be up to two drives per interface, as per the ATA-2 spec.
+ *
+ * Primary i/f: ide0: major=3; (hda) minor=0; (hdb) minor=64
+ * Secondary i/f: ide1: major=22; (hdc or hd1a) minor=0; (hdd or hd1b) minor=64
+ * Tertiary i/f: ide2: major=33; (hde) minor=0; (hdf) minor=64
+ * Quaternary i/f: ide3: major=34; (hdg) minor=0; (hdh) minor=64
+ */
+
+/******************************************************************************
+ * IDE driver configuration options (play with these as desired):
+ *
+ * REALLY_SLOW_IO can be defined in ide.c and ide-cd.c, if necessary
+ */
+#undef REALLY_FAST_IO /* define if ide ports are perfect */
+#define INITIAL_MULT_COUNT 0 /* off=0; on=2,4,8,16,32, etc.. */
+
+#ifndef SUPPORT_VLB_SYNC /* 1 to support weird 32-bit chips */
+#define SUPPORT_VLB_SYNC 1 /* 0 to reduce kernel size */
+#endif
+#ifndef DISK_RECOVERY_TIME /* off=0; on=access_delay_time */
+#define DISK_RECOVERY_TIME 0 /* for hardware that needs it */
+#endif
+#ifndef OK_TO_RESET_CONTROLLER /* 1 needed for good error recovery */
+#define OK_TO_RESET_CONTROLLER 1 /* 0 for use with AH2372A/B interface */
+#endif
+#ifndef FAKE_FDISK_FOR_EZDRIVE /* 1 to help linux fdisk with EZDRIVE */
+#define FAKE_FDISK_FOR_EZDRIVE 1 /* 0 to reduce kernel size */
+#endif
+#ifndef FANCY_STATUS_DUMPS /* 1 for human-readable drive errors */
+#define FANCY_STATUS_DUMPS 1 /* 0 to reduce kernel size */
+#endif
+
+#if defined(CONFIG_BLK_DEV_IDECD) || defined(CONFIG_BLK_DEV_IDETAPE)
+#define CONFIG_BLK_DEV_IDEATAPI 1
+#endif
+
+/*
+ * IDE_DRIVE_CMD is used to implement many features of the hdparm utility
+ */
+#define IDE_DRIVE_CMD 99 /* (magic) undef to reduce kernel size*/
+
+/*
+ * "No user-serviceable parts" beyond this point :)
+ *****************************************************************************/
+
+typedef unsigned char byte; /* used everywhere */
+
+/*
+ * Probably not wise to fiddle with these
+ */
+#define ERROR_MAX 8 /* Max read/write errors per sector */
+#define ERROR_RESET 3 /* Reset controller every 4th retry */
+#define ERROR_RECAL 1 /* Recalibrate every 2nd retry */
+
+/*
+ * Ensure that various configuration flags have compatible settings
+ */
+#ifdef REALLY_SLOW_IO
+#undef REALLY_FAST_IO
+#endif
+
+/*
+ * Definitions for accessing IDE controller registers
+ */
+
+#define HWIF(drive) ((ide_hwif_t *)drive->hwif)
+#define HWGROUP(drive) ((ide_hwgroup_t *)(HWIF(drive)->hwgroup))
+
+#define IDE_DATA_OFFSET (0)
+#define IDE_ERROR_OFFSET (1)
+#define IDE_NSECTOR_OFFSET (2)
+#define IDE_SECTOR_OFFSET (3)
+#define IDE_LCYL_OFFSET (4)
+#define IDE_HCYL_OFFSET (5)
+#define IDE_SELECT_OFFSET (6)
+#define IDE_STATUS_OFFSET (7)
+#define IDE_FEATURE_OFFSET IDE_ERROR_OFFSET
+#define IDE_COMMAND_OFFSET IDE_STATUS_OFFSET
+
+#define IDE_DATA_REG (HWIF(drive)->io_base+IDE_DATA_OFFSET)
+#define IDE_ERROR_REG (HWIF(drive)->io_base+IDE_ERROR_OFFSET)
+#define IDE_NSECTOR_REG (HWIF(drive)->io_base+IDE_NSECTOR_OFFSET)
+#define IDE_SECTOR_REG (HWIF(drive)->io_base+IDE_SECTOR_OFFSET)
+#define IDE_LCYL_REG (HWIF(drive)->io_base+IDE_LCYL_OFFSET)
+#define IDE_HCYL_REG (HWIF(drive)->io_base+IDE_HCYL_OFFSET)
+#define IDE_SELECT_REG (HWIF(drive)->io_base+IDE_SELECT_OFFSET)
+#define IDE_STATUS_REG (HWIF(drive)->io_base+IDE_STATUS_OFFSET)
+#define IDE_CONTROL_REG (HWIF(drive)->ctl_port)
+#define IDE_FEATURE_REG IDE_ERROR_REG
+#define IDE_COMMAND_REG IDE_STATUS_REG
+#define IDE_ALTSTATUS_REG IDE_CONTROL_REG
+
+#ifdef REALLY_FAST_IO
+#define OUT_BYTE(b,p) outb((b),p)
+#define IN_BYTE(p) (byte)inb(p)
+#else
+#define OUT_BYTE(b,p) outb_p((b),p)
+#define IN_BYTE(p) (byte)inb_p(p)
+#endif /* REALLY_FAST_IO */
+
+#define GET_ERR() IN_BYTE(IDE_ERROR_REG)
+#define GET_STAT() IN_BYTE(IDE_STATUS_REG)
+#define OK_STAT(stat,good,bad) (((stat)&((good)|(bad)))==(good))
+#define BAD_R_STAT (BUSY_STAT | ERR_STAT)
+#define BAD_W_STAT (BAD_R_STAT | WRERR_STAT)
+#define BAD_STAT (BAD_R_STAT | DRQ_STAT)
+#define DRIVE_READY (READY_STAT | SEEK_STAT)
+#define DATA_READY (DRIVE_READY | DRQ_STAT)
+
+/*
+ * Some more useful definitions
+ */
+#define IDE_MAJOR_NAME "ide" /* the same for all i/f; see also genhd.c */
+#define MAJOR_NAME IDE_MAJOR_NAME
+#define PARTN_BITS 6 /* number of minor dev bits for partitions */
+#define PARTN_MASK ((1<<PARTN_BITS)-1) /* a useful bit mask */
+#define MAX_DRIVES 2 /* per interface; 2 assumed by lots of code */
+#define MAX_HWIFS 4 /* an arbitrary, but realistic limit */
+#define SECTOR_WORDS (512 / 4) /* number of 32bit words per sector */
+
+/*
+ * Timeouts for various operations:
+ */
+#define WAIT_DRQ (5*HZ/100) /* 50msec - spec allows up to 20ms */
+#define WAIT_READY (3*HZ/100) /* 30msec - should be instantaneous */
+#define WAIT_PIDENTIFY (1*HZ) /* 1sec - should be less than 3ms (?) */
+#define WAIT_WORSTCASE (30*HZ) /* 30sec - worst case when spinning up */
+#define WAIT_CMD (10*HZ) /* 10sec - maximum wait for an IRQ to happen */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+#include "ide-tape.h"
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+#ifdef CONFIG_BLK_DEV_IDECD
+
+struct atapi_request_sense {
+ unsigned char error_code : 7;
+ unsigned char valid : 1;
+ byte reserved1;
+ unsigned char sense_key : 4;
+ unsigned char reserved2 : 1;
+ unsigned char ili : 1;
+ unsigned char reserved3 : 2;
+ byte info[4];
+ byte sense_len;
+ byte command_info[4];
+ byte asc;
+ byte ascq;
+ byte fru;
+ byte sense_key_specific[3];
+};
+
+struct packet_command {
+ char *buffer;
+ int buflen;
+ int stat;
+ struct atapi_request_sense *sense_data;
+ unsigned char c[12];
+};
+
+/* Space to hold the disk TOC. */
+
+#define MAX_TRACKS 99
+struct atapi_toc_header {
+ unsigned short toc_length;
+ byte first_track;
+ byte last_track;
+};
+
+struct atapi_toc_entry {
+ byte reserved1;
+ unsigned control : 4;
+ unsigned adr : 4;
+ byte track;
+ byte reserved2;
+ union {
+ unsigned lba;
+ struct {
+ byte reserved3;
+ byte m;
+ byte s;
+ byte f;
+ } msf;
+ } addr;
+};
+
+struct atapi_toc {
+ int last_session_lba;
+ int xa_flag;
+ unsigned capacity;
+ struct atapi_toc_header hdr;
+ struct atapi_toc_entry ent[MAX_TRACKS+1]; /* One extra for the leadout. */
+};
+
+
+/* This structure is annoyingly close to, but not identical with,
+ the cdrom_subchnl structure from cdrom.h. */
+struct atapi_cdrom_subchnl
+{
+ u_char acdsc_reserved;
+ u_char acdsc_audiostatus;
+ u_short acdsc_length;
+ u_char acdsc_format;
+
+ u_char acdsc_adr: 4;
+ u_char acdsc_ctrl: 4;
+ u_char acdsc_trk;
+ u_char acdsc_ind;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } acdsc_absaddr;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } acdsc_reladdr;
+};
+
+
+/* Extra per-device info for cdrom drives. */
+struct cdrom_info {
+
+ /* Buffer for table of contents. NULL if we haven't allocated
+ a TOC buffer for this device yet. */
+
+ struct atapi_toc *toc;
+
+ /* Sector buffer. If a read request wants only the first part of a cdrom
+ block, we cache the rest of the block here, in the expectation that that
+ data is going to be wanted soon. SECTOR_BUFFERED is the number of the
+ first buffered sector, and NSECTORS_BUFFERED is the number of sectors
+ in the buffer. Before the buffer is allocated, we should have
+ SECTOR_BUFFER == NULL and NSECTORS_BUFFERED == 0. */
+
+ unsigned long sector_buffered;
+ unsigned long nsectors_buffered;
+ char *sector_buffer;
+
+ /* The result of the last successful request sense command
+ on this device. */
+ struct atapi_request_sense sense_data;
+};
+
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+/*
+ * Now for the data we need to maintain per-drive: ide_drive_t
+ */
+
+typedef enum {ide_disk, ide_cdrom, ide_tape} ide_media_t;
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned set_geometry : 1; /* respecify drive geometry */
+ unsigned recalibrate : 1; /* seek to cyl 0 */
+ unsigned set_multmode : 1; /* set multmode count */
+ unsigned set_pio : 1; /* set pio mode */
+ unsigned reserved : 4; /* unused */
+ } b;
+ } special_t;
+
+typedef union {
+ unsigned all : 8; /* all of the bits together */
+ struct {
+ unsigned head : 4; /* always zeros here */
+ unsigned unit : 1; /* drive select number, 0 or 1 */
+ unsigned bit5 : 1; /* always 1 */
+ unsigned lba : 1; /* using LBA instead of CHS */
+ unsigned bit7 : 1; /* always 1 */
+ } b;
+ } select_t;
+
+typedef struct ide_drive_s {
+ special_t special; /* special action flags */
+ unsigned present : 1; /* drive is physically present */
+ unsigned noprobe : 1; /* from: hdx=noprobe */
+ unsigned keep_settings : 1; /* restore settings after drive reset */
+ unsigned busy : 1; /* currently doing revalidate_disk() */
+ unsigned removeable : 1; /* 1 if need to do check_media_change */
+ unsigned using_dma : 1; /* disk is using dma for read/write */
+ unsigned forced_geom : 1; /* 1 if hdx=c,h,s was given at boot */
+ unsigned unmask : 1; /* flag: okay to unmask other irqs */
+ unsigned autotune : 2; /* 1=autotune, 2=noautotune, 0=default */
+#if FAKE_FDISK_FOR_EZDRIVE
+ unsigned remap_0_to_1 : 1; /* flag: partitioned with ezdrive */
+#endif /* FAKE_FDISK_FOR_EZDRIVE */
+ ide_media_t media; /* disk, cdrom, tape */
+ select_t select; /* basic drive/head select reg value */
+ byte ctl; /* "normal" value for IDE_CONTROL_REG */
+ byte ready_stat; /* min status value for drive ready */
+ byte mult_count; /* current multiple sector setting */
+ byte mult_req; /* requested multiple sector setting */
+ byte pio_req; /* requested multiple sector setting */
+ byte io_32bit; /* 0=16-bit, 1=32-bit, 2/3=32bit+sync */
+ byte bad_wstat; /* used for ignoring WRERR_STAT */
+ byte sect0; /* offset of first sector for DM6:DDO */
+ byte usage; /* current "open()" count for drive */
+ byte head; /* "real" number of heads */
+ byte sect; /* "real" sectors per track */
+ byte bios_head; /* BIOS/fdisk/LILO number of heads */
+ byte bios_sect; /* BIOS/fdisk/LILO sectors per track */
+ unsigned short bios_cyl; /* BIOS/fdisk/LILO number of cyls */
+ unsigned short cyl; /* "real" number of cyls */
+ void *hwif; /* actually (ide_hwif_t *) */
+ struct wait_queue *wqueue; /* used to wait for drive in open() */
+ struct hd_driveid *id; /* drive model identification info */
+ struct hd_struct *part; /* drive partition table */
+ char name[4]; /* drive name, such as "hda" */
+#ifdef CONFIG_BLK_DEV_IDECD
+ struct cdrom_info cdrom_info; /* for ide-cd.c */
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ idetape_tape_t tape; /* for ide-tape.c */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+ } ide_drive_t;
+
+/*
+ * An ide_dmaproc_t() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case the caller
+ * should either try again later, or revert to PIO for the current request.
+ */
+typedef enum { ide_dma_read = 0, ide_dma_write = 1,
+ ide_dma_abort = 2, ide_dma_check = 3,
+ ide_dma_status_bad = 4, ide_dma_transferred = 5,
+ ide_dma_begin = 6 }
+ ide_dma_action_t;
+
+typedef int (ide_dmaproc_t)(ide_dma_action_t, ide_drive_t *);
+
+
+/*
+ * An ide_tuneproc_t() is used to set the speed of an IDE interface
+ * to a particular PIO mode. The "byte" parameter is used
+ * to select the PIO mode by number (0,1,2,3,4,5), and a value of 255
+ * indicates that the interface driver should "auto-tune" the PIO mode
+ * according to the drive capabilities in drive->id;
+ *
+ * Not all interface types support tuning, and not all of those
+ * support all possible PIO settings. They may silently ignore
+ * or round values as they see fit.
+ */
+typedef void (ide_tuneproc_t)(ide_drive_t *, byte);
+
+/*
+ * This is used to provide HT6560B interface support.
+ * It will probably also be used by the DC4030VL driver.
+ */
+typedef void (ide_selectproc_t) (ide_drive_t *);
+
+/*
+ * hwif_chipset_t is used to keep track of the specific hardware
+ * chipset used by each IDE interface, if known.
+ */
+typedef enum { ide_unknown, ide_generic, ide_triton,
+ ide_cmd640, ide_dtc2278, ide_ali14xx,
+ ide_qd6580, ide_umc8672, ide_ht6560b }
+ hwif_chipset_t;
+
+typedef struct hwif_s {
+ struct hwif_s *next; /* for linked-list in ide_hwgroup_t */
+ void *hwgroup; /* actually (ide_hwgroup_t *) */
+ unsigned short io_base; /* base io port addr */
+ unsigned short ctl_port; /* usually io_base+0x206 */
+ ide_drive_t drives[MAX_DRIVES]; /* drive info */
+ struct gendisk *gd; /* gendisk structure */
+ ide_tuneproc_t *tuneproc; /* routine to tune PIO mode for drives */
+#ifdef CONFIG_BLK_DEV_HT6560B
+ ide_selectproc_t *selectproc; /* tweaks hardware to select drive */
+#endif /* CONFIG_BLK_DEV_HT6560B */
+ ide_dmaproc_t *dmaproc; /* dma read/write/abort routine */
+ unsigned long *dmatable; /* dma physical region descriptor table */
+ unsigned short dma_base; /* base addr for dma ports (triton) */
+ byte irq; /* our irq number */
+ byte major; /* our major number */
+ char name[5]; /* name of interface, eg. "ide0" */
+ byte index; /* 0 for ide0; 1 for ide1; ... */
+ hwif_chipset_t chipset; /* sub-module for tuning.. */
+ unsigned noprobe : 1; /* don't probe for this interface */
+ unsigned present : 1; /* this interface exists */
+ unsigned serialized : 1; /* valid only for ide_hwifs[0] */
+ unsigned no_unmask : 1; /* disallow setting unmask bits */
+#if (DISK_RECOVERY_TIME > 0)
+ unsigned long last_time; /* time when previous rq was done */
+#endif
+#ifdef CONFIG_BLK_DEV_IDECD
+ struct request request_sense_request; /* from ide-cd.c */
+ struct packet_command request_sense_pc; /* from ide-cd.c */
+#endif /* CONFIG_BLK_DEV_IDECD */
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ ide_drive_t *tape_drive; /* Pointer to the tape on this interface */
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ } ide_hwif_t;
+
+/*
+ * internal ide interrupt handler type
+ */
+typedef void (ide_handler_t)(ide_drive_t *);
+
+typedef struct hwgroup_s {
+ ide_handler_t *handler;/* irq handler, if active */
+ ide_drive_t *drive; /* current drive */
+ ide_hwif_t *hwif; /* ptr to current hwif in linked-list */
+ struct request *rq; /* current request */
+ struct timer_list timer; /* failsafe timer */
+ struct request wrq; /* local copy of current write rq */
+ unsigned long poll_timeout; /* timeout value during long polls */
+ } ide_hwgroup_t;
+
+/*
+ * ide_hwifs[] is the master data structure used to keep track
+ * of just about everything in ide.c. Whenever possible, routines
+ * should be using pointers to a drive (ide_drive_t *) or
+ * pointers to a hwif (ide_hwif_t *), rather than indexing this
+ * structure directly (the allocation/layout may change!).
+ */
+#ifdef _IDE_C
+ ide_hwif_t ide_hwifs[MAX_HWIFS]; /* master data repository */
+#else
+extern ide_hwif_t ide_hwifs[];
+#endif
+
+/*
+ * One final include file, which references some of the data/defns from above
+ */
+#define IDE_DRIVER /* "parameter" for blk.h */
+#include <linux/blk.h>
+
+#if (DISK_RECOVERY_TIME > 0)
+void ide_set_recovery_timer (ide_hwif_t *);
+#define SET_RECOVERY_TIMER(drive) ide_set_recovery_timer (drive)
+#else
+#define SET_RECOVERY_TIMER(drive)
+#endif
+
+/*
+ * This is used for (nearly) all data transfers from the IDE interface
+ */
+void ide_input_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+/*
+ * This is used for (nearly) all data transfers to the IDE interface
+ */
+void ide_output_data (ide_drive_t *drive, void *buffer, unsigned int wcount);
+
+/*
+ * This is used on exit from the driver, to designate the next irq handler
+ * and also to start the safety timer.
+ */
+void ide_set_handler (ide_drive_t *drive, ide_handler_t *handler, unsigned int timeout);
+
+/*
+ * Error reporting, in human readable form (luxurious, but a memory hog).
+ */
+byte ide_dump_status (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * ide_error() takes action based on the error returned by the controller.
+ * The calling function must return afterwards, to restart the request.
+ */
+void ide_error (ide_drive_t *drive, const char *msg, byte stat);
+
+/*
+ * ide_fixstring() cleans up and (optionally) byte-swaps a text string,
+ * removing leading/trailing blanks and compressing internal blanks.
+ * It is primarily used to tidy up the model name/number fields as
+ * returned by the WIN_[P]IDENTIFY commands.
+ */
+void ide_fixstring (byte *s, const int bytecount, const int byteswap);
+
+/*
+ * This routine busy-waits for the drive status to be not "busy".
+ * It then checks the status for all of the "good" bits and none
+ * of the "bad" bits, and if all is okay it returns 0. All other
+ * cases return 1 after invoking ide_error() -- caller should return.
+ *
+ */
+int ide_wait_stat (ide_drive_t *drive, byte good, byte bad, unsigned long timeout);
+
+/*
+ * This routine is called from the partition-table code in genhd.c
+ * to "convert" a drive to a logical geometry with fewer than 1024 cyls.
+ *
+ * The second parameter, "xparm", determines exactly how the translation
+ * will be handled:
+ * 0 = convert to CHS with fewer than 1024 cyls
+ * using the same method as Ontrack DiskManager.
+ * 1 = same as "0", plus offset everything by 63 sectors.
+ * -1 = similar to "0", plus redirect sector 0 to sector 1.
+ * >1 = convert to a CHS geometry with "xparm" heads.
+ *
+ * Returns 0 if the translation was not possible, if the device was not
+ * an IDE disk drive, or if a geometry was "forced" on the commandline.
+ * Returns 1 if the geometry translation was successful.
+ */
+int ide_xlate_1024 (kdev_t, int, const char *);
+
+/*
+ * Start a reset operation for an IDE interface.
+ * The caller should return immediately after invoking this.
+ */
+void ide_do_reset (ide_drive_t *);
+
+/*
+ * This function is intended to be used prior to invoking ide_do_drive_cmd().
+ */
+void ide_init_drive_cmd (struct request *rq);
+
+/*
+ * "action" parameter type for ide_do_drive_cmd() below.
+ */
+typedef enum
+ {ide_wait, /* insert rq at end of list, and wait for it */
+ ide_next, /* insert rq immediately after current request */
+ ide_preempt, /* insert rq in front of current request */
+ ide_end} /* insert rq at end of list, but don't wait for it */
+ ide_action_t;
+
+/*
+ * This function issues a special IDE device request
+ * onto the request queue.
+ *
+ * If action is ide_wait, then then rq is queued at the end of
+ * the request queue, and the function sleeps until it has been
+ * processed. This is for use when invoked from an ioctl handler.
+ *
+ * If action is ide_preempt, then the rq is queued at the head of
+ * the request queue, displacing the currently-being-processed
+ * request and this function returns immediately without waiting
+ * for the new rq to be completed. This is VERY DANGEROUS, and is
+ * intended for careful use by the ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_next, then the rq is queued immediately after
+ * the currently-being-processed-request (if any), and the function
+ * returns without waiting for the new rq to be completed. As above,
+ * This is VERY DANGEROUS, and is intended for careful use by the
+ * ATAPI tape/cdrom driver code.
+ *
+ * If action is ide_end, then the rq is queued at the end of the
+ * request queue, and the function returns immediately without waiting
+ * for the new rq to be completed. This is again intended for careful
+ * use by the ATAPI tape/cdrom driver code. (Currently used by ide-tape.c,
+ * when operating in the pipelined operation mode).
+ */
+int ide_do_drive_cmd (ide_drive_t *drive, struct request *rq, ide_action_t action);
+
+/*
+ * Clean up after success/failure of an explicit drive cmd.
+ * stat/err are used only when (HWGROUP(drive)->rq->cmd == IDE_DRIVE_CMD).
+ */
+void ide_end_drive_cmd (ide_drive_t *drive, byte stat, byte err);
+
+#ifdef CONFIG_BLK_DEV_IDECD
+/*
+ * These are routines in ide-cd.c invoked from ide.c
+ */
+void ide_do_rw_cdrom (ide_drive_t *, unsigned long);
+int ide_cdrom_ioctl (ide_drive_t *, struct inode *, struct file *, unsigned int, unsigned long);
+int ide_cdrom_check_media_change (ide_drive_t *);
+int ide_cdrom_open (struct inode *, struct file *, ide_drive_t *);
+void ide_cdrom_release (struct inode *, struct file *, ide_drive_t *);
+void ide_cdrom_setup (ide_drive_t *);
+#endif /* CONFIG_BLK_DEV_IDECD */
+
+#ifdef CONFIG_BLK_DEV_IDETAPE
+
+/*
+ * Functions in ide-tape.c which are invoked from ide.c:
+ */
+
+/*
+ * idetape_identify_device is called during device probing stage to
+ * probe for an ide atapi tape drive and to initialize global variables
+ * in ide-tape.c which provide the link between the character device
+ * and the correspoding block device.
+ *
+ * Returns 1 if an ide tape was detected and is supported.
+ * Returns 0 otherwise.
+ */
+
+int idetape_identify_device (ide_drive_t *drive,struct hd_driveid *id);
+
+/*
+ * idetape_setup is called a bit later than idetape_identify_device,
+ * during the search for disk partitions, to initialize various tape
+ * state variables in ide_drive_t *drive.
+ */
+
+void idetape_setup (ide_drive_t *drive);
+
+/*
+ * idetape_do_request is our request function. It is called by ide.c
+ * to process a new request.
+ */
+
+void idetape_do_request (ide_drive_t *drive, struct request *rq, unsigned long block);
+
+/*
+ * idetape_end_request is used to finish servicing a request, and to
+ * insert a pending pipeline request into the main device queue.
+ */
+
+void idetape_end_request (byte uptodate, ide_hwgroup_t *hwgroup);
+
+/*
+ * Block device interface functions.
+ */
+
+int idetape_blkdev_ioctl (ide_drive_t *drive, struct inode *inode, struct file *file,
+ unsigned int cmd, unsigned long arg);
+int idetape_blkdev_open (struct inode *inode, struct file *filp, ide_drive_t *drive);
+void idetape_blkdev_release (struct inode *inode, struct file *filp, ide_drive_t *drive);
+
+/*
+ * idetape_register_chrdev initializes the character device interface to
+ * the ide tape drive.
+ */
+
+void idetape_register_chrdev (void);
+
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+
+#ifdef CONFIG_BLK_DEV_TRITON
+void ide_init_triton (byte, byte);
+#endif /* CONFIG_BLK_DEV_TRITON */
diff --git a/i386/i386at/gpl/linux/block/ide_modes.h b/i386/i386at/gpl/linux/block/ide_modes.h
new file mode 100644
index 00000000..e174d5dc
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/ide_modes.h
@@ -0,0 +1,142 @@
+#ifndef _IDE_MODES_H
+#define _IDE_MODES_H
+/*
+ * linux/drivers/block/ide_modes.h
+ *
+ * Copyright (C) 1996 Linus Torvalds, Igor Abramov, and Mark Lord
+ */
+
+/*
+ * Shared data/functions for determining best PIO mode for an IDE drive.
+ * Most of this stuff originally lived in cmd640.c, and changes to the
+ * ide_pio_blacklist[] table should be made with EXTREME CAUTION to avoid
+ * breaking the fragile cmd640.c support.
+ */
+
+#if defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS)
+
+#ifndef _IDE_C
+
+int ide_scan_pio_blacklist (char *model);
+unsigned int ide_get_best_pio_mode (ide_drive_t *drive);
+
+#else /* _IDE_C */
+
+/*
+ * Black list. Some drives incorrectly report their maximal PIO mode,
+ * at least in respect to CMD640. Here we keep info on some known drives.
+ */
+static struct ide_pio_info {
+ const char *name;
+ int pio;
+} ide_pio_blacklist [] = {
+/* { "Conner Peripherals 1275MB - CFS1275A", 4 }, */
+
+ { "WDC AC2700", 3 },
+ { "WDC AC2540", 3 },
+ { "WDC AC2420", 3 },
+ { "WDC AC2340", 3 },
+ { "WDC AC2250", 0 },
+ { "WDC AC2200", 0 },
+ { "WDC AC2120", 0 },
+ { "WDC AC2850", 3 },
+ { "WDC AC1270", 3 },
+ { "WDC AC1170", 3 },
+ { "WDC AC1210", 1 },
+ { "WDC AC280", 0 },
+/* { "WDC AC21000", 4 }, */
+ { "WDC AC31000", 3 },
+/* { "WDC AC21200", 4 }, */
+ { "WDC AC31200", 3 },
+/* { "WDC AC31600", 4 }, */
+
+ { "Maxtor 7131 AT", 1 },
+ { "Maxtor 7171 AT", 1 },
+ { "Maxtor 7213 AT", 1 },
+ { "Maxtor 7245 AT", 1 },
+ { "Maxtor 7345 AT", 1 },
+ { "Maxtor 7546 AT", 3 },
+ { "Maxtor 7540 AV", 3 },
+
+ { "SAMSUNG SHD-3121A", 1 },
+ { "SAMSUNG SHD-3122A", 1 },
+ { "SAMSUNG SHD-3172A", 1 },
+
+/* { "ST51080A", 4 },
+ * { "ST51270A", 4 },
+ * { "ST31220A", 4 },
+ * { "ST31640A", 4 },
+ * { "ST32140A", 4 },
+ * { "ST3780A", 4 },
+ */
+ { "ST5660A", 3 },
+ { "ST3660A", 3 },
+ { "ST3630A", 3 },
+ { "ST3655A", 3 },
+ { "ST3391A", 3 },
+ { "ST3390A", 1 },
+ { "ST3600A", 1 },
+ { "ST3290A", 0 },
+ { "ST3144A", 0 },
+
+ { "QUANTUM ELS127A", 0 },
+ { "QUANTUM ELS170A", 0 },
+ { "QUANTUM LPS240A", 0 },
+ { "QUANTUM LPS210A", 3 },
+ { "QUANTUM LPS270A", 3 },
+ { "QUANTUM LPS365A", 3 },
+ { "QUANTUM LPS540A", 3 },
+ { "QUANTUM FIREBALL", 3 }, /* For models 540/640/1080/1280 */
+ /* 1080A works fine in mode4 with triton */
+ { NULL, 0 }
+};
+
+/*
+ * This routine searches the ide_pio_blacklist for an entry
+ * matching the start/whole of the supplied model name.
+ *
+ * Returns -1 if no match found.
+ * Otherwise returns the recommended PIO mode from ide_pio_blacklist[].
+ */
+int ide_scan_pio_blacklist (char *model)
+{
+ struct ide_pio_info *p;
+
+ for (p = ide_pio_blacklist; p->name != NULL; p++) {
+ if (strncmp(p->name, model, strlen(p->name)) == 0)
+ return p->pio;
+ }
+ return -1;
+}
+
+/*
+ * This routine returns the recommended PIO mode for a given drive,
+ * based on the drive->id information and the ide_pio_blacklist[].
+ * This is used by most chipset support modules when "auto-tuning".
+ */
+unsigned int ide_get_best_pio_mode (ide_drive_t *drive)
+{
+ unsigned int pio = 0;
+ struct hd_driveid *id = drive->id;
+
+ if (id != NULL) {
+ if (HWIF(drive)->chipset != ide_cmd640 && !strcmp("QUANTUM FIREBALL1080A", id->model))
+ pio = 4;
+ else
+ pio = ide_scan_pio_blacklist(id->model);
+ if (pio == -1) {
+ pio = (id->tPIO < 2) ? id->tPIO : 2;
+ if (id->field_valid & 2) {
+ byte modes = id->eide_pio_modes;
+ if (modes & 4) pio = 5;
+ else if (modes & 2) pio = 4;
+ else if (modes & 1) pio = 3;
+ }
+ }
+ }
+ return pio;
+}
+
+#endif /* _IDE_C */
+#endif /* defined(CONFIG_BLK_DEV_CMD640) || defined(CONFIG_IDE_CHIPSETS) */
+#endif /* _IDE_MODES_H */
diff --git a/i386/i386at/gpl/linux/block/rz1000.c b/i386/i386at/gpl/linux/block/rz1000.c
new file mode 100644
index 00000000..11f1dbd5
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/rz1000.c
@@ -0,0 +1,56 @@
+/*
+ * linux/drivers/block/rz1000.c Version 0.02 Feb 08, 1996
+ *
+ * Copyright (C) 1995-1996 Linus Torvalds & author (see below)
+ */
+
+/*
+ * Principal Author/Maintainer: mlord@bnr.ca (Mark Lord)
+ *
+ * This file provides support for disabling the buggy read-ahead
+ * mode of the RZ1000 IDE chipset, commonly used on Intel motherboards.
+ */
+
+#undef REALLY_SLOW_IO /* most systems can safely undef this */
+
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/delay.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <asm/io.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include "ide.h"
+
+static void ide_pci_access_error (int rc)
+{
+ printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+
+void init_rz1000 (byte bus, byte fn)
+{
+ int rc;
+ unsigned short reg;
+
+ printk("ide: buggy RZ1000 interface: ");
+ if ((rc = pcibios_read_config_word (bus, fn, PCI_COMMAND, &reg))) {
+ ide_pci_access_error (rc);
+ } else if (!(reg & 1)) {
+ printk("not enabled\n");
+ } else {
+ if ((rc = pcibios_read_config_word(bus, fn, 0x40, &reg))
+ || (rc = pcibios_write_config_word(bus, fn, 0x40, reg & 0xdfff)))
+ {
+ ide_hwifs[0].no_unmask = 1;
+ ide_hwifs[1].no_unmask = 1;
+ ide_hwifs[0].serialized = 1;
+ ide_pci_access_error (rc);
+ printk("serialized, disabled unmasking\n");
+ } else
+ printk("disabled read-ahead\n");
+ }
+}
diff --git a/i386/i386at/gpl/linux/block/triton.c b/i386/i386at/gpl/linux/block/triton.c
new file mode 100644
index 00000000..58296611
--- /dev/null
+++ b/i386/i386at/gpl/linux/block/triton.c
@@ -0,0 +1,459 @@
+/*
+ * linux/drivers/block/triton.c Version 1.06 Feb 6, 1996
+ *
+ * Copyright (c) 1995-1996 Mark Lord
+ * May be copied or modified under the terms of the GNU General Public License
+ */
+
+/*
+ * This module provides support for the Bus Master IDE DMA function
+ * of the Intel PCI Triton chipset (82371FB).
+ *
+ * DMA is currently supported only for hard disk drives (not cdroms).
+ *
+ * Support for cdroms will likely be added at a later date,
+ * after broader experience has been obtained with hard disks.
+ *
+ * Up to four drives may be enabled for DMA, and the Triton chipset will
+ * (hopefully) arbitrate the PCI bus among them. Note that the 82371FB chip
+ * provides a single "line buffer" for the BM IDE function, so performance of
+ * multiple (two) drives doing DMA simultaneously will suffer somewhat,
+ * as they contest for that resource bottleneck. This is handled transparently
+ * inside the 82371FB chip.
+ *
+ * By default, DMA support is prepared for use, but is currently enabled only
+ * for drives which support multi-word DMA mode2 (mword2), or which are
+ * recognized as "good" (see table below). Drives with only mode0 or mode1
+ * (single or multi) DMA should also work with this chipset/driver (eg. MC2112A)
+ * but are not enabled by default. Use "hdparm -i" to view modes supported
+ * by a given drive.
+ *
+ * The hdparm-2.4 (or later) utility can be used for manually enabling/disabling
+ * DMA support, but must be (re-)compiled against this kernel version or later.
+ *
+ * To enable DMA, use "hdparm -d1 /dev/hd?" on a per-drive basis after booting.
+ * If problems arise, ide.c will disable DMA operation after a few retries.
+ * This error recovery mechanism works and has been extremely well exercised.
+ *
+ * IDE drives, depending on their vintage, may support several different modes
+ * of DMA operation. The boot-time modes are indicated with a "*" in
+ * the "hdparm -i" listing, and can be changed with *knowledgeable* use of
+ * the "hdparm -X" feature. There is seldom a need to do this, as drives
+ * normally power-up with their "best" PIO/DMA modes enabled.
+ *
+ * Testing was done with an ASUS P55TP4XE/100 system and the following drives:
+ *
+ * Quantum Fireball 1080A (1Gig w/83kB buffer), DMA mode2, PIO mode4.
+ * - DMA mode2 works well (7.4MB/sec), despite the tiny on-drive buffer.
+ * - This drive also does PIO mode4, at about the same speed as DMA mode2.
+ * An awesome drive for the price!
+ *
+ * Fujitsu M1606TA (1Gig w/256kB buffer), DMA mode2, PIO mode4.
+ * - DMA mode2 gives horrible performance (1.6MB/sec), despite the good
+ * size of the on-drive buffer and a boasted 10ms average access time.
+ * - PIO mode4 was better, but peaked at a mere 4.5MB/sec.
+ *
+ * Micropolis MC2112A (1Gig w/508kB buffer), drive pre-dates EIDE and ATA2.
+ * - DMA works fine (2.2MB/sec), probably due to the large on-drive buffer.
+ * - This older drive can also be tweaked for fastPIO (3.7MB/sec) by using
+ * maximum clock settings (5,4) and setting all flags except prefetch.
+ *
+ * Western Digital AC31000H (1Gig w/128kB buffer), DMA mode1, PIO mode3.
+ * - DMA does not work reliably. The drive appears to be somewhat tardy
+ * in deasserting DMARQ at the end of a sector. This is evident in
+ * the observation that WRITEs work most of the time, depending on
+ * cache-buffer occupancy, but multi-sector reads seldom work.
+ *
+ * Testing was done with a Gigabyte GA-586 ATE system and the following drive:
+ * (Uwe Bonnes - bon@elektron.ikp.physik.th-darmstadt.de)
+ *
+ * Western Digital AC31600H (1.6Gig w/128kB buffer), DMA mode2, PIO mode4.
+ * - much better than its 1Gig cousin, this drive is reported to work
+ * very well with DMA (7.3MB/sec).
+ *
+ * Other drives:
+ *
+ * Maxtor 7540AV (515Meg w/32kB buffer), DMA modes mword0/sword2, PIO mode3.
+ * - a budget drive, with budget performance, around 3MB/sec.
+ *
+ * Western Digital AC2850F (814Meg w/64kB buffer), DMA mode1, PIO mode3.
+ * - another "caviar" drive, similar to the AC31000, except that this one
+ * worked with DMA in at least one system. Throughput is about 3.8MB/sec
+ * for both DMA and PIO.
+ *
+ * Conner CFS850A (812Meg w/64kB buffer), DMA mode2, PIO mode4.
+ * - like most Conner models, this drive proves that even a fast interface
+ * cannot improve slow media. Both DMA and PIO peak around 3.5MB/sec.
+ *
+ * If you have any drive models to add, email your results to: mlord@bnr.ca
+ * Keep an eye on /var/adm/messages for "DMA disabled" messages.
+ *
+ * Some people have reported trouble with Intel Zappa motherboards.
+ * This can be fixed by upgrading the AMI BIOS to version 1.00.04.BS0,
+ * available from ftp://ftp.intel.com/pub/bios/10004bs0.exe
+ * (thanks to Glen Morrell <glen@spin.Stanford.edu> for researching this).
+ *
+ * And, yes, Intel Zappa boards really *do* use the Triton IDE ports.
+ */
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/timer.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/blkdev.h>
+#include <linux/hdreg.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include "ide.h"
+
+/*
+ * good_dma_drives() lists the model names (from "hdparm -i")
+ * of drives which do not support mword2 DMA but which are
+ * known to work fine with this interface under Linux.
+ */
+const char *good_dma_drives[] = {"Micropolis 2112A",
+ "CONNER CTMA 4000"};
+
+/*
+ * Our Physical Region Descriptor (PRD) table should be large enough
+ * to handle the biggest I/O request we are likely to see. Since requests
+ * can have no more than 256 sectors, and since the typical blocksize is
+ * two sectors, we could get by with a limit of 128 entries here for the
+ * usual worst case. Most requests seem to include some contiguous blocks,
+ * further reducing the number of table entries required.
+ *
+ * The driver reverts to PIO mode for individual requests that exceed
+ * this limit (possible with 512 byte blocksizes, eg. MSDOS f/s), so handling
+ * 100% of all crazy scenarios here is not necessary.
+ *
+ * As it turns out though, we must allocate a full 4KB page for this,
+ * so the two PRD tables (ide0 & ide1) will each get half of that,
+ * allowing each to have about 256 entries (8 bytes each) from this.
+ */
+#define PRD_BYTES 8
+#define PRD_ENTRIES (PAGE_SIZE / (2 * PRD_BYTES))
+
+/*
+ * dma_intr() is the handler for disk read/write DMA interrupts
+ */
+static void dma_intr (ide_drive_t *drive)
+{
+ byte stat, dma_stat;
+ int i;
+ struct request *rq = HWGROUP(drive)->rq;
+ unsigned short dma_base = HWIF(drive)->dma_base;
+
+ dma_stat = inb(dma_base+2); /* get DMA status */
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA operation */
+ stat = GET_STAT(); /* get drive status */
+ if (OK_STAT(stat,DRIVE_READY,drive->bad_wstat|DRQ_STAT)) {
+ if ((dma_stat & 7) == 4) { /* verify good DMA status */
+ rq = HWGROUP(drive)->rq;
+ for (i = rq->nr_sectors; i > 0;) {
+ i -= rq->current_nr_sectors;
+ ide_end_request(1, HWGROUP(drive));
+ }
+ return;
+ }
+ printk("%s: bad DMA status: 0x%02x\n", drive->name, dma_stat);
+ }
+ sti();
+ ide_error(drive, "dma_intr", stat);
+}
+
+/*
+ * build_dmatable() prepares a dma request.
+ * Returns 0 if all went okay, returns 1 otherwise.
+ */
+static int build_dmatable (ide_drive_t *drive)
+{
+ struct request *rq = HWGROUP(drive)->rq;
+ struct buffer_head *bh = rq->bh;
+ unsigned long size, addr, *table = HWIF(drive)->dmatable;
+ unsigned int count = 0;
+
+ do {
+ /*
+ * Determine addr and size of next buffer area. We assume that
+ * individual virtual buffers are always composed linearly in
+ * physical memory. For example, we assume that any 8kB buffer
+ * is always composed of two adjacent physical 4kB pages rather
+ * than two possibly non-adjacent physical 4kB pages.
+ */
+ if (bh == NULL) { /* paging and tape requests have (rq->bh == NULL) */
+ addr = virt_to_bus (rq->buffer);
+#ifdef CONFIG_BLK_DEV_IDETAPE
+ if (drive->media == ide_tape)
+ size = drive->tape.pc->request_transfer;
+ else
+#endif /* CONFIG_BLK_DEV_IDETAPE */
+ size = rq->nr_sectors << 9;
+ } else {
+ /* group sequential buffers into one large buffer */
+ addr = virt_to_bus (bh->b_data);
+ size = bh->b_size;
+ while ((bh = bh->b_reqnext) != NULL) {
+ if ((addr + size) != virt_to_bus (bh->b_data))
+ break;
+ size += bh->b_size;
+ }
+ }
+
+ /*
+ * Fill in the dma table, without crossing any 64kB boundaries.
+ * We assume 16-bit alignment of all blocks.
+ */
+ while (size) {
+ if (++count >= PRD_ENTRIES) {
+ printk("%s: DMA table too small\n", drive->name);
+ return 1; /* revert to PIO for this request */
+ } else {
+ unsigned long bcount = 0x10000 - (addr & 0xffff);
+ if (bcount > size)
+ bcount = size;
+ *table++ = addr;
+ *table++ = bcount;
+ addr += bcount;
+ size -= bcount;
+ }
+ }
+ } while (bh != NULL);
+ if (count) {
+ *--table |= 0x80000000; /* set End-Of-Table (EOT) bit */
+ return 0;
+ }
+ printk("%s: empty DMA table?\n", drive->name);
+ return 1; /* let the PIO routines handle this weirdness */
+}
+
+static int config_drive_for_dma (ide_drive_t *drive)
+{
+ const char **list;
+
+ struct hd_driveid *id = drive->id;
+ if (id && (id->capability & 1)) {
+ /* Enable DMA on any drive that supports mword2 DMA */
+ if ((id->field_valid & 2) && (id->dma_mword & 0x404) == 0x404) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ /* Consult the list of known "good" drives */
+ list = good_dma_drives;
+ while (*list) {
+ if (!strcmp(*list++,id->model)) {
+ drive->using_dma = 1;
+ return 0; /* DMA enabled */
+ }
+ }
+ }
+ return 1; /* DMA not enabled */
+}
+
+/*
+ * triton_dmaproc() initiates/aborts DMA read/write operations on a drive.
+ *
+ * The caller is assumed to have selected the drive and programmed the drive's
+ * sector address using CHS or LBA. All that remains is to prepare for DMA
+ * and then issue the actual read/write DMA/PIO command to the drive.
+ *
+ * For ATAPI devices, we just prepare for DMA and return. The caller should
+ * then issue the packet command to the drive and call us again with
+ * ide_dma_begin afterwards.
+ *
+ * Returns 0 if all went well.
+ * Returns 1 if DMA read/write could not be started, in which case
+ * the caller should revert to PIO for the current request.
+ */
+static int triton_dmaproc (ide_dma_action_t func, ide_drive_t *drive)
+{
+ unsigned long dma_base = HWIF(drive)->dma_base;
+ unsigned int reading = (1 << 3);
+
+ switch (func) {
+ case ide_dma_abort:
+ outb(inb(dma_base)&~1, dma_base); /* stop DMA */
+ return 0;
+ case ide_dma_check:
+ return config_drive_for_dma (drive);
+ case ide_dma_write:
+ reading = 0;
+ case ide_dma_read:
+ break;
+ case ide_dma_status_bad:
+ return ((inb(dma_base+2) & 7) != 4); /* verify good DMA status */
+ case ide_dma_transferred:
+#if 0
+ return (number of bytes actually transferred);
+#else
+ return (0);
+#endif
+ case ide_dma_begin:
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+ default:
+ printk("triton_dmaproc: unsupported func: %d\n", func);
+ return 1;
+ }
+ if (build_dmatable (drive))
+ return 1;
+ outl(virt_to_bus (HWIF(drive)->dmatable), dma_base + 4); /* PRD table */
+ outb(reading, dma_base); /* specify r/w */
+ outb(0x26, dma_base+2); /* clear status bits */
+#ifdef CONFIG_BLK_DEV_IDEATAPI
+ if (drive->media != ide_disk)
+ return 0;
+#endif /* CONFIG_BLK_DEV_IDEATAPI */
+ ide_set_handler(drive, &dma_intr, WAIT_CMD); /* issue cmd to drive */
+ OUT_BYTE(reading ? WIN_READDMA : WIN_WRITEDMA, IDE_COMMAND_REG);
+ outb(inb(dma_base)|1, dma_base); /* begin DMA */
+ return 0;
+}
+
+/*
+ * print_triton_drive_flags() displays the currently programmed options
+ * in the Triton chipset for a given drive.
+ *
+ * If fastDMA is "no", then slow ISA timings are used for DMA data xfers.
+ * If fastPIO is "no", then slow ISA timings are used for PIO data xfers.
+ * If IORDY is "no", then IORDY is assumed to always be asserted.
+ * If PreFetch is "no", then data pre-fetch/post are not used.
+ *
+ * When "fastPIO" and/or "fastDMA" are "yes", then faster PCI timings and
+ * back-to-back 16-bit data transfers are enabled, using the sample_CLKs
+ * and recovery_CLKs (PCI clock cycles) timing parameters for that interface.
+ */
+static void print_triton_drive_flags (unsigned int unit, byte flags)
+{
+ printk(" %s ", unit ? "slave :" : "master:");
+ printk( "fastDMA=%s", (flags&9) ? "on " : "off");
+ printk(" PreFetch=%s", (flags&4) ? "on " : "off");
+ printk(" IORDY=%s", (flags&2) ? "on " : "off");
+ printk(" fastPIO=%s\n", ((flags&9)==1) ? "on " : "off");
+}
+
+static void init_triton_dma (ide_hwif_t *hwif, unsigned short base)
+{
+ static unsigned long dmatable = 0;
+
+ printk(" %s: BusMaster DMA at 0x%04x-0x%04x", hwif->name, base, base+7);
+ if (check_region(base, 8)) {
+ printk(" -- ERROR, PORTS ALREADY IN USE");
+ } else {
+ request_region(base, 8, "triton DMA");
+ hwif->dma_base = base;
+ if (!dmatable) {
+ /*
+ * Since we know we are on a PCI bus, we could
+ * actually use __get_free_pages() here instead
+ * of __get_dma_pages() -- no ISA limitations.
+ */
+ dmatable = __get_dma_pages(GFP_KERNEL, 0);
+ }
+ if (dmatable) {
+ hwif->dmatable = (unsigned long *) dmatable;
+ dmatable += (PRD_ENTRIES * PRD_BYTES);
+ outl(virt_to_bus(hwif->dmatable), base + 4);
+ hwif->dmaproc = &triton_dmaproc;
+ }
+ }
+ printk("\n");
+}
+
+/*
+ * calc_mode() returns the ATA PIO mode number, based on the number
+ * of cycle clks passed in. Assumes 33Mhz bus operation (30ns per clk).
+ */
+byte calc_mode (byte clks)
+{
+ if (clks == 3) return 5;
+ if (clks == 4) return 4;
+ if (clks < 6) return 3;
+ if (clks < 8) return 2;
+ if (clks < 13) return 1;
+ return 0;
+}
+
+/*
+ * ide_init_triton() prepares the IDE driver for DMA operation.
+ * This routine is called once, from ide.c during driver initialization,
+ * for each triton chipset which is found (unlikely to be more than one).
+ */
+void ide_init_triton (byte bus, byte fn)
+{
+ int rc = 0, h;
+ int dma_enabled = 0;
+ unsigned short bmiba, pcicmd;
+ unsigned int timings;
+
+ printk("ide: Triton BM-IDE on PCI bus %d function %d\n", bus, fn);
+ /*
+ * See if IDE and BM-DMA features are enabled:
+ */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x04, &pcicmd)))
+ goto quit;
+ if ((pcicmd & 1) == 0) {
+ printk("ide: Triton IDE ports are not enabled\n");
+ goto quit;
+ }
+ if ((pcicmd & 4) == 0) {
+ printk("ide: Triton BM-DMA feature is not enabled -- upgrade your BIOS\n");
+ } else {
+ /*
+ * Get the bmiba base address
+ */
+ if ((rc = pcibios_read_config_word(bus, fn, 0x20, &bmiba)))
+ goto quit;
+ bmiba &= 0xfff0; /* extract port base address */
+ dma_enabled = 1;
+ }
+
+ /*
+ * See if ide port(s) are enabled
+ */
+ if ((rc = pcibios_read_config_dword(bus, fn, 0x40, &timings)))
+ goto quit;
+ if (!(timings & 0x80008000)) {
+ printk("ide: neither Triton IDE port is enabled\n");
+ goto quit;
+ }
+
+ /*
+ * Save the dma_base port addr for each interface
+ */
+ for (h = 0; h < MAX_HWIFS; ++h) {
+ byte s_clks, r_clks;
+ ide_hwif_t *hwif = &ide_hwifs[h];
+ unsigned short time;
+ if (hwif->io_base == 0x1f0) {
+ time = timings & 0xffff;
+ if ((timings & 0x8000) == 0) /* interface enabled? */
+ continue;
+ hwif->chipset = ide_triton;
+ if (dma_enabled)
+ init_triton_dma(hwif, bmiba);
+ } else if (hwif->io_base == 0x170) {
+ time = timings >> 16;
+ if ((timings & 0x8000) == 0) /* interface enabled? */
+ continue;
+ hwif->chipset = ide_triton;
+ if (dma_enabled)
+ init_triton_dma(hwif, bmiba + 8);
+ } else
+ continue;
+ s_clks = ((~time >> 12) & 3) + 2;
+ r_clks = ((~time >> 8) & 3) + 1;
+ printk(" %s timing: (0x%04x) sample_CLKs=%d, recovery_CLKs=%d (PIO mode%d)\n",
+ hwif->name, time, s_clks, r_clks, calc_mode(s_clks+r_clks));
+ print_triton_drive_flags (0, time & 0xf);
+ print_triton_drive_flags (1, (time >> 4) & 0xf);
+ }
+
+quit: if (rc) printk("ide: pcibios access failed - %s\n", pcibios_strerror(rc));
+}
+
diff --git a/i386/i386at/gpl/linux/include/asm/bitops.h b/i386/i386at/gpl/linux/include/asm/bitops.h
new file mode 100644
index 00000000..5387f31d
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/bitops.h
@@ -0,0 +1,137 @@
+#ifndef _I386_BITOPS_H
+#define _I386_BITOPS_H
+
+/*
+ * Copyright 1992, Linus Torvalds.
+ */
+
+/*
+ * These have to be done with inline assembly: that way the bit-setting
+ * is guaranteed to be atomic. All bit operations return 0 if the bit
+ * was cleared before the operation and != 0 if it was not.
+ *
+ * bit 0 is the LSB of addr; bit 32 is the LSB of (addr+1).
+ */
+
+#ifdef __SMP__
+#define LOCK_PREFIX "lock ; "
+#else
+#define LOCK_PREFIX ""
+#endif
+
+/*
+ * Some hacks to defeat gcc over-optimizations..
+ */
+struct __dummy { unsigned long a[100]; };
+#define ADDR (*(struct __dummy *) addr)
+#define CONST_ADDR (*(const struct __dummy *) addr)
+
+extern __inline__ int set_bit(int nr, void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__(LOCK_PREFIX
+ "btsl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"ir" (nr));
+ return oldbit;
+}
+
+extern __inline__ int clear_bit(int nr, void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__(LOCK_PREFIX
+ "btrl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"ir" (nr));
+ return oldbit;
+}
+
+extern __inline__ int change_bit(int nr, void * addr)
+{
+ int oldbit;
+
+ __asm__ __volatile__(LOCK_PREFIX
+ "btcl %2,%1\n\tsbbl %0,%0"
+ :"=r" (oldbit),"=m" (ADDR)
+ :"ir" (nr));
+ return oldbit;
+}
+
+/*
+ * This routine doesn't need to be atomic.
+ */
+extern __inline__ int test_bit(int nr, const void * addr)
+{
+ return 1UL & (((const unsigned int *) addr)[nr >> 5] >> (nr & 31));
+}
+
+/*
+ * Find-bit routines..
+ */
+extern __inline__ int find_first_zero_bit(void * addr, unsigned size)
+{
+ int res;
+
+ if (!size)
+ return 0;
+ __asm__("
+ cld
+ movl $-1,%%eax
+ xorl %%edx,%%edx
+ repe; scasl
+ je 1f
+ xorl -4(%%edi),%%eax
+ subl $4,%%edi
+ bsfl %%eax,%%edx
+1: subl %%ebx,%%edi
+ shll $3,%%edi
+ addl %%edi,%%edx"
+ :"=d" (res)
+ :"c" ((size + 31) >> 5), "D" (addr), "b" (addr)
+ :"ax", "cx", "di");
+ return res;
+}
+
+extern __inline__ int find_next_zero_bit (void * addr, int size, int offset)
+{
+ unsigned long * p = ((unsigned long *) addr) + (offset >> 5);
+ int set = 0, bit = offset & 31, res;
+
+ if (bit) {
+ /*
+ * Look for zero in first byte
+ */
+ __asm__("
+ bsfl %1,%0
+ jne 1f
+ movl $32, %0
+1: "
+ : "=r" (set)
+ : "r" (~(*p >> bit)));
+ if (set < (32 - bit))
+ return set + offset;
+ set = 32 - bit;
+ p++;
+ }
+ /*
+ * No zero yet, search remaining full bytes for a zero
+ */
+ res = find_first_zero_bit (p, size - 32 * (p - (unsigned long *) addr));
+ return (offset + set + res);
+}
+
+/*
+ * ffz = Find First Zero in word. Undefined if no zero exists,
+ * so code should check against ~0UL first..
+ */
+extern __inline__ unsigned long ffz(unsigned long word)
+{
+ __asm__("bsfl %1,%0"
+ :"=r" (word)
+ :"r" (~word));
+ return word;
+}
+
+#endif /* _I386_BITOPS_H */
diff --git a/i386/i386at/gpl/linux/include/asm/byteorder.h b/i386/i386at/gpl/linux/include/asm/byteorder.h
new file mode 100644
index 00000000..3f40767f
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/byteorder.h
@@ -0,0 +1,90 @@
+#ifndef _I386_BYTEORDER_H
+#define _I386_BYTEORDER_H
+
+#undef ntohl
+#undef ntohs
+#undef htonl
+#undef htons
+
+#ifndef __LITTLE_ENDIAN
+#define __LITTLE_ENDIAN 1234
+#endif
+
+#ifndef __LITTLE_ENDIAN_BITFIELD
+#define __LITTLE_ENDIAN_BITFIELD
+#endif
+
+/* For avoiding bswap on i386 */
+#ifdef __KERNEL__
+#include <linux/config.h>
+#endif
+
+extern unsigned long int ntohl(unsigned long int);
+extern unsigned short int ntohs(unsigned short int);
+extern unsigned long int htonl(unsigned long int);
+extern unsigned short int htons(unsigned short int);
+
+extern __inline__ unsigned long int __ntohl(unsigned long int);
+extern __inline__ unsigned short int __ntohs(unsigned short int);
+extern __inline__ unsigned long int __constant_ntohl(unsigned long int);
+extern __inline__ unsigned short int __constant_ntohs(unsigned short int);
+
+extern __inline__ unsigned long int
+__ntohl(unsigned long int x)
+{
+#if defined(__KERNEL__) && !defined(CONFIG_M386)
+ __asm__("bswap %0" : "=r" (x) : "0" (x));
+#else
+ __asm__("xchgb %b0,%h0\n\t" /* swap lower bytes */
+ "rorl $16,%0\n\t" /* swap words */
+ "xchgb %b0,%h0" /* swap higher bytes */
+ :"=q" (x)
+ : "0" (x));
+#endif
+ return x;
+}
+
+#define __constant_ntohl(x) \
+ ((unsigned long int)((((unsigned long int)(x) & 0x000000ffU) << 24) | \
+ (((unsigned long int)(x) & 0x0000ff00U) << 8) | \
+ (((unsigned long int)(x) & 0x00ff0000U) >> 8) | \
+ (((unsigned long int)(x) & 0xff000000U) >> 24)))
+
+extern __inline__ unsigned short int
+__ntohs(unsigned short int x)
+{
+ __asm__("xchgb %b0,%h0" /* swap bytes */
+ : "=q" (x)
+ : "0" (x));
+ return x;
+}
+
+#define __constant_ntohs(x) \
+ ((unsigned short int)((((unsigned short int)(x) & 0x00ff) << 8) | \
+ (((unsigned short int)(x) & 0xff00) >> 8))) \
+
+#define __htonl(x) __ntohl(x)
+#define __htons(x) __ntohs(x)
+#define __constant_htonl(x) __constant_ntohl(x)
+#define __constant_htons(x) __constant_ntohs(x)
+
+#ifdef __OPTIMIZE__
+# define ntohl(x) \
+(__builtin_constant_p((long)(x)) ? \
+ __constant_ntohl((x)) : \
+ __ntohl((x)))
+# define ntohs(x) \
+(__builtin_constant_p((short)(x)) ? \
+ __constant_ntohs((x)) : \
+ __ntohs((x)))
+# define htonl(x) \
+(__builtin_constant_p((long)(x)) ? \
+ __constant_htonl((x)) : \
+ __htonl((x)))
+# define htons(x) \
+(__builtin_constant_p((short)(x)) ? \
+ __constant_htons((x)) : \
+ __htons((x)))
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/delay.h b/i386/i386at/gpl/linux/include/asm/delay.h
new file mode 100644
index 00000000..e22e8d6b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/delay.h
@@ -0,0 +1,59 @@
+#ifndef _I386_DELAY_H
+#define _I386_DELAY_H
+
+/*
+ * Copyright (C) 1993 Linus Torvalds
+ *
+ * Delay routines, using a pre-computed "loops_per_second" value.
+ */
+
+#ifdef __SMP__
+#include <asm/smp.h>
+#endif
+
+extern __inline__ void __delay(int loops)
+{
+ __asm__ __volatile__(
+ ".align 2,0x90\n1:\tdecl %0\n\tjns 1b"
+ :/* no outputs */
+ :"a" (loops)
+ :"ax");
+}
+
+/*
+ * division by multiplication: you don't have to worry about
+ * loss of precision.
+ *
+ * Use only for very small delays ( < 1 msec). Should probably use a
+ * lookup table, really, as the multiplications take much too long with
+ * short delays. This is a "reasonable" implementation, though (and the
+ * first constant multiplications gets optimized away if the delay is
+ * a constant)
+ */
+extern __inline__ void udelay(unsigned long usecs)
+{
+ usecs *= 0x000010c6; /* 2**32 / 1000000 */
+ __asm__("mull %0"
+ :"=d" (usecs)
+#ifdef __SMP__
+ :"a" (usecs),"0" (cpu_data[smp_processor_id()].udelay_val)
+#else
+ :"a" (usecs),"0" (loops_per_sec)
+#endif
+ :"ax");
+
+ __delay(usecs);
+}
+
+extern __inline__ unsigned long muldiv(unsigned long a, unsigned long b, unsigned long c)
+{
+ __asm__("mull %1 ; divl %2"
+ :"=a" (a)
+ :"d" (b),
+ "r" (c),
+ "0" (a)
+ :"dx");
+ return a;
+}
+
+#endif /* defined(_I386_DELAY_H) */
diff --git a/i386/i386at/gpl/linux/include/asm/dma.h b/i386/i386at/gpl/linux/include/asm/dma.h
new file mode 100644
index 00000000..b739ed7d
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/dma.h
@@ -0,0 +1,271 @@
+/* $Id: dma.h,v 1.1.1.1 1997/02/25 21:27:24 thomas Exp $
+ * linux/include/asm/dma.h: Defines for using and allocating dma channels.
+ * Written by Hennus Bergman, 1992.
+ * High DMA channel support & info by Hannu Savolainen
+ * and John Boyd, Nov. 1992.
+ */
+
+#ifndef _ASM_DMA_H
+#define _ASM_DMA_H
+
+#include <asm/io.h> /* need byte IO */
+
+
+#ifdef HAVE_REALLY_SLOW_DMA_CONTROLLER
+#define dma_outb outb_p
+#else
+#define dma_outb outb
+#endif
+
+#define dma_inb inb
+
+/*
+ * NOTES about DMA transfers:
+ *
+ * controller 1: channels 0-3, byte operations, ports 00-1F
+ * controller 2: channels 4-7, word operations, ports C0-DF
+ *
+ * - ALL registers are 8 bits only, regardless of transfer size
+ * - channel 4 is not used - cascades 1 into 2.
+ * - channels 0-3 are byte - addresses/counts are for physical bytes
+ * - channels 5-7 are word - addresses/counts are for physical words
+ * - transfers must not cross physical 64K (0-3) or 128K (5-7) boundaries
+ * - transfer count loaded to registers is 1 less than actual count
+ * - controller 2 offsets are all even (2x offsets for controller 1)
+ * - page registers for 5-7 don't use data bit 0, represent 128K pages
+ * - page registers for 0-3 use bit 0, represent 64K pages
+ *
+ * DMA transfers are limited to the lower 16MB of _physical_ memory.
+ * Note that addresses loaded into registers must be _physical_ addresses,
+ * not logical addresses (which may differ if paging is active).
+ *
+ * Address mapping for channels 0-3:
+ *
+ * A23 ... A16 A15 ... A8 A7 ... A0 (Physical addresses)
+ * | ... | | ... | | ... |
+ * | ... | | ... | | ... |
+ * | ... | | ... | | ... |
+ * P7 ... P0 A7 ... A0 A7 ... A0
+ * | Page | Addr MSB | Addr LSB | (DMA registers)
+ *
+ * Address mapping for channels 5-7:
+ *
+ * A23 ... A17 A16 A15 ... A9 A8 A7 ... A1 A0 (Physical addresses)
+ * | ... | \ \ ... \ \ \ ... \ \
+ * | ... | \ \ ... \ \ \ ... \ (not used)
+ * | ... | \ \ ... \ \ \ ... \
+ * P7 ... P1 (0) A7 A6 ... A0 A7 A6 ... A0
+ * | Page | Addr MSB | Addr LSB | (DMA registers)
+ *
+ * Again, channels 5-7 transfer _physical_ words (16 bits), so addresses
+ * and counts _must_ be word-aligned (the lowest address bit is _ignored_ at
+ * the hardware level, so odd-byte transfers aren't possible).
+ *
+ * Transfer count (_not # bytes_) is limited to 64K, represented as actual
+ * count - 1 : 64K => 0xFFFF, 1 => 0x0000. Thus, count is always 1 or more,
+ * and up to 128K bytes may be transferred on channels 5-7 in one operation.
+ *
+ */
+
+#define MAX_DMA_CHANNELS 8
+
+/* The maximum address that we can perform a DMA transfer to on this platform */
+#define MAX_DMA_ADDRESS 0x1000000
+
+/* 8237 DMA controllers */
+#define IO_DMA1_BASE 0x00 /* 8 bit slave DMA, channels 0..3 */
+#define IO_DMA2_BASE 0xC0 /* 16 bit master DMA, ch 4(=slave input)..7 */
+
+/* DMA controller registers */
+#define DMA1_CMD_REG 0x08 /* command register (w) */
+#define DMA1_STAT_REG 0x08 /* status register (r) */
+#define DMA1_REQ_REG 0x09 /* request register (w) */
+#define DMA1_MASK_REG 0x0A /* single-channel mask (w) */
+#define DMA1_MODE_REG 0x0B /* mode register (w) */
+#define DMA1_CLEAR_FF_REG 0x0C /* clear pointer flip-flop (w) */
+#define DMA1_TEMP_REG 0x0D /* Temporary Register (r) */
+#define DMA1_RESET_REG 0x0D /* Master Clear (w) */
+#define DMA1_CLR_MASK_REG 0x0E /* Clear Mask */
+#define DMA1_MASK_ALL_REG 0x0F /* all-channels mask (w) */
+
+#define DMA2_CMD_REG 0xD0 /* command register (w) */
+#define DMA2_STAT_REG 0xD0 /* status register (r) */
+#define DMA2_REQ_REG 0xD2 /* request register (w) */
+#define DMA2_MASK_REG 0xD4 /* single-channel mask (w) */
+#define DMA2_MODE_REG 0xD6 /* mode register (w) */
+#define DMA2_CLEAR_FF_REG 0xD8 /* clear pointer flip-flop (w) */
+#define DMA2_TEMP_REG 0xDA /* Temporary Register (r) */
+#define DMA2_RESET_REG 0xDA /* Master Clear (w) */
+#define DMA2_CLR_MASK_REG 0xDC /* Clear Mask */
+#define DMA2_MASK_ALL_REG 0xDE /* all-channels mask (w) */
+
+#define DMA_ADDR_0 0x00 /* DMA address registers */
+#define DMA_ADDR_1 0x02
+#define DMA_ADDR_2 0x04
+#define DMA_ADDR_3 0x06
+#define DMA_ADDR_4 0xC0
+#define DMA_ADDR_5 0xC4
+#define DMA_ADDR_6 0xC8
+#define DMA_ADDR_7 0xCC
+
+#define DMA_CNT_0 0x01 /* DMA count registers */
+#define DMA_CNT_1 0x03
+#define DMA_CNT_2 0x05
+#define DMA_CNT_3 0x07
+#define DMA_CNT_4 0xC2
+#define DMA_CNT_5 0xC6
+#define DMA_CNT_6 0xCA
+#define DMA_CNT_7 0xCE
+
+#define DMA_PAGE_0 0x87 /* DMA page registers */
+#define DMA_PAGE_1 0x83
+#define DMA_PAGE_2 0x81
+#define DMA_PAGE_3 0x82
+#define DMA_PAGE_5 0x8B
+#define DMA_PAGE_6 0x89
+#define DMA_PAGE_7 0x8A
+
+#define DMA_MODE_READ 0x44 /* I/O to memory, no autoinit, increment, single mode */
+#define DMA_MODE_WRITE 0x48 /* memory to I/O, no autoinit, increment, single mode */
+#define DMA_MODE_CASCADE 0xC0 /* pass thru DREQ->HRQ, DACK<-HLDA only */
+
+/* enable/disable a specific DMA channel */
+static __inline__ void enable_dma(unsigned int dmanr)
+{
+ if (dmanr<=3)
+ dma_outb(dmanr, DMA1_MASK_REG);
+ else
+ dma_outb(dmanr & 3, DMA2_MASK_REG);
+}
+
+static __inline__ void disable_dma(unsigned int dmanr)
+{
+ if (dmanr<=3)
+ dma_outb(dmanr | 4, DMA1_MASK_REG);
+ else
+ dma_outb((dmanr & 3) | 4, DMA2_MASK_REG);
+}
+
+/* Clear the 'DMA Pointer Flip Flop'.
+ * Write 0 for LSB/MSB, 1 for MSB/LSB access.
+ * Use this once to initialize the FF to a known state.
+ * After that, keep track of it. :-)
+ * --- In order to do that, the DMA routines below should ---
+ * --- only be used while interrupts are disabled! ---
+ */
+static __inline__ void clear_dma_ff(unsigned int dmanr)
+{
+ if (dmanr<=3)
+ dma_outb(0, DMA1_CLEAR_FF_REG);
+ else
+ dma_outb(0, DMA2_CLEAR_FF_REG);
+}
+
+/* set mode (above) for a specific DMA channel */
+static __inline__ void set_dma_mode(unsigned int dmanr, char mode)
+{
+ if (dmanr<=3)
+ dma_outb(mode | dmanr, DMA1_MODE_REG);
+ else
+ dma_outb(mode | (dmanr&3), DMA2_MODE_REG);
+}
+
+/* Set only the page register bits of the transfer address.
+ * This is used for successive transfers when we know the contents of
+ * the lower 16 bits of the DMA current address register, but a 64k boundary
+ * may have been crossed.
+ */
+static __inline__ void set_dma_page(unsigned int dmanr, char pagenr)
+{
+ switch(dmanr) {
+ case 0:
+ dma_outb(pagenr, DMA_PAGE_0);
+ break;
+ case 1:
+ dma_outb(pagenr, DMA_PAGE_1);
+ break;
+ case 2:
+ dma_outb(pagenr, DMA_PAGE_2);
+ break;
+ case 3:
+ dma_outb(pagenr, DMA_PAGE_3);
+ break;
+ case 5:
+ dma_outb(pagenr & 0xfe, DMA_PAGE_5);
+ break;
+ case 6:
+ dma_outb(pagenr & 0xfe, DMA_PAGE_6);
+ break;
+ case 7:
+ dma_outb(pagenr & 0xfe, DMA_PAGE_7);
+ break;
+ }
+}
+
+
+/* Set transfer address & page bits for specific DMA channel.
+ * Assumes dma flipflop is clear.
+ */
+static __inline__ void set_dma_addr(unsigned int dmanr, unsigned int a)
+{
+ set_dma_page(dmanr, a>>16);
+ if (dmanr <= 3) {
+ dma_outb( a & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
+ dma_outb( (a>>8) & 0xff, ((dmanr&3)<<1) + IO_DMA1_BASE );
+ } else {
+ dma_outb( (a>>1) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+ dma_outb( (a>>9) & 0xff, ((dmanr&3)<<2) + IO_DMA2_BASE );
+ }
+}
+
+
+/* Set transfer size (max 64k for DMA1..3, 128k for DMA5..7) for
+ * a specific DMA channel.
+ * You must ensure the parameters are valid.
+ * NOTE: from a manual: "the number of transfers is one more
+ * than the initial word count"! This is taken into account.
+ * Assumes dma flip-flop is clear.
+ * NOTE 2: "count" represents _bytes_ and must be even for channels 5-7.
+ */
+static __inline__ void set_dma_count(unsigned int dmanr, unsigned int count)
+{
+ count--;
+ if (dmanr <= 3) {
+ dma_outb( count & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
+ dma_outb( (count>>8) & 0xff, ((dmanr&3)<<1) + 1 + IO_DMA1_BASE );
+ } else {
+ dma_outb( (count>>1) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
+ dma_outb( (count>>9) & 0xff, ((dmanr&3)<<2) + 2 + IO_DMA2_BASE );
+ }
+}
+
+
+/* Get DMA residue count. After a DMA transfer, this
+ * should return zero. Reading this while a DMA transfer is
+ * still in progress will return unpredictable results.
+ * If called before the channel has been used, it may return 1.
+ * Otherwise, it returns the number of _bytes_ left to transfer.
+ *
+ * Assumes DMA flip-flop is clear.
+ */
+static __inline__ int get_dma_residue(unsigned int dmanr)
+{
+ unsigned int io_port = (dmanr<=3)? ((dmanr&3)<<1) + 1 + IO_DMA1_BASE
+ : ((dmanr&3)<<2) + 2 + IO_DMA2_BASE;
+
+ /* using short to get 16-bit wrap around */
+ unsigned short count;
+
+ count = 1 + dma_inb(io_port);
+ count += dma_inb(io_port) << 8;
+
+ return (dmanr<=3)? count : (count<<1);
+}
+
+
+/* These are in kernel/dma.c: */
+extern int request_dma(unsigned int dmanr, const char * device_id); /* reserve a DMA channel */
+extern void free_dma(unsigned int dmanr); /* release it again */
+
+
+#endif /* _ASM_DMA_H */
diff --git a/i386/i386at/gpl/linux/include/asm/errno.h b/i386/i386at/gpl/linux/include/asm/errno.h
new file mode 100644
index 00000000..f5c37cad
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/errno.h
@@ -0,0 +1,252 @@
+#ifndef _I386_ERRNO_H
+#define _I386_ERRNO_H
+
+#ifdef MACH_INCLUDE
+#define LINUX_EPERM 1 /* Operation not permitted */
+#define LINUX_ENOENT 2 /* No such file or directory */
+#define LINUX_ESRCH 3 /* No such process */
+#define LINUX_EINTR 4 /* Interrupted system call */
+#define LINUX_EIO 5 /* I/O error */
+#define LINUX_ENXIO 6 /* No such device or address */
+#define LINUX_E2BIG 7 /* Arg list too long */
+#define LINUX_ENOEXEC 8 /* Exec format error */
+#define LINUX_EBADF 9 /* Bad file number */
+#define LINUX_ECHILD 10 /* No child processes */
+#define LINUX_EAGAIN 11 /* Try again */
+#define LINUX_ENOMEM 12 /* Out of memory */
+#define LINUX_EACCES 13 /* Permission denied */
+#define LINUX_EFAULT 14 /* Bad address */
+#define LINUX_ENOTBLK 15 /* Block device required */
+#define LINUX_EBUSY 16 /* Device or resource busy */
+#define LINUX_EEXIST 17 /* File exists */
+#define LINUX_EXDEV 18 /* Cross-device link */
+#define LINUX_ENODEV 19 /* No such device */
+#define LINUX_ENOTDIR 20 /* Not a directory */
+#define LINUX_EISDIR 21 /* Is a directory */
+#define LINUX_EINVAL 22 /* Invalid argument */
+#define LINUX_ENFILE 23 /* File table overflow */
+#define LINUX_EMFILE 24 /* Too many open files */
+#define LINUX_ENOTTY 25 /* Not a typewriter */
+#define LINUX_ETXTBSY 26 /* Text file busy */
+#define LINUX_EFBIG 27 /* File too large */
+#define LINUX_ENOSPC 28 /* No space left on device */
+#define LINUX_ESPIPE 29 /* Illegal seek */
+#define LINUX_EROFS 30 /* Read-only file system */
+#define LINUX_EMLINK 31 /* Too many links */
+#define LINUX_EPIPE 32 /* Broken pipe */
+#define LINUX_EDOM 33 /* Math argument out of domain of func */
+#define LINUX_ERANGE 34 /* Math result not representable */
+#define LINUX_EDEADLK 35 /* Resource deadlock would occur */
+#define LINUX_ENAMETOOLONG 36 /* File name too long */
+#define LINUX_ENOLCK 37 /* No record locks available */
+#define LINUX_ENOSYS 38 /* Function not implemented */
+#define LINUX_ENOTEMPTY 39 /* Directory not empty */
+#define LINUX_ELOOP 40 /* Too many symbolic links encountered */
+#define LINUX_EWOULDBLOCK LINUX_EAGAIN /* Operation would block */
+#define LINUX_ENOMSG 42 /* No message of desired type */
+#define LINUX_EIDRM 43 /* Identifier removed */
+#define LINUX_ECHRNG 44 /* Channel number out of range */
+#define LINUX_EL2NSYNC 45 /* Level 2 not synchronized */
+#define LINUX_EL3HLT 46 /* Level 3 halted */
+#define LINUX_EL3RST 47 /* Level 3 reset */
+#define LINUX_ELNRNG 48 /* Link number out of range */
+#define LINUX_EUNATCH 49 /* Protocol driver not attached */
+#define LINUX_ENOCSI 50 /* No CSI structure available */
+#define LINUX_EL2HLT 51 /* Level 2 halted */
+#define LINUX_EBADE 52 /* Invalid exchange */
+#define LINUX_EBADR 53 /* Invalid request descriptor */
+#define LINUX_EXFULL 54 /* Exchange full */
+#define LINUX_ENOANO 55 /* No anode */
+#define LINUX_EBADRQC 56 /* Invalid request code */
+#define LINUX_EBADSLT 57 /* Invalid slot */
+#define LINUX_EDEADLOCK 58 /* File locking deadlock error */
+#define LINUX_EBFONT 59 /* Bad font file format */
+#define LINUX_ENOSTR 60 /* Device not a stream */
+#define LINUX_ENODATA 61 /* No data available */
+#define LINUX_ETIME 62 /* Timer expired */
+#define LINUX_ENOSR 63 /* Out of streams resources */
+#define LINUX_ENONET 64 /* Machine is not on the network */
+#define LINUX_ENOPKG 65 /* Package not installed */
+#define LINUX_EREMOTE 66 /* Object is remote */
+#define LINUX_ENOLINK 67 /* Link has been severed */
+#define LINUX_EADV 68 /* Advertise error */
+#define LINUX_ESRMNT 69 /* Srmount error */
+#define LINUX_ECOMM 70 /* Communication error on send */
+#define LINUX_EPROTO 71 /* Protocol error */
+#define LINUX_EMULTIHOP 72 /* Multihop attempted */
+#define LINUX_EDOTDOT 73 /* RFS specific error */
+#define LINUX_EBADMSG 74 /* Not a data message */
+#define LINUX_EOVERFLOW 75 /* Value too large for defined data type */
+#define LINUX_ENOTUNIQ 76 /* Name not unique on network */
+#define LINUX_EBADFD 77 /* File descriptor in bad state */
+#define LINUX_EREMCHG 78 /* Remote address changed */
+#define LINUX_ELIBACC 79 /* Can not access a needed shared library */
+#define LINUX_ELIBBAD 80 /* Accessing a corrupted shared library */
+#define LINUX_ELIBSCN 81 /* .lib section in a.out corrupted */
+#define LINUX_ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define LINUX_ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define LINUX_EILSEQ 84 /* Illegal byte sequence */
+#define LINUX_ERESTART 85 /* Interrupted system call should be restarted */
+#define LINUX_ESTRPIPE 86 /* Streams pipe error */
+#define LINUX_EUSERS 87 /* Too many users */
+#define LINUX_ENOTSOCK 88 /* Socket operation on non-socket */
+#define LINUX_EDESTADDRREQ 89 /* Destination address required */
+#define LINUX_EMSGSIZE 90 /* Message too long */
+#define LINUX_EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define LINUX_ENOPROTOOPT 92 /* Protocol not available */
+#define LINUX_EPROTONOSUPPORT 93 /* Protocol not supported */
+#define LINUX_ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define LINUX_EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define LINUX_EPFNOSUPPORT 96 /* Protocol family not supported */
+#define LINUX_EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define LINUX_EADDRINUSE 98 /* Address already in use */
+#define LINUX_EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define LINUX_ENETDOWN 100 /* Network is down */
+#define LINUX_ENETUNREACH 101 /* Network is unreachable */
+#define LINUX_ENETRESET 102 /* Network dropped connection because of reset */
+#define LINUX_ECONNABORTED 103 /* Software caused connection abort */
+#define LINUX_ECONNRESET 104 /* Connection reset by peer */
+#define LINUX_ENOBUFS 105 /* No buffer space available */
+#define LINUX_EISCONN 106 /* Transport endpoint is already connected */
+#define LINUX_ENOTCONN 107 /* Transport endpoint is not connected */
+#define LINUX_ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define LINUX_ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define LINUX_ETIMEDOUT 110 /* Connection timed out */
+#define LINUX_ECONNREFUSED 111 /* Connection refused */
+#define LINUX_EHOSTDOWN 112 /* Host is down */
+#define LINUX_EHOSTUNREACH 113 /* No route to host */
+#define LINUX_EALREADY 114 /* Operation already in progress */
+#define LINUX_EINPROGRESS 115 /* Operation now in progress */
+#define LINUX_ESTALE 116 /* Stale NFS file handle */
+#define LINUX_EUCLEAN 117 /* Structure needs cleaning */
+#define LINUX_ENOTNAM 118 /* Not a XENIX named type file */
+#define LINUX_ENAVAIL 119 /* No XENIX semaphores available */
+#define LINUX_EISNAM 120 /* Is a named type file */
+#define LINUX_EREMOTEIO 121 /* Remote I/O error */
+#define LINUX_EDQUOT 122 /* Quota exceeded */
+#else /* ! MACH_INCLUDE */
+#define EPERM 1 /* Operation not permitted */
+#define ENOENT 2 /* No such file or directory */
+#define ESRCH 3 /* No such process */
+#define EINTR 4 /* Interrupted system call */
+#define EIO 5 /* I/O error */
+#define ENXIO 6 /* No such device or address */
+#define E2BIG 7 /* Arg list too long */
+#define ENOEXEC 8 /* Exec format error */
+#define EBADF 9 /* Bad file number */
+#define ECHILD 10 /* No child processes */
+#define EAGAIN 11 /* Try again */
+#define ENOMEM 12 /* Out of memory */
+#define EACCES 13 /* Permission denied */
+#define EFAULT 14 /* Bad address */
+#define ENOTBLK 15 /* Block device required */
+#define EBUSY 16 /* Device or resource busy */
+#define EEXIST 17 /* File exists */
+#define EXDEV 18 /* Cross-device link */
+#define ENODEV 19 /* No such device */
+#define ENOTDIR 20 /* Not a directory */
+#define EISDIR 21 /* Is a directory */
+#define EINVAL 22 /* Invalid argument */
+#define ENFILE 23 /* File table overflow */
+#define EMFILE 24 /* Too many open files */
+#define ENOTTY 25 /* Not a typewriter */
+#define ETXTBSY 26 /* Text file busy */
+#define EFBIG 27 /* File too large */
+#define ENOSPC 28 /* No space left on device */
+#define ESPIPE 29 /* Illegal seek */
+#define EROFS 30 /* Read-only file system */
+#define EMLINK 31 /* Too many links */
+#define EPIPE 32 /* Broken pipe */
+#define EDOM 33 /* Math argument out of domain of func */
+#define ERANGE 34 /* Math result not representable */
+#define EDEADLK 35 /* Resource deadlock would occur */
+#define ENAMETOOLONG 36 /* File name too long */
+#define ENOLCK 37 /* No record locks available */
+#define ENOSYS 38 /* Function not implemented */
+#define ENOTEMPTY 39 /* Directory not empty */
+#define ELOOP 40 /* Too many symbolic links encountered */
+#define EWOULDBLOCK EAGAIN /* Operation would block */
+#define ENOMSG 42 /* No message of desired type */
+#define EIDRM 43 /* Identifier removed */
+#define ECHRNG 44 /* Channel number out of range */
+#define EL2NSYNC 45 /* Level 2 not synchronized */
+#define EL3HLT 46 /* Level 3 halted */
+#define EL3RST 47 /* Level 3 reset */
+#define ELNRNG 48 /* Link number out of range */
+#define EUNATCH 49 /* Protocol driver not attached */
+#define ENOCSI 50 /* No CSI structure available */
+#define EL2HLT 51 /* Level 2 halted */
+#define EBADE 52 /* Invalid exchange */
+#define EBADR 53 /* Invalid request descriptor */
+#define EXFULL 54 /* Exchange full */
+#define ENOANO 55 /* No anode */
+#define EBADRQC 56 /* Invalid request code */
+#define EBADSLT 57 /* Invalid slot */
+#define EDEADLOCK 58 /* File locking deadlock error */
+#define EBFONT 59 /* Bad font file format */
+#define ENOSTR 60 /* Device not a stream */
+#define ENODATA 61 /* No data available */
+#define ETIME 62 /* Timer expired */
+#define ENOSR 63 /* Out of streams resources */
+#define ENONET 64 /* Machine is not on the network */
+#define ENOPKG 65 /* Package not installed */
+#define EREMOTE 66 /* Object is remote */
+#define ENOLINK 67 /* Link has been severed */
+#define EADV 68 /* Advertise error */
+#define ESRMNT 69 /* Srmount error */
+#define ECOMM 70 /* Communication error on send */
+#define EPROTO 71 /* Protocol error */
+#define EMULTIHOP 72 /* Multihop attempted */
+#define EDOTDOT 73 /* RFS specific error */
+#define EBADMSG 74 /* Not a data message */
+#define EOVERFLOW 75 /* Value too large for defined data type */
+#define ENOTUNIQ 76 /* Name not unique on network */
+#define EBADFD 77 /* File descriptor in bad state */
+#define EREMCHG 78 /* Remote address changed */
+#define ELIBACC 79 /* Can not access a needed shared library */
+#define ELIBBAD 80 /* Accessing a corrupted shared library */
+#define ELIBSCN 81 /* .lib section in a.out corrupted */
+#define ELIBMAX 82 /* Attempting to link in too many shared libraries */
+#define ELIBEXEC 83 /* Cannot exec a shared library directly */
+#define EILSEQ 84 /* Illegal byte sequence */
+#define ERESTART 85 /* Interrupted system call should be restarted */
+#define ESTRPIPE 86 /* Streams pipe error */
+#define EUSERS 87 /* Too many users */
+#define ENOTSOCK 88 /* Socket operation on non-socket */
+#define EDESTADDRREQ 89 /* Destination address required */
+#define EMSGSIZE 90 /* Message too long */
+#define EPROTOTYPE 91 /* Protocol wrong type for socket */
+#define ENOPROTOOPT 92 /* Protocol not available */
+#define EPROTONOSUPPORT 93 /* Protocol not supported */
+#define ESOCKTNOSUPPORT 94 /* Socket type not supported */
+#define EOPNOTSUPP 95 /* Operation not supported on transport endpoint */
+#define EPFNOSUPPORT 96 /* Protocol family not supported */
+#define EAFNOSUPPORT 97 /* Address family not supported by protocol */
+#define EADDRINUSE 98 /* Address already in use */
+#define EADDRNOTAVAIL 99 /* Cannot assign requested address */
+#define ENETDOWN 100 /* Network is down */
+#define ENETUNREACH 101 /* Network is unreachable */
+#define ENETRESET 102 /* Network dropped connection because of reset */
+#define ECONNABORTED 103 /* Software caused connection abort */
+#define ECONNRESET 104 /* Connection reset by peer */
+#define ENOBUFS 105 /* No buffer space available */
+#define EISCONN 106 /* Transport endpoint is already connected */
+#define ENOTCONN 107 /* Transport endpoint is not connected */
+#define ESHUTDOWN 108 /* Cannot send after transport endpoint shutdown */
+#define ETOOMANYREFS 109 /* Too many references: cannot splice */
+#define ETIMEDOUT 110 /* Connection timed out */
+#define ECONNREFUSED 111 /* Connection refused */
+#define EHOSTDOWN 112 /* Host is down */
+#define EHOSTUNREACH 113 /* No route to host */
+#define EALREADY 114 /* Operation already in progress */
+#define EINPROGRESS 115 /* Operation now in progress */
+#define ESTALE 116 /* Stale NFS file handle */
+#define EUCLEAN 117 /* Structure needs cleaning */
+#define ENOTNAM 118 /* Not a XENIX named type file */
+#define ENAVAIL 119 /* No XENIX semaphores available */
+#define EISNAM 120 /* Is a named type file */
+#define EREMOTEIO 121 /* Remote I/O error */
+#define EDQUOT 122 /* Quota exceeded */
+#endif /* ! MACH_INCLUDE */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/fcntl.h b/i386/i386at/gpl/linux/include/asm/fcntl.h
new file mode 100644
index 00000000..0cb8fcdb
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/fcntl.h
@@ -0,0 +1,64 @@
+#ifndef _I386_FCNTL_H
+#define _I386_FCNTL_H
+
+/* open/fcntl - O_SYNC is only implemented on blocks devices and on files
+ located on an ext2 file system */
+#define O_ACCMODE 0003
+#define O_RDONLY 00
+#define O_WRONLY 01
+#define O_RDWR 02
+#define O_CREAT 0100 /* not fcntl */
+#define O_EXCL 0200 /* not fcntl */
+#define O_NOCTTY 0400 /* not fcntl */
+#define O_TRUNC 01000 /* not fcntl */
+#define O_APPEND 02000
+#define O_NONBLOCK 04000
+#define O_NDELAY O_NONBLOCK
+#define O_SYNC 010000
+#define FASYNC 020000 /* fcntl, for BSD compatibility */
+
+#define F_DUPFD 0 /* dup */
+#define F_GETFD 1 /* get f_flags */
+#define F_SETFD 2 /* set f_flags */
+#define F_GETFL 3 /* more flags (cloexec) */
+#define F_SETFL 4
+#define F_GETLK 5
+#define F_SETLK 6
+#define F_SETLKW 7
+
+#define F_SETOWN 8 /* for sockets. */
+#define F_GETOWN 9 /* for sockets. */
+
+/* for F_[GET|SET]FL */
+#define FD_CLOEXEC 1 /* actually anything with low bit set goes */
+
+/* for posix fcntl() and lockf() */
+#define F_RDLCK 0
+#define F_WRLCK 1
+#define F_UNLCK 2
+
+/* for old implementation of bsd flock () */
+#define F_EXLCK 4 /* or 3 */
+#define F_SHLCK 8 /* or 4 */
+
+/* operations for bsd flock(), also used by the kernel implementation */
+#define LOCK_SH 1 /* shared lock */
+#define LOCK_EX 2 /* exclusive lock */
+#define LOCK_NB 4 /* or'd with one of the above to prevent
+ blocking */
+#define LOCK_UN 8 /* remove lock */
+
+#ifdef __KERNEL__
+#define F_POSIX 1
+#define F_FLOCK 2
+#endif /* __KERNEL__ */
+
+struct flock {
+ short l_type;
+ short l_whence;
+ off_t l_start;
+ off_t l_len;
+ pid_t l_pid;
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/floppy.h b/i386/i386at/gpl/linux/include/asm/floppy.h
new file mode 100644
index 00000000..93c58fea
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/floppy.h
@@ -0,0 +1,56 @@
+/*
+ * Architecture specific parts of the Floppy driver
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License. See the file "COPYING" in the main directory of this archive
+ * for more details.
+ *
+ * Copyright (C) 1995
+ */
+#ifndef __ASM_I386_FLOPPY_H
+#define __ASM_I386_FLOPPY_H
+
+#define fd_inb(port) inb_p(port)
+#define fd_outb(port,value) outb_p(port,value)
+
+#define fd_enable_dma() enable_dma(FLOPPY_DMA)
+#define fd_disable_dma() disable_dma(FLOPPY_DMA)
+#define fd_request_dma() request_dma(FLOPPY_DMA,"floppy")
+#define fd_free_dma() free_dma(FLOPPY_DMA)
+#define fd_clear_dma_ff() clear_dma_ff(FLOPPY_DMA)
+#define fd_set_dma_mode(mode) set_dma_mode(FLOPPY_DMA,mode)
+#define fd_set_dma_addr(addr) set_dma_addr(FLOPPY_DMA,addr)
+#define fd_set_dma_count(count) set_dma_count(FLOPPY_DMA,count)
+#define fd_enable_irq() enable_irq(FLOPPY_IRQ)
+#define fd_disable_irq() disable_irq(FLOPPY_IRQ)
+#define fd_cacheflush(addr,size) /* nothing */
+#define fd_request_irq() request_irq(FLOPPY_IRQ, floppy_interrupt, \
+ SA_INTERRUPT|SA_SAMPLE_RANDOM, \
+ "floppy")
+#define fd_free_irq() free_irq(FLOPPY_IRQ);
+
+__inline__ void virtual_dma_init(void)
+{
+ /* Nothing to do on an i386 */
+}
+
+static int FDC1 = 0x3f0;
+static int FDC2 = -1;
+
+#define FLOPPY0_TYPE ((CMOS_READ(0x10) >> 4) & 15)
+#define FLOPPY1_TYPE (CMOS_READ(0x10) & 15)
+
+#define N_FDC 2
+#define N_DRIVE 8
+
+/*
+ * The DMA channel used by the floppy controller cannot access data at
+ * addresses >= 16MB
+ *
+ * Went back to the 1MB limit, as some people had problems with the floppy
+ * driver otherwise. It doesn't matter much for performance anyway, as most
+ * floppy accesses go through the track buffer.
+ */
+#define CROSS_64KB(a,s) ((unsigned long)(a)/K_64 != ((unsigned long)(a) + (s) - 1) / K_64)
+
+#endif /* __ASM_I386_FLOPPY_H */
diff --git a/i386/i386at/gpl/linux/include/asm/io.h b/i386/i386at/gpl/linux/include/asm/io.h
new file mode 100644
index 00000000..98e32ce6
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/io.h
@@ -0,0 +1,213 @@
+#ifndef _ASM_IO_H
+#define _ASM_IO_H
+
+/*
+ * This file contains the definitions for the x86 IO instructions
+ * inb/inw/inl/outb/outw/outl and the "string versions" of the same
+ * (insb/insw/insl/outsb/outsw/outsl). You can also use "pausing"
+ * versions of the single-IO instructions (inb_p/inw_p/..).
+ *
+ * This file is not meant to be obfuscating: it's just complicated
+ * to (a) handle it all in a way that makes gcc able to optimize it
+ * as well as possible and (b) trying to avoid writing the same thing
+ * over and over again with slight variations and possibly making a
+ * mistake somewhere.
+ */
+
+/*
+ * Thanks to James van Artsdalen for a better timing-fix than
+ * the two short jumps: using outb's to a nonexistent port seems
+ * to guarantee better timings even on fast machines.
+ *
+ * On the other hand, I'd like to be sure of a non-existent port:
+ * I feel a bit unsafe about using 0x80 (should be safe, though)
+ *
+ * Linus
+ */
+
+#ifdef SLOW_IO_BY_JUMPING
+#define __SLOW_DOWN_IO __asm__ __volatile__("jmp 1f\n1:\tjmp 1f\n1:")
+#else
+#define __SLOW_DOWN_IO __asm__ __volatile__("outb %al,$0x80")
+#endif
+
+#ifdef REALLY_SLOW_IO
+#define SLOW_DOWN_IO { __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; __SLOW_DOWN_IO; }
+#else
+#define SLOW_DOWN_IO __SLOW_DOWN_IO
+#endif
+
+/*
+ * Change virtual addresses to physical addresses and vv.
+ * These are trivial on the 1:1 Linux/i386 mapping (but if we ever
+ * make the kernel segment mapped at 0, we need to do translation
+ * on the i386 as well)
+ */
+extern inline unsigned long virt_to_phys(volatile void * address)
+{
+ return (unsigned long) address;
+}
+
+extern inline void * phys_to_virt(unsigned long address)
+{
+ return (void *) address;
+}
+
+/*
+ * IO bus memory addresses are also 1:1 with the physical address
+ */
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#define readl(addr) (*(volatile unsigned int *) (addr))
+
+#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
+#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+#define memset_io(a,b,c) memset((void *)(a),(b),(c))
+#define memcpy_fromio(a,b,c) memcpy((a),(void *)(b),(c))
+#define memcpy_toio(a,b,c) memcpy((void *)(a),(b),(c))
+
+/*
+ * Again, i386 does not require mem IO specific function.
+ */
+
+#define eth_io_copy_and_sum(a,b,c,d) eth_copy_and_sum((a),(void *)(b),(c),(d))
+
+/*
+ * Talk about misusing macros..
+ */
+
+#define __OUT1(s,x) \
+extern inline void __out##s(unsigned x value, unsigned short port) {
+
+#define __OUT2(s,s1,s2) \
+__asm__ __volatile__ ("out" #s " %" s1 "0,%" s2 "1"
+
+#define __OUT(s,s1,x) \
+__OUT1(s,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); } \
+__OUT1(s##c,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); } \
+__OUT1(s##_p,x) __OUT2(s,s1,"w") : : "a" (value), "d" (port)); SLOW_DOWN_IO; } \
+__OUT1(s##c_p,x) __OUT2(s,s1,"") : : "a" (value), "id" (port)); SLOW_DOWN_IO; }
+
+#define __IN1(s) \
+extern inline RETURN_TYPE __in##s(unsigned short port) { RETURN_TYPE _v;
+
+#define __IN2(s,s1,s2) \
+__asm__ __volatile__ ("in" #s " %" s2 "1,%" s1 "0"
+
+#define __IN(s,s1,i...) \
+__IN1(s) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); return _v; } \
+__IN1(s##c) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); return _v; } \
+__IN1(s##_p) __IN2(s,s1,"w") : "=a" (_v) : "d" (port) ,##i ); SLOW_DOWN_IO; return _v; } \
+__IN1(s##c_p) __IN2(s,s1,"") : "=a" (_v) : "id" (port) ,##i ); SLOW_DOWN_IO; return _v; }
+
+#define __INS(s) \
+extern inline void ins##s(unsigned short port, void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; ins" #s \
+: "=D" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define __OUTS(s) \
+extern inline void outs##s(unsigned short port, const void * addr, unsigned long count) \
+{ __asm__ __volatile__ ("cld ; rep ; outs" #s \
+: "=S" (addr), "=c" (count) : "d" (port),"0" (addr),"1" (count)); }
+
+#define RETURN_TYPE unsigned char
+/* __IN(b,"b","0" (0)) */
+__IN(b,"")
+#undef RETURN_TYPE
+#define RETURN_TYPE unsigned short
+/* __IN(w,"w","0" (0)) */
+__IN(w,"")
+#undef RETURN_TYPE
+#define RETURN_TYPE unsigned int
+__IN(l,"")
+#undef RETURN_TYPE
+
+__OUT(b,"b",char)
+__OUT(w,"w",short)
+__OUT(l,,int)
+
+__INS(b)
+__INS(w)
+__INS(l)
+
+__OUTS(b)
+__OUTS(w)
+__OUTS(l)
+
+/*
+ * Note that due to the way __builtin_constant_p() works, you
+ * - can't use it inside a inline function (it will never be true)
+ * - you don't have to worry about side effects within the __builtin..
+ */
+#define outb(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc((val),(port)) : \
+ __outb((val),(port)))
+
+#define inb(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc(port) : \
+ __inb(port))
+
+#define outb_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outbc_p((val),(port)) : \
+ __outb_p((val),(port)))
+
+#define inb_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inbc_p(port) : \
+ __inb_p(port))
+
+#define outw(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc((val),(port)) : \
+ __outw((val),(port)))
+
+#define inw(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc(port) : \
+ __inw(port))
+
+#define outw_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outwc_p((val),(port)) : \
+ __outw_p((val),(port)))
+
+#define inw_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inwc_p(port) : \
+ __inw_p(port))
+
+#define outl(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc((val),(port)) : \
+ __outl((val),(port)))
+
+#define inl(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc(port) : \
+ __inl(port))
+
+#define outl_p(val,port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __outlc_p((val),(port)) : \
+ __outl_p((val),(port)))
+
+#define inl_p(port) \
+((__builtin_constant_p((port)) && (port) < 256) ? \
+ __inlc_p(port) : \
+ __inl_p(port))
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/ioctl.h b/i386/i386at/gpl/linux/include/asm/ioctl.h
new file mode 100644
index 00000000..cc94e3b9
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/ioctl.h
@@ -0,0 +1,75 @@
+/* $Id: ioctl.h,v 1.1.1.1 1997/02/25 21:27:24 thomas Exp $
+ *
+ * linux/ioctl.h for Linux by H.H. Bergman.
+ */
+
+#ifndef _ASMI386_IOCTL_H
+#define _ASMI386_IOCTL_H
+
+/* ioctl command encoding: 32 bits total, command in lower 16 bits,
+ * size of the parameter structure in the lower 14 bits of the
+ * upper 16 bits.
+ * Encoding the size of the parameter structure in the ioctl request
+ * is useful for catching programs compiled with old versions
+ * and to avoid overwriting user space outside the user buffer area.
+ * The highest 2 bits are reserved for indicating the ``access mode''.
+ * NOTE: This limits the max parameter size to 16kB -1 !
+ */
+
+/*
+ * The following is for compatibility across the various Linux
+ * platforms. The i386 ioctl numbering scheme doesn't really enforce
+ * a type field. De facto, however, the top 8 bits of the lower 16
+ * bits are indeed used as a type field, so we might just as well make
+ * this explicit here. Please be sure to use the decoding macros
+ * below from now on.
+ */
+#define _IOC_NRBITS 8
+#define _IOC_TYPEBITS 8
+#define _IOC_SIZEBITS 14
+#define _IOC_DIRBITS 2
+
+#define _IOC_NRMASK ((1 << _IOC_NRBITS)-1)
+#define _IOC_TYPEMASK ((1 << _IOC_TYPEBITS)-1)
+#define _IOC_SIZEMASK ((1 << _IOC_SIZEBITS)-1)
+#define _IOC_DIRMASK ((1 << _IOC_DIRBITS)-1)
+
+#define _IOC_NRSHIFT 0
+#define _IOC_TYPESHIFT (_IOC_NRSHIFT+_IOC_NRBITS)
+#define _IOC_SIZESHIFT (_IOC_TYPESHIFT+_IOC_TYPEBITS)
+#define _IOC_DIRSHIFT (_IOC_SIZESHIFT+_IOC_SIZEBITS)
+
+/*
+ * Direction bits.
+ */
+#define _IOC_NONE 0U
+#define _IOC_WRITE 1U
+#define _IOC_READ 2U
+
+#define _IOC(dir,type,nr,size) \
+ (((dir) << _IOC_DIRSHIFT) | \
+ ((type) << _IOC_TYPESHIFT) | \
+ ((nr) << _IOC_NRSHIFT) | \
+ ((size) << _IOC_SIZESHIFT))
+
+/* used to create numbers */
+#define _IO(type,nr) _IOC(_IOC_NONE,(type),(nr),0)
+#define _IOR(type,nr,size) _IOC(_IOC_READ,(type),(nr),sizeof(size))
+#define _IOW(type,nr,size) _IOC(_IOC_WRITE,(type),(nr),sizeof(size))
+#define _IOWR(type,nr,size) _IOC(_IOC_READ|_IOC_WRITE,(type),(nr),sizeof(size))
+
+/* used to decode ioctl numbers.. */
+#define _IOC_DIR(nr) (((nr) >> _IOC_DIRSHIFT) & _IOC_DIRMASK)
+#define _IOC_TYPE(nr) (((nr) >> _IOC_TYPESHIFT) & _IOC_TYPEMASK)
+#define _IOC_NR(nr) (((nr) >> _IOC_NRSHIFT) & _IOC_NRMASK)
+#define _IOC_SIZE(nr) (((nr) >> _IOC_SIZESHIFT) & _IOC_SIZEMASK)
+
+/* ...and for the drivers/sound files... */
+
+#define IOC_IN (_IOC_WRITE << _IOC_DIRSHIFT)
+#define IOC_OUT (_IOC_READ << _IOC_DIRSHIFT)
+#define IOC_INOUT ((_IOC_WRITE|_IOC_READ) << _IOC_DIRSHIFT)
+#define IOCSIZE_MASK (_IOC_SIZEMASK << _IOC_SIZESHIFT)
+#define IOCSIZE_SHIFT (_IOC_SIZESHIFT)
+
+#endif /* _ASMI386_IOCTL_H */
diff --git a/i386/i386at/gpl/linux/include/asm/irq.h b/i386/i386at/gpl/linux/include/asm/irq.h
new file mode 100644
index 00000000..8fd44b48
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/irq.h
@@ -0,0 +1,346 @@
+#ifndef _ASM_IRQ_H
+#define _ASM_IRQ_H
+
+/*
+ * linux/include/asm/irq.h
+ *
+ * (C) 1992, 1993 Linus Torvalds
+ *
+ * IRQ/IPI changes taken from work by Thomas Radke <tomsoft@informatik.tu-chemnitz.de>
+ */
+
+#include <linux/linkage.h>
+#include <asm/segment.h>
+
+#define NR_IRQS 16
+
+extern void disable_irq(unsigned int);
+extern void enable_irq(unsigned int);
+
+#define __STR(x) #x
+#define STR(x) __STR(x)
+
+#define SAVE_ALL \
+ "cld\n\t" \
+ "push %gs\n\t" \
+ "push %fs\n\t" \
+ "push %es\n\t" \
+ "push %ds\n\t" \
+ "pushl %eax\n\t" \
+ "pushl %ebp\n\t" \
+ "pushl %edi\n\t" \
+ "pushl %esi\n\t" \
+ "pushl %edx\n\t" \
+ "pushl %ecx\n\t" \
+ "pushl %ebx\n\t" \
+ "movl $" STR(KERNEL_DS) ",%edx\n\t" \
+ "mov %dx,%ds\n\t" \
+ "mov %dx,%es\n\t" \
+ "movl $" STR(USER_DS) ",%edx\n\t" \
+ "mov %dx,%fs\n\t" \
+ "movl $0,%edx\n\t" \
+ "movl %edx,%db7\n\t"
+
+/*
+ * SAVE_MOST/RESTORE_MOST is used for the faster version of IRQ handlers,
+ * installed by using the SA_INTERRUPT flag. These kinds of IRQ's don't
+ * call the routines that do signal handling etc on return, and can have
+ * more relaxed register-saving etc. They are also atomic, and are thus
+ * suited for small, fast interrupts like the serial lines or the harddisk
+ * drivers, which don't actually need signal handling etc.
+ *
+ * Also note that we actually save only those registers that are used in
+ * C subroutines (%eax, %edx and %ecx), so if you do something weird,
+ * you're on your own. The only segments that are saved (not counting the
+ * automatic stack and code segment handling) are %ds and %es, and they
+ * point to kernel space. No messing around with %fs here.
+ */
+#define SAVE_MOST \
+ "cld\n\t" \
+ "push %es\n\t" \
+ "push %ds\n\t" \
+ "pushl %eax\n\t" \
+ "pushl %edx\n\t" \
+ "pushl %ecx\n\t" \
+ "movl $" STR(KERNEL_DS) ",%edx\n\t" \
+ "mov %dx,%ds\n\t" \
+ "mov %dx,%es\n\t"
+
+#define RESTORE_MOST \
+ "popl %ecx\n\t" \
+ "popl %edx\n\t" \
+ "popl %eax\n\t" \
+ "pop %ds\n\t" \
+ "pop %es\n\t" \
+ "iret"
+
+/*
+ * The "inb" instructions are not needed, but seem to change the timings
+ * a bit - without them it seems that the harddisk driver won't work on
+ * all hardware. Arghh.
+ */
+#define ACK_FIRST(mask) \
+ "inb $0x21,%al\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\torb $" #mask ","SYMBOL_NAME_STR(cache_21)"\n\t" \
+ "movb "SYMBOL_NAME_STR(cache_21)",%al\n\t" \
+ "outb %al,$0x21\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\tmovb $0x20,%al\n\t" \
+ "outb %al,$0x20\n\t"
+
+#define ACK_SECOND(mask) \
+ "inb $0xA1,%al\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\torb $" #mask ","SYMBOL_NAME_STR(cache_A1)"\n\t" \
+ "movb "SYMBOL_NAME_STR(cache_A1)",%al\n\t" \
+ "outb %al,$0xA1\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\tmovb $0x20,%al\n\t" \
+ "outb %al,$0xA0\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\toutb %al,$0x20\n\t"
+
+#define UNBLK_FIRST(mask) \
+ "inb $0x21,%al\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\tandb $~(" #mask "),"SYMBOL_NAME_STR(cache_21)"\n\t" \
+ "movb "SYMBOL_NAME_STR(cache_21)",%al\n\t" \
+ "outb %al,$0x21\n\t"
+
+#define UNBLK_SECOND(mask) \
+ "inb $0xA1,%al\n\t" \
+ "jmp 1f\n" \
+ "1:\tjmp 1f\n" \
+ "1:\tandb $~(" #mask "),"SYMBOL_NAME_STR(cache_A1)"\n\t" \
+ "movb "SYMBOL_NAME_STR(cache_A1)",%al\n\t" \
+ "outb %al,$0xA1\n\t"
+
+#define IRQ_NAME2(nr) nr##_interrupt(void)
+#define IRQ_NAME(nr) IRQ_NAME2(IRQ##nr)
+#define FAST_IRQ_NAME(nr) IRQ_NAME2(fast_IRQ##nr)
+#define BAD_IRQ_NAME(nr) IRQ_NAME2(bad_IRQ##nr)
+
+#ifdef __SMP__
+
+#ifndef __SMP_PROF__
+#define SMP_PROF_INT_SPINS
+#define SMP_PROF_IPI_CNT
+#else
+#define SMP_PROF_INT_SPINS "incl "SYMBOL_NAME_STR(smp_spins)"(,%eax,4)\n\t"
+#define SMP_PROF_IPI_CNT "incl "SYMBOL_NAME_STR(ipi_count)"\n\t"
+#endif
+
+#define GET_PROCESSOR_ID \
+ "movl "SYMBOL_NAME_STR(apic_reg)", %edx\n\t" \
+ "movl 32(%edx), %eax\n\t" \
+ "shrl $24,%eax\n\t" \
+ "andb $0x0F,%al\n"
+
+#define ENTER_KERNEL \
+ "pushl %eax\n\t" \
+ "pushl %edx\n\t" \
+ "pushfl\n\t" \
+ "cli\n\t" \
+ GET_PROCESSOR_ID \
+ "1: " \
+ "lock\n\t" \
+ "btsl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
+ "jnc 3f\n\t" \
+ "cmpb "SYMBOL_NAME_STR(active_kernel_processor)", %al\n\t" \
+ "je 4f\n\t" \
+ "2: " \
+ SMP_PROF_INT_SPINS \
+ "btl %al, "SYMBOL_NAME_STR(smp_invalidate_needed)"\n\t" \
+ "jnc 5f\n\t" \
+ "lock\n\t" \
+ "btrl %al, "SYMBOL_NAME_STR(smp_invalidate_needed)"\n\t" \
+ "jnc 5f\n\t" \
+ "movl %cr3,%edx\n\t" \
+ "movl %edx,%cr3\n" \
+ "5: btl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
+ "jc 2b\n\t" \
+ "jmp 1b\n\t" \
+ "3: " \
+ "movb %al, "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
+ "4: " \
+ "incl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
+ "popfl\n\t" \
+ "popl %edx\n\t" \
+ "popl %eax\n\t"
+
+#define LEAVE_KERNEL \
+ "pushfl\n\t" \
+ "cli\n\t" \
+ "decl "SYMBOL_NAME_STR(kernel_counter)"\n\t" \
+ "jnz 1f\n\t" \
+ "movb $" STR (NO_PROC_ID) ", "SYMBOL_NAME_STR(active_kernel_processor)"\n\t" \
+ "lock\n\t" \
+ "btrl $0, "SYMBOL_NAME_STR(kernel_flag)"\n\t" \
+ "1: " \
+ "popfl\n\t"
+
+
+/*
+ * the syscall count inc is a gross hack because ret_from_syscall is used by both irq and
+ * syscall return paths (urghh).
+ */
+
+#define BUILD_IRQ(chip,nr,mask) \
+asmlinkage void IRQ_NAME(nr); \
+asmlinkage void FAST_IRQ_NAME(nr); \
+asmlinkage void BAD_IRQ_NAME(nr); \
+__asm__( \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
+ "pushl $-"#nr"-2\n\t" \
+ SAVE_ALL \
+ ENTER_KERNEL \
+ ACK_##chip(mask) \
+ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\
+ "sti\n\t" \
+ "movl %esp,%ebx\n\t" \
+ "pushl %ebx\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
+ "addl $8,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ "incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+ "jmp ret_from_sys_call\n" \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
+ SAVE_MOST \
+ ENTER_KERNEL \
+ ACK_##chip(mask) \
+ "incl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
+ "addl $4,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ LEAVE_KERNEL \
+ RESTORE_MOST \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \
+ SAVE_MOST \
+ ENTER_KERNEL \
+ ACK_##chip(mask) \
+ LEAVE_KERNEL \
+ RESTORE_MOST);
+
+
+/*
+ * Message pass must be a fast IRQ..
+ */
+
+#define BUILD_MSGIRQ(chip,nr,mask) \
+asmlinkage void IRQ_NAME(nr); \
+asmlinkage void FAST_IRQ_NAME(nr); \
+asmlinkage void BAD_IRQ_NAME(nr); \
+__asm__( \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
+ "pushl $-"#nr"-2\n\t" \
+ SAVE_ALL \
+ ENTER_KERNEL \
+ ACK_##chip(mask) \
+ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\
+ "sti\n\t" \
+ "movl %esp,%ebx\n\t" \
+ "pushl %ebx\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
+ "addl $8,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ "incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+ "jmp ret_from_sys_call\n" \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
+ SAVE_MOST \
+ ACK_##chip(mask) \
+ SMP_PROF_IPI_CNT \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
+ "addl $4,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ RESTORE_MOST \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \
+ SAVE_MOST \
+ ACK_##chip(mask) \
+ RESTORE_MOST);
+
+#define BUILD_RESCHEDIRQ(nr) \
+asmlinkage void IRQ_NAME(nr); \
+__asm__( \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
+ "pushl $-"#nr"-2\n\t" \
+ SAVE_ALL \
+ ENTER_KERNEL \
+ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\
+ "sti\n\t" \
+ "movl %esp,%ebx\n\t" \
+ "pushl %ebx\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(smp_reschedule_irq)"\n\t" \
+ "addl $8,%esp\n\t" \
+ "cli\n\t" \
+ "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ "incl "SYMBOL_NAME_STR(syscall_count)"\n\t" \
+ "jmp ret_from_sys_call\n");
+#else
+
+#define BUILD_IRQ(chip,nr,mask) \
+asmlinkage void IRQ_NAME(nr); \
+asmlinkage void FAST_IRQ_NAME(nr); \
+asmlinkage void BAD_IRQ_NAME(nr); \
+__asm__( \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(IRQ) #nr "_interrupt:\n\t" \
+ "pushl $-"#nr"-2\n\t" \
+ SAVE_ALL \
+ ACK_##chip(mask) \
+ "incl "SYMBOL_NAME_STR(intr_count)"\n\t"\
+ "sti\n\t" \
+ "movl %esp,%ebx\n\t" \
+ "pushl %ebx\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(do_IRQ)"\n\t" \
+ "addl $8,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ "jmp ret_from_sys_call\n" \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(fast_IRQ) #nr "_interrupt:\n\t" \
+ SAVE_MOST \
+ ACK_##chip(mask) \
+ "incl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ "pushl $" #nr "\n\t" \
+ "call "SYMBOL_NAME_STR(do_fast_IRQ)"\n\t" \
+ "addl $4,%esp\n\t" \
+ "cli\n\t" \
+ UNBLK_##chip(mask) \
+ "decl "SYMBOL_NAME_STR(intr_count)"\n\t" \
+ RESTORE_MOST \
+"\n"__ALIGN_STR"\n" \
+SYMBOL_NAME_STR(bad_IRQ) #nr "_interrupt:\n\t" \
+ SAVE_MOST \
+ ACK_##chip(mask) \
+ RESTORE_MOST);
+
+#endif
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/page.h b/i386/i386at/gpl/linux/include/asm/page.h
new file mode 100644
index 00000000..2bb6837e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/page.h
@@ -0,0 +1,64 @@
+#ifndef _I386_PAGE_H
+#define _I386_PAGE_H
+
+#ifndef MACH_INCLUDE
+/* PAGE_SHIFT determines the page size */
+#define PAGE_SHIFT 12
+#define PAGE_SIZE (1UL << PAGE_SHIFT)
+#define PAGE_MASK (~(PAGE_SIZE-1))
+#endif
+
+#ifdef __KERNEL__
+
+#define STRICT_MM_TYPECHECKS
+
+#ifdef STRICT_MM_TYPECHECKS
+/*
+ * These are used to make use of C type-checking..
+ */
+typedef struct { unsigned long pte; } pte_t;
+typedef struct { unsigned long pmd; } pmd_t;
+typedef struct { unsigned long pgd; } pgd_t;
+typedef struct { unsigned long pgprot; } pgprot_t;
+
+#define pte_val(x) ((x).pte)
+#define pmd_val(x) ((x).pmd)
+#define pgd_val(x) ((x).pgd)
+#define pgprot_val(x) ((x).pgprot)
+
+#define __pte(x) ((pte_t) { (x) } )
+#define __pmd(x) ((pmd_t) { (x) } )
+#define __pgd(x) ((pgd_t) { (x) } )
+#define __pgprot(x) ((pgprot_t) { (x) } )
+
+#else
+/*
+ * .. while these make it easier on the compiler
+ */
+typedef unsigned long pte_t;
+typedef unsigned long pmd_t;
+typedef unsigned long pgd_t;
+typedef unsigned long pgprot_t;
+
+#define pte_val(x) (x)
+#define pmd_val(x) (x)
+#define pgd_val(x) (x)
+#define pgprot_val(x) (x)
+
+#define __pte(x) (x)
+#define __pmd(x) (x)
+#define __pgd(x) (x)
+#define __pgprot(x) (x)
+
+#endif
+
+/* to align the pointer to the (next) page boundary */
+#define PAGE_ALIGN(addr) (((addr)+PAGE_SIZE-1)&PAGE_MASK)
+
+/* This handles the memory map.. */
+#define PAGE_OFFSET 0
+#define MAP_NR(addr) (((unsigned long)(addr)) >> PAGE_SHIFT)
+
+#endif /* __KERNEL__ */
+
+#endif /* _I386_PAGE_H */
diff --git a/i386/i386at/gpl/linux/include/asm/param.h b/i386/i386at/gpl/linux/include/asm/param.h
new file mode 100644
index 00000000..f821b864
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/param.h
@@ -0,0 +1,20 @@
+#ifndef _ASMi386_PARAM_H
+#define _ASMi386_PARAM_H
+
+#ifndef HZ
+#define HZ 100
+#endif
+
+#define EXEC_PAGESIZE 4096
+
+#ifndef NGROUPS
+#define NGROUPS 32
+#endif
+
+#ifndef NOGROUP
+#define NOGROUP (-1)
+#endif
+
+#define MAXHOSTNAMELEN 64 /* max length of hostname */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/processor.h b/i386/i386at/gpl/linux/include/asm/processor.h
new file mode 100644
index 00000000..dea7827b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/processor.h
@@ -0,0 +1,146 @@
+/*
+ * include/asm-i386/processor.h
+ *
+ * Copyright (C) 1994 Linus Torvalds
+ */
+
+#ifndef __ASM_I386_PROCESSOR_H
+#define __ASM_I386_PROCESSOR_H
+
+/*
+ * System setup and hardware bug flags..
+ */
+extern char hard_math;
+extern char x86; /* lower 4 bits */
+extern char x86_vendor_id[13];
+extern char x86_model; /* lower 4 bits */
+extern char x86_mask; /* lower 4 bits */
+extern int x86_capability; /* field of flags */
+extern int fdiv_bug;
+extern char ignore_irq13;
+extern char wp_works_ok; /* doesn't work on a 386 */
+extern char hlt_works_ok; /* problems on some 486Dx4's and old 386's */
+
+/*
+ * Bus types (default is ISA, but people can check others with these..)
+ * MCA_bus hardcoded to 0 for now.
+ */
+extern int EISA_bus;
+#define MCA_bus 0
+#define MCA_bus__is_a_macro /* for versions in ksyms.c */
+
+/*
+ * User space process size: 3GB. This is hardcoded into a few places,
+ * so don't change it unless you know what you are doing.
+ */
+#define TASK_SIZE (0xC0000000UL)
+
+/*
+ * Size of io_bitmap in longwords: 32 is ports 0-0x3ff.
+ */
+#define IO_BITMAP_SIZE 32
+
+struct i387_hard_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long st_space[20]; /* 8*10 bytes for each FP-reg = 80 bytes */
+};
+
+struct i387_soft_struct {
+ long cwd;
+ long swd;
+ long twd;
+ long fip;
+ long fcs;
+ long foo;
+ long fos;
+ long top;
+ struct fpu_reg regs[8]; /* 8*16 bytes for each FP-reg = 128 bytes */
+ unsigned char lookahead;
+ struct info *info;
+ unsigned long entry_eip;
+};
+
+union i387_union {
+ struct i387_hard_struct hard;
+ struct i387_soft_struct soft;
+};
+
+struct thread_struct {
+ unsigned short back_link,__blh;
+ unsigned long esp0;
+ unsigned short ss0,__ss0h;
+ unsigned long esp1;
+ unsigned short ss1,__ss1h;
+ unsigned long esp2;
+ unsigned short ss2,__ss2h;
+ unsigned long cr3;
+ unsigned long eip;
+ unsigned long eflags;
+ unsigned long eax,ecx,edx,ebx;
+ unsigned long esp;
+ unsigned long ebp;
+ unsigned long esi;
+ unsigned long edi;
+ unsigned short es, __esh;
+ unsigned short cs, __csh;
+ unsigned short ss, __ssh;
+ unsigned short ds, __dsh;
+ unsigned short fs, __fsh;
+ unsigned short gs, __gsh;
+ unsigned short ldt, __ldth;
+ unsigned short trace, bitmap;
+ unsigned long io_bitmap[IO_BITMAP_SIZE+1];
+ unsigned long tr;
+ unsigned long cr2, trap_no, error_code;
+/* floating point info */
+ union i387_union i387;
+/* virtual 86 mode info */
+ struct vm86_struct * vm86_info;
+ unsigned long screen_bitmap;
+ unsigned long v86flags, v86mask, v86mode;
+};
+
+#define INIT_MMAP { &init_mm, 0, 0x40000000, PAGE_SHARED, VM_READ | VM_WRITE | VM_EXEC }
+
+#define INIT_TSS { \
+ 0,0, \
+ sizeof(init_kernel_stack) + (long) &init_kernel_stack, \
+ KERNEL_DS, 0, \
+ 0,0,0,0,0,0, \
+ (long) &swapper_pg_dir, \
+ 0,0,0,0,0,0,0,0,0,0, \
+ USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0,USER_DS,0, \
+ _LDT(0),0, \
+ 0, 0x8000, \
+ {~0, }, /* ioperm */ \
+ _TSS(0), 0, 0,0, \
+ { { 0, }, }, /* 387 state */ \
+ NULL, 0, 0, 0, 0 /* vm86_info */ \
+}
+
+#define alloc_kernel_stack() get_free_page(GFP_KERNEL)
+#define free_kernel_stack(page) free_page((page))
+
+static inline void start_thread(struct pt_regs * regs, unsigned long eip, unsigned long esp)
+{
+ regs->cs = USER_CS;
+ regs->ds = regs->es = regs->ss = regs->fs = regs->gs = USER_DS;
+ regs->eip = eip;
+ regs->esp = esp;
+}
+
+/*
+ * Return saved PC of a blocked thread.
+ */
+extern inline unsigned long thread_saved_pc(struct thread_struct *t)
+{
+ return ((unsigned long *)t->esp)[3];
+}
+
+#endif /* __ASM_I386_PROCESSOR_H */
diff --git a/i386/i386at/gpl/linux/include/asm/ptrace.h b/i386/i386at/gpl/linux/include/asm/ptrace.h
new file mode 100644
index 00000000..8e4aa52f
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/ptrace.h
@@ -0,0 +1,52 @@
+#ifndef _I386_PTRACE_H
+#define _I386_PTRACE_H
+
+#define EBX 0
+#define ECX 1
+#define EDX 2
+#define ESI 3
+#define EDI 4
+#define EBP 5
+#define EAX 6
+#define DS 7
+#define ES 8
+#define FS 9
+#define GS 10
+#define ORIG_EAX 11
+#define EIP 12
+#define CS 13
+#define EFL 14
+#define UESP 15
+#define SS 16
+
+
+/* this struct defines the way the registers are stored on the
+ stack during a system call. */
+
+struct pt_regs {
+ long ebx;
+ long ecx;
+ long edx;
+ long esi;
+ long edi;
+ long ebp;
+ long eax;
+ unsigned short ds, __dsu;
+ unsigned short es, __esu;
+ unsigned short fs, __fsu;
+ unsigned short gs, __gsu;
+ long orig_eax;
+ long eip;
+ unsigned short cs, __csu;
+ long eflags;
+ long esp;
+ unsigned short ss, __ssu;
+};
+
+#ifdef __KERNEL__
+#define user_mode(regs) ((VM_MASK & (regs)->eflags) || (3 & (regs)->cs))
+#define instruction_pointer(regs) ((regs)->eip)
+extern void show_regs(struct pt_regs *);
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/resource.h b/i386/i386at/gpl/linux/include/asm/resource.h
new file mode 100644
index 00000000..83940d16
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/resource.h
@@ -0,0 +1,37 @@
+#ifndef _I386_RESOURCE_H
+#define _I386_RESOURCE_H
+
+/*
+ * Resource limits
+ */
+
+#define RLIMIT_CPU 0 /* CPU time in ms */
+#define RLIMIT_FSIZE 1 /* Maximum filesize */
+#define RLIMIT_DATA 2 /* max data size */
+#define RLIMIT_STACK 3 /* max stack size */
+#define RLIMIT_CORE 4 /* max core file size */
+#define RLIMIT_RSS 5 /* max resident set size */
+#define RLIMIT_NPROC 6 /* max number of processes */
+#define RLIMIT_NOFILE 7 /* max number of open files */
+#define RLIMIT_MEMLOCK 8 /* max locked-in-memory address space */
+
+#define RLIM_NLIMITS 9
+
+#ifdef __KERNEL__
+
+#define INIT_RLIMITS \
+{ \
+ { LONG_MAX, LONG_MAX }, \
+ { LONG_MAX, LONG_MAX }, \
+ { LONG_MAX, LONG_MAX }, \
+ { _STK_LIM, _STK_LIM }, \
+ { 0, LONG_MAX }, \
+ { LONG_MAX, LONG_MAX }, \
+ { MAX_TASKS_PER_USER, MAX_TASKS_PER_USER }, \
+ { NR_OPEN, NR_OPEN }, \
+ { LONG_MAX, LONG_MAX }, \
+}
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/segment.h b/i386/i386at/gpl/linux/include/asm/segment.h
new file mode 100644
index 00000000..7cfafa4b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/segment.h
@@ -0,0 +1,347 @@
+#ifndef _ASM_SEGMENT_H
+#define _ASM_SEGMENT_H
+
+#ifdef MACH
+#define KERNEL_CS 0x08
+#define KERNEL_DS 0x10
+
+#define USER_CS 0x17
+#define USER_DS 0x1f
+#else
+#define KERNEL_CS 0x10
+#define KERNEL_DS 0x18
+
+#define USER_CS 0x23
+#define USER_DS 0x2B
+#endif
+
+#ifndef __ASSEMBLY__
+
+/*
+ * Uh, these should become the main single-value transfer routines..
+ * They automatically use the right size if we just have the right
+ * pointer type..
+ */
+#define put_user(x,ptr) __put_user((unsigned long)(x),(ptr),sizeof(*(ptr)))
+#define get_user(ptr) ((__typeof__(*(ptr)))__get_user((ptr),sizeof(*(ptr))))
+
+/*
+ * This is a silly but good way to make sure that
+ * the __put_user function is indeed always optimized,
+ * and that we use the correct sizes..
+ */
+extern int bad_user_access_length(void);
+
+/*
+ * dummy pointer type structure.. gcc won't try to do something strange
+ * this way..
+ */
+struct __segment_dummy { unsigned long a[100]; };
+#define __sd(x) ((struct __segment_dummy *) (x))
+#define __const_sd(x) ((const struct __segment_dummy *) (x))
+
+static inline void __put_user(unsigned long x, void * y, int size)
+{
+ switch (size) {
+ case 1:
+ __asm__ ("movb %b1,%%fs:%0"
+ :"=m" (*__sd(y))
+ :"iq" ((unsigned char) x), "m" (*__sd(y)));
+ break;
+ case 2:
+ __asm__ ("movw %w1,%%fs:%0"
+ :"=m" (*__sd(y))
+ :"ir" ((unsigned short) x), "m" (*__sd(y)));
+ break;
+ case 4:
+ __asm__ ("movl %1,%%fs:%0"
+ :"=m" (*__sd(y))
+ :"ir" (x), "m" (*__sd(y)));
+ break;
+ default:
+ bad_user_access_length();
+ }
+}
+
+static inline unsigned long __get_user(const void * y, int size)
+{
+ unsigned long result;
+
+ switch (size) {
+ case 1:
+ __asm__ ("movb %%fs:%1,%b0"
+ :"=q" (result)
+ :"m" (*__const_sd(y)));
+ return (unsigned char) result;
+ case 2:
+ __asm__ ("movw %%fs:%1,%w0"
+ :"=r" (result)
+ :"m" (*__const_sd(y)));
+ return (unsigned short) result;
+ case 4:
+ __asm__ ("movl %%fs:%1,%0"
+ :"=r" (result)
+ :"m" (*__const_sd(y)));
+ return result;
+ default:
+ return bad_user_access_length();
+ }
+}
+
+static inline void __generic_memcpy_tofs(void * to, const void * from, unsigned long n)
+{
+ __asm__ volatile
+ (" cld
+ push %%es
+ movw %%fs,%%cx
+ movw %%cx,%%es
+ cmpl $3,%0
+ jbe 1f
+ movl %%edi,%%ecx
+ negl %%ecx
+ andl $3,%%ecx
+ subl %%ecx,%0
+ rep; movsb
+ movl %0,%%ecx
+ shrl $2,%%ecx
+ rep; movsl
+ andl $3,%0
+ 1: movl %0,%%ecx
+ rep; movsb
+ pop %%es"
+ :"=abd" (n)
+ :"0" (n),"D" ((long) to),"S" ((long) from)
+ :"cx","di","si");
+}
+
+static inline void __constant_memcpy_tofs(void * to, const void * from, unsigned long n)
+{
+ switch (n) {
+ case 0:
+ return;
+ case 1:
+ __put_user(*(const char *) from, (char *) to, 1);
+ return;
+ case 2:
+ __put_user(*(const short *) from, (short *) to, 2);
+ return;
+ case 3:
+ __put_user(*(const short *) from, (short *) to, 2);
+ __put_user(*(2+(const char *) from), 2+(char *) to, 1);
+ return;
+ case 4:
+ __put_user(*(const int *) from, (int *) to, 4);
+ return;
+ case 8:
+ __put_user(*(const int *) from, (int *) to, 4);
+ __put_user(*(1+(const int *) from), 1+(int *) to, 4);
+ return;
+ case 12:
+ __put_user(*(const int *) from, (int *) to, 4);
+ __put_user(*(1+(const int *) from), 1+(int *) to, 4);
+ __put_user(*(2+(const int *) from), 2+(int *) to, 4);
+ return;
+ case 16:
+ __put_user(*(const int *) from, (int *) to, 4);
+ __put_user(*(1+(const int *) from), 1+(int *) to, 4);
+ __put_user(*(2+(const int *) from), 2+(int *) to, 4);
+ __put_user(*(3+(const int *) from), 3+(int *) to, 4);
+ return;
+ }
+#define COMMON(x) \
+__asm__("cld\n\t" \
+ "push %%es\n\t" \
+ "push %%fs\n\t" \
+ "pop %%es\n\t" \
+ "rep ; movsl\n\t" \
+ x \
+ "pop %%es" \
+ : /* no outputs */ \
+ :"c" (n/4),"D" ((long) to),"S" ((long) from) \
+ :"cx","di","si")
+
+ switch (n % 4) {
+ case 0:
+ COMMON("");
+ return;
+ case 1:
+ COMMON("movsb\n\t");
+ return;
+ case 2:
+ COMMON("movsw\n\t");
+ return;
+ case 3:
+ COMMON("movsw\n\tmovsb\n\t");
+ return;
+ }
+#undef COMMON
+}
+
+static inline void __generic_memcpy_fromfs(void * to, const void * from, unsigned long n)
+{
+ __asm__ volatile
+ (" cld
+ cmpl $3,%0
+ jbe 1f
+ movl %%edi,%%ecx
+ negl %%ecx
+ andl $3,%%ecx
+ subl %%ecx,%0
+ fs; rep; movsb
+ movl %0,%%ecx
+ shrl $2,%%ecx
+ fs; rep; movsl
+ andl $3,%0
+ 1: movl %0,%%ecx
+ fs; rep; movsb"
+ :"=abd" (n)
+ :"0" (n),"D" ((long) to),"S" ((long) from)
+ :"cx","di","si", "memory");
+}
+
+static inline void __constant_memcpy_fromfs(void * to, const void * from, unsigned long n)
+{
+ switch (n) {
+ case 0:
+ return;
+ case 1:
+ *(char *)to = __get_user((const char *) from, 1);
+ return;
+ case 2:
+ *(short *)to = __get_user((const short *) from, 2);
+ return;
+ case 3:
+ *(short *) to = __get_user((const short *) from, 2);
+ *((char *) to + 2) = __get_user(2+(const char *) from, 1);
+ return;
+ case 4:
+ *(int *) to = __get_user((const int *) from, 4);
+ return;
+ case 8:
+ *(int *) to = __get_user((const int *) from, 4);
+ *(1+(int *) to) = __get_user(1+(const int *) from, 4);
+ return;
+ case 12:
+ *(int *) to = __get_user((const int *) from, 4);
+ *(1+(int *) to) = __get_user(1+(const int *) from, 4);
+ *(2+(int *) to) = __get_user(2+(const int *) from, 4);
+ return;
+ case 16:
+ *(int *) to = __get_user((const int *) from, 4);
+ *(1+(int *) to) = __get_user(1+(const int *) from, 4);
+ *(2+(int *) to) = __get_user(2+(const int *) from, 4);
+ *(3+(int *) to) = __get_user(3+(const int *) from, 4);
+ return;
+ }
+#define COMMON(x) \
+__asm__("cld\n\t" \
+ "rep ; fs ; movsl\n\t" \
+ x \
+ : /* no outputs */ \
+ :"c" (n/4),"D" ((long) to),"S" ((long) from) \
+ :"cx","di","si","memory")
+
+ switch (n % 4) {
+ case 0:
+ COMMON("");
+ return;
+ case 1:
+ COMMON("fs ; movsb");
+ return;
+ case 2:
+ COMMON("fs ; movsw");
+ return;
+ case 3:
+ COMMON("fs ; movsw\n\tfs ; movsb");
+ return;
+ }
+#undef COMMON
+}
+
+#define memcpy_fromfs(to, from, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy_fromfs((to),(from),(n)) : \
+ __generic_memcpy_fromfs((to),(from),(n)))
+
+#define memcpy_tofs(to, from, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy_tofs((to),(from),(n)) : \
+ __generic_memcpy_tofs((to),(from),(n)))
+
+/*
+ * These are deprecated..
+ *
+ * Use "put_user()" and "get_user()" with the proper pointer types instead.
+ */
+
+#define get_fs_byte(addr) __get_user((const unsigned char *)(addr),1)
+#define get_fs_word(addr) __get_user((const unsigned short *)(addr),2)
+#define get_fs_long(addr) __get_user((const unsigned int *)(addr),4)
+
+#define put_fs_byte(x,addr) __put_user((x),(unsigned char *)(addr),1)
+#define put_fs_word(x,addr) __put_user((x),(unsigned short *)(addr),2)
+#define put_fs_long(x,addr) __put_user((x),(unsigned int *)(addr),4)
+
+#ifdef WE_REALLY_WANT_TO_USE_A_BROKEN_INTERFACE
+
+static inline unsigned short get_user_word(const short *addr)
+{
+ return __get_user(addr, 2);
+}
+
+static inline unsigned char get_user_byte(const char * addr)
+{
+ return __get_user(addr,1);
+}
+
+static inline unsigned long get_user_long(const int *addr)
+{
+ return __get_user(addr, 4);
+}
+
+static inline void put_user_byte(char val,char *addr)
+{
+ __put_user(val, addr, 1);
+}
+
+static inline void put_user_word(short val,short * addr)
+{
+ __put_user(val, addr, 2);
+}
+
+static inline void put_user_long(unsigned long val,int * addr)
+{
+ __put_user(val, addr, 4);
+}
+
+#endif
+
+/*
+ * Someone who knows GNU asm better than I should double check the following.
+ * It seems to work, but I don't know if I'm doing something subtly wrong.
+ * --- TYT, 11/24/91
+ * [ nothing wrong here, Linus: I just changed the ax to be any reg ]
+ */
+
+static inline unsigned long get_fs(void)
+{
+ unsigned long _v;
+ __asm__("mov %%fs,%w0":"=r" (_v):"0" (0));
+ return _v;
+}
+
+static inline unsigned long get_ds(void)
+{
+ unsigned long _v;
+ __asm__("mov %%ds,%w0":"=r" (_v):"0" (0));
+ return _v;
+}
+
+static inline void set_fs(unsigned long val)
+{
+ __asm__ __volatile__("mov %w0,%%fs": /* no output */ :"r" (val));
+}
+
+#endif /* __ASSEMBLY__ */
+
+#endif /* _ASM_SEGMENT_H */
diff --git a/i386/i386at/gpl/linux/include/asm/sigcontext.h b/i386/i386at/gpl/linux/include/asm/sigcontext.h
new file mode 100644
index 00000000..5b84694f
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/sigcontext.h
@@ -0,0 +1,29 @@
+#ifndef _ASMi386_SIGCONTEXT_H
+#define _ASMi386_SIGCONTEXT_H
+
+struct sigcontext_struct {
+ unsigned short gs, __gsh;
+ unsigned short fs, __fsh;
+ unsigned short es, __esh;
+ unsigned short ds, __dsh;
+ unsigned long edi;
+ unsigned long esi;
+ unsigned long ebp;
+ unsigned long esp;
+ unsigned long ebx;
+ unsigned long edx;
+ unsigned long ecx;
+ unsigned long eax;
+ unsigned long trapno;
+ unsigned long err;
+ unsigned long eip;
+ unsigned short cs, __csh;
+ unsigned long eflags;
+ unsigned long esp_at_signal;
+ unsigned short ss, __ssh;
+ unsigned long i387;
+ unsigned long oldmask;
+ unsigned long cr2;
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/signal.h b/i386/i386at/gpl/linux/include/asm/signal.h
new file mode 100644
index 00000000..b37613fe
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/signal.h
@@ -0,0 +1,95 @@
+#ifndef _ASMi386_SIGNAL_H
+#define _ASMi386_SIGNAL_H
+
+typedef unsigned long sigset_t; /* at least 32 bits */
+
+#define _NSIG 32
+#define NSIG _NSIG
+
+#define SIGHUP 1
+#define SIGINT 2
+#define SIGQUIT 3
+#define SIGILL 4
+#define SIGTRAP 5
+#define SIGABRT 6
+#define SIGIOT 6
+#define SIGBUS 7
+#define SIGFPE 8
+#define SIGKILL 9
+#define SIGUSR1 10
+#define SIGSEGV 11
+#define SIGUSR2 12
+#define SIGPIPE 13
+#define SIGALRM 14
+#define SIGTERM 15
+#define SIGSTKFLT 16
+#define SIGCHLD 17
+#define SIGCONT 18
+#define SIGSTOP 19
+#define SIGTSTP 20
+#define SIGTTIN 21
+#define SIGTTOU 22
+#define SIGURG 23
+#define SIGXCPU 24
+#define SIGXFSZ 25
+#define SIGVTALRM 26
+#define SIGPROF 27
+#define SIGWINCH 28
+#define SIGIO 29
+#define SIGPOLL SIGIO
+/*
+#define SIGLOST 29
+*/
+#define SIGPWR 30
+#define SIGUNUSED 31
+
+/*
+ * sa_flags values: SA_STACK is not currently supported, but will allow the
+ * usage of signal stacks by using the (now obsolete) sa_restorer field in
+ * the sigaction structure as a stack pointer. This is now possible due to
+ * the changes in signal handling. LBT 010493.
+ * SA_INTERRUPT is a no-op, but left due to historical reasons. Use the
+ * SA_RESTART flag to get restarting signals (which were the default long ago)
+ */
+#define SA_NOCLDSTOP 1
+#define SA_STACK 0x08000000
+#define SA_RESTART 0x10000000
+#define SA_INTERRUPT 0x20000000
+#define SA_NOMASK 0x40000000
+#define SA_ONESHOT 0x80000000
+
+#ifdef __KERNEL__
+/*
+ * These values of sa_flags are used only by the kernel as part of the
+ * irq handling routines.
+ *
+ * SA_INTERRUPT is also used by the irq handling routines.
+ */
+#define SA_PROBE SA_ONESHOT
+#define SA_SAMPLE_RANDOM SA_RESTART
+#endif
+
+
+#define SIG_BLOCK 0 /* for blocking signals */
+#define SIG_UNBLOCK 1 /* for unblocking signals */
+#define SIG_SETMASK 2 /* for setting the signal mask */
+
+/* Type of a signal handler. */
+typedef void (*__sighandler_t)(int);
+
+#define SIG_DFL ((__sighandler_t)0) /* default signal handling */
+#define SIG_IGN ((__sighandler_t)1) /* ignore signal */
+#define SIG_ERR ((__sighandler_t)-1) /* error return from signal */
+
+struct sigaction {
+ __sighandler_t sa_handler;
+ sigset_t sa_mask;
+ unsigned long sa_flags;
+ void (*sa_restorer)(void);
+};
+
+#ifdef __KERNEL__
+#include <asm/sigcontext.h>
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/socket.h b/i386/i386at/gpl/linux/include/asm/socket.h
new file mode 100644
index 00000000..dc923006
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/socket.h
@@ -0,0 +1,31 @@
+#ifndef _ASM_SOCKET_H
+#define _ASM_SOCKET_H
+
+/* Socket-level I/O control calls. */
+#define FIOSETOWN 0x8901
+#define SIOCSPGRP 0x8902
+#define FIOGETOWN 0x8903
+#define SIOCGPGRP 0x8904
+#define SIOCATMARK 0x8905
+#define SIOCGSTAMP 0x8906 /* Get stamp */
+
+/* For setsockoptions(2) */
+#define SOL_SOCKET 1
+
+#define SO_DEBUG 1
+#define SO_REUSEADDR 2
+#define SO_TYPE 3
+#define SO_ERROR 4
+#define SO_DONTROUTE 5
+#define SO_BROADCAST 6
+#define SO_SNDBUF 7
+#define SO_RCVBUF 8
+#define SO_KEEPALIVE 9
+#define SO_OOBINLINE 10
+#define SO_NO_CHECK 11
+#define SO_PRIORITY 12
+#define SO_LINGER 13
+#define SO_BSDCOMPAT 14
+/* To add :#define SO_REUSEPORT 15 */
+
+#endif /* _ASM_SOCKET_H */
diff --git a/i386/i386at/gpl/linux/include/asm/stat.h b/i386/i386at/gpl/linux/include/asm/stat.h
new file mode 100644
index 00000000..b4c64869
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/stat.h
@@ -0,0 +1,41 @@
+#ifndef _I386_STAT_H
+#define _I386_STAT_H
+
+struct old_stat {
+ unsigned short st_dev;
+ unsigned short st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ unsigned long st_size;
+ unsigned long st_atime;
+ unsigned long st_mtime;
+ unsigned long st_ctime;
+};
+
+struct new_stat {
+ unsigned short st_dev;
+ unsigned short __pad1;
+ unsigned long st_ino;
+ unsigned short st_mode;
+ unsigned short st_nlink;
+ unsigned short st_uid;
+ unsigned short st_gid;
+ unsigned short st_rdev;
+ unsigned short __pad2;
+ unsigned long st_size;
+ unsigned long st_blksize;
+ unsigned long st_blocks;
+ unsigned long st_atime;
+ unsigned long __unused1;
+ unsigned long st_mtime;
+ unsigned long __unused2;
+ unsigned long st_ctime;
+ unsigned long __unused3;
+ unsigned long __unused4;
+ unsigned long __unused5;
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/statfs.h b/i386/i386at/gpl/linux/include/asm/statfs.h
new file mode 100644
index 00000000..6efb7411
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/statfs.h
@@ -0,0 +1,21 @@
+#ifndef _I386_STATFS_H
+#define _I386_STATFS_H
+
+typedef struct {
+ long val[2];
+} fsid_t;
+
+struct statfs {
+ long f_type;
+ long f_bsize;
+ long f_blocks;
+ long f_bfree;
+ long f_bavail;
+ long f_files;
+ long f_ffree;
+ fsid_t f_fsid;
+ long f_namelen;
+ long f_spare[6];
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/string.h b/i386/i386at/gpl/linux/include/asm/string.h
new file mode 100644
index 00000000..d93a2a77
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/string.h
@@ -0,0 +1,593 @@
+#ifndef _I386_STRING_H_
+#define _I386_STRING_H_
+
+/*
+ * This string-include defines all string functions as inline
+ * functions. Use gcc. It also assumes ds=es=data space, this should be
+ * normal. Most of the string-functions are rather heavily hand-optimized,
+ * see especially strtok,strstr,str[c]spn. They should work, but are not
+ * very easy to understand. Everything is done entirely within the register
+ * set, making the functions fast and clean. String instructions have been
+ * used through-out, making for "slightly" unclear code :-)
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#define __HAVE_ARCH_STRCPY
+extern inline char * strcpy(char * dest,const char *src)
+{
+__asm__ __volatile__(
+ "cld\n"
+ "1:\tlodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b"
+ : /* no output */
+ :"S" (src),"D" (dest):"si","di","ax","memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCPY
+extern inline char * strncpy(char * dest,const char *src,size_t count)
+{
+__asm__ __volatile__(
+ "cld\n"
+ "1:\tdecl %2\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n\t"
+ "rep\n\t"
+ "stosb\n"
+ "2:"
+ : /* no output */
+ :"S" (src),"D" (dest),"c" (count):"si","di","ax","cx","memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCAT
+extern inline char * strcat(char * dest,const char * src)
+{
+__asm__ __volatile__(
+ "cld\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "decl %1\n"
+ "1:\tlodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b"
+ : /* no output */
+ :"S" (src),"D" (dest),"a" (0),"c" (0xffffffff):"si","di","ax","cx");
+return dest;
+}
+
+#define __HAVE_ARCH_STRNCAT
+extern inline char * strncat(char * dest,const char * src,size_t count)
+{
+__asm__ __volatile__(
+ "cld\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "decl %1\n\t"
+ "movl %4,%3\n"
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "stosb\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %2,%2\n\t"
+ "stosb"
+ : /* no output */
+ :"S" (src),"D" (dest),"a" (0),"c" (0xffffffff),"g" (count)
+ :"si","di","ax","cx","memory");
+return dest;
+}
+
+#define __HAVE_ARCH_STRCMP
+extern inline int strcmp(const char * cs,const char * ct)
+{
+register int __res;
+__asm__ __volatile__(
+ "cld\n"
+ "1:\tlodsb\n\t"
+ "scasb\n\t"
+ "jne 2f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n\t"
+ "xorl %%eax,%%eax\n\t"
+ "jmp 3f\n"
+ "2:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%eax\n"
+ "3:"
+ :"=a" (__res):"S" (cs),"D" (ct):"si","di");
+return __res;
+}
+
+#define __HAVE_ARCH_STRNCMP
+extern inline int strncmp(const char * cs,const char * ct,size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+ "cld\n"
+ "1:\tdecl %3\n\t"
+ "js 2f\n\t"
+ "lodsb\n\t"
+ "scasb\n\t"
+ "jne 3f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n"
+ "2:\txorl %%eax,%%eax\n\t"
+ "jmp 4f\n"
+ "3:\tsbbl %%eax,%%eax\n\t"
+ "orb $1,%%al\n"
+ "4:"
+ :"=a" (__res):"S" (cs),"D" (ct),"c" (count):"si","di","cx");
+return __res;
+}
+
+#define __HAVE_ARCH_STRCHR
+extern inline char * strchr(const char * s, int c)
+{
+register char * __res;
+__asm__ __volatile__(
+ "cld\n\t"
+ "movb %%al,%%ah\n"
+ "1:\tlodsb\n\t"
+ "cmpb %%ah,%%al\n\t"
+ "je 2f\n\t"
+ "testb %%al,%%al\n\t"
+ "jne 1b\n\t"
+ "movl $1,%1\n"
+ "2:\tmovl %1,%0\n\t"
+ "decl %0"
+ :"=a" (__res):"S" (s),"0" (c):"si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRRCHR
+extern inline char * strrchr(const char * s, int c)
+{
+register char * __res;
+__asm__ __volatile__(
+ "cld\n\t"
+ "movb %%al,%%ah\n"
+ "1:\tlodsb\n\t"
+ "cmpb %%ah,%%al\n\t"
+ "jne 2f\n\t"
+ "leal -1(%%esi),%0\n"
+ "2:\ttestb %%al,%%al\n\t"
+ "jne 1b"
+ :"=d" (__res):"0" (0),"S" (s),"a" (c):"ax","si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRSPN
+extern inline size_t strspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+ "cld\n\t"
+ "movl %4,%%edi\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %%ecx\n\t"
+ "decl %%ecx\n\t"
+ "movl %%ecx,%%edx\n"
+ "1:\tlodsb\n\t"
+ "testb %%al,%%al\n\t"
+ "je 2f\n\t"
+ "movl %4,%%edi\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "je 1b\n"
+ "2:\tdecl %0"
+ :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+ :"ax","cx","dx","di");
+return __res-cs;
+}
+
+#define __HAVE_ARCH_STRCSPN
+extern inline size_t strcspn(const char * cs, const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+ "cld\n\t"
+ "movl %4,%%edi\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %%ecx\n\t"
+ "decl %%ecx\n\t"
+ "movl %%ecx,%%edx\n"
+ "1:\tlodsb\n\t"
+ "testb %%al,%%al\n\t"
+ "je 2f\n\t"
+ "movl %4,%%edi\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "jne 1b\n"
+ "2:\tdecl %0"
+ :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+ :"ax","cx","dx","di");
+return __res-cs;
+}
+
+#define __HAVE_ARCH_STRPBRK
+extern inline char * strpbrk(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+ "cld\n\t"
+ "movl %4,%%edi\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %%ecx\n\t"
+ "decl %%ecx\n\t"
+ "movl %%ecx,%%edx\n"
+ "1:\tlodsb\n\t"
+ "testb %%al,%%al\n\t"
+ "je 2f\n\t"
+ "movl %4,%%edi\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "jne 1b\n\t"
+ "decl %0\n\t"
+ "jmp 3f\n"
+ "2:\txorl %0,%0\n"
+ "3:"
+ :"=S" (__res):"a" (0),"c" (0xffffffff),"0" (cs),"g" (ct)
+ :"ax","cx","dx","di");
+return __res;
+}
+
+#define __HAVE_ARCH_STRSTR
+extern inline char * strstr(const char * cs,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+ "cld\n\t" \
+ "movl %4,%%edi\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %%ecx\n\t"
+ "decl %%ecx\n\t" /* NOTE! This also sets Z if searchstring='' */
+ "movl %%ecx,%%edx\n"
+ "1:\tmovl %4,%%edi\n\t"
+ "movl %%esi,%%eax\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "repe\n\t"
+ "cmpsb\n\t"
+ "je 2f\n\t" /* also works for empty string, see above */
+ "xchgl %%eax,%%esi\n\t"
+ "incl %%esi\n\t"
+ "cmpb $0,-1(%%eax)\n\t"
+ "jne 1b\n\t"
+ "xorl %%eax,%%eax\n\t"
+ "2:"
+ :"=a" (__res):"0" (0),"c" (0xffffffff),"S" (cs),"g" (ct)
+ :"cx","dx","di","si");
+return __res;
+}
+
+#define __HAVE_ARCH_STRLEN
+extern inline size_t strlen(const char * s)
+{
+register int __res;
+__asm__ __volatile__(
+ "cld\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %0\n\t"
+ "decl %0"
+ :"=c" (__res):"D" (s),"a" (0),"0" (0xffffffff):"di");
+return __res;
+}
+
+#define __HAVE_ARCH_STRTOK
+extern inline char * strtok(char * s,const char * ct)
+{
+register char * __res;
+__asm__ __volatile__(
+ "testl %1,%1\n\t"
+ "jne 1f\n\t"
+ "testl %0,%0\n\t"
+ "je 8f\n\t"
+ "movl %0,%1\n"
+ "1:\txorl %0,%0\n\t"
+ "movl $-1,%%ecx\n\t"
+ "xorl %%eax,%%eax\n\t"
+ "cld\n\t"
+ "movl %4,%%edi\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "notl %%ecx\n\t"
+ "decl %%ecx\n\t"
+ "je 7f\n\t" /* empty delimiter-string */
+ "movl %%ecx,%%edx\n"
+ "2:\tlodsb\n\t"
+ "testb %%al,%%al\n\t"
+ "je 7f\n\t"
+ "movl %4,%%edi\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "je 2b\n\t"
+ "decl %1\n\t"
+ "cmpb $0,(%1)\n\t"
+ "je 7f\n\t"
+ "movl %1,%0\n"
+ "3:\tlodsb\n\t"
+ "testb %%al,%%al\n\t"
+ "je 5f\n\t"
+ "movl %4,%%edi\n\t"
+ "movl %%edx,%%ecx\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "jne 3b\n\t"
+ "decl %1\n\t"
+ "cmpb $0,(%1)\n\t"
+ "je 5f\n\t"
+ "movb $0,(%1)\n\t"
+ "incl %1\n\t"
+ "jmp 6f\n"
+ "5:\txorl %1,%1\n"
+ "6:\tcmpb $0,(%0)\n\t"
+ "jne 7f\n\t"
+ "xorl %0,%0\n"
+ "7:\ttestl %0,%0\n\t"
+ "jne 8f\n\t"
+ "movl %0,%1\n"
+ "8:"
+ :"=b" (__res),"=S" (___strtok)
+ :"0" (___strtok),"1" (s),"g" (ct)
+ :"ax","cx","dx","di","memory");
+return __res;
+}
+
+extern inline void * __memcpy(void * to, const void * from, size_t n)
+{
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep ; movsl\n\t"
+ "testb $2,%b1\n\t"
+ "je 1f\n\t"
+ "movsw\n"
+ "1:\ttestb $1,%b1\n\t"
+ "je 2f\n\t"
+ "movsb\n"
+ "2:"
+ : /* no output */
+ :"c" (n/4), "q" (n),"D" ((long) to),"S" ((long) from)
+ : "cx","di","si","memory");
+return (to);
+}
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as the count is constant.
+ */
+extern inline void * __constant_memcpy(void * to, const void * from, size_t n)
+{
+ switch (n) {
+ case 0:
+ return to;
+ case 1:
+ *(unsigned char *)to = *(const unsigned char *)from;
+ return to;
+ case 2:
+ *(unsigned short *)to = *(const unsigned short *)from;
+ return to;
+ case 3:
+ *(unsigned short *)to = *(const unsigned short *)from;
+ *(2+(unsigned char *)to) = *(2+(const unsigned char *)from);
+ return to;
+ case 4:
+ *(unsigned long *)to = *(const unsigned long *)from;
+ return to;
+ }
+#define COMMON(x) \
+__asm__("cld\n\t" \
+ "rep ; movsl" \
+ x \
+ : /* no outputs */ \
+ : "c" (n/4),"D" ((long) to),"S" ((long) from) \
+ : "cx","di","si","memory");
+
+ switch (n % 4) {
+ case 0: COMMON(""); return to;
+ case 1: COMMON("\n\tmovsb"); return to;
+ case 2: COMMON("\n\tmovsw"); return to;
+ case 3: COMMON("\n\tmovsw\n\tmovsb"); return to;
+ }
+#undef COMMON
+}
+
+#define __HAVE_ARCH_MEMCPY
+#define memcpy(t, f, n) \
+(__builtin_constant_p(n) ? \
+ __constant_memcpy((t),(f),(n)) : \
+ __memcpy((t),(f),(n)))
+
+#define __HAVE_ARCH_MEMMOVE
+extern inline void * memmove(void * dest,const void * src, size_t n)
+{
+if (dest<src)
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "movsb"
+ : /* no output */
+ :"c" (n),"S" (src),"D" (dest)
+ :"cx","si","di");
+else
+__asm__ __volatile__(
+ "std\n\t"
+ "rep\n\t"
+ "movsb\n\t"
+ "cld"
+ : /* no output */
+ :"c" (n),
+ "S" (n-1+(const char *)src),
+ "D" (n-1+(char *)dest)
+ :"cx","si","di","memory");
+return dest;
+}
+
+#define memcmp __builtin_memcmp
+
+#define __HAVE_ARCH_MEMCHR
+extern inline void * memchr(const void * cs,int c,size_t count)
+{
+register void * __res;
+if (!count)
+ return NULL;
+__asm__ __volatile__(
+ "cld\n\t"
+ "repne\n\t"
+ "scasb\n\t"
+ "je 1f\n\t"
+ "movl $1,%0\n"
+ "1:\tdecl %0"
+ :"=D" (__res):"a" (c),"D" (cs),"c" (count)
+ :"cx");
+return __res;
+}
+
+extern inline void * __memset_generic(void * s, char c,size_t count)
+{
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep\n\t"
+ "stosb"
+ : /* no output */
+ :"a" (c),"D" (s),"c" (count)
+ :"cx","di","memory");
+return s;
+}
+
+/* we might want to write optimized versions of these later */
+#define __constant_count_memset(s,c,count) __memset_generic((s),(c),(count))
+
+/*
+ * memset(x,0,y) is a reasonably common thing to do, so we want to fill
+ * things 32 bits at a time even when we don't know the size of the
+ * area at compile-time..
+ */
+extern inline void * __constant_c_memset(void * s, unsigned long c, size_t count)
+{
+__asm__ __volatile__(
+ "cld\n\t"
+ "rep ; stosl\n\t"
+ "testb $2,%b1\n\t"
+ "je 1f\n\t"
+ "stosw\n"
+ "1:\ttestb $1,%b1\n\t"
+ "je 2f\n\t"
+ "stosb\n"
+ "2:"
+ : /* no output */
+ :"a" (c), "q" (count), "c" (count/4), "D" ((long) s)
+ :"cx","di","memory");
+return (s);
+}
+
+/* Added by Gertjan van Wingerde to make minix and sysv module work */
+#define __HAVE_ARCH_STRNLEN
+extern inline size_t strnlen(const char * s, size_t count)
+{
+register int __res;
+__asm__ __volatile__(
+ "movl %1,%0\n\t"
+ "jmp 2f\n"
+ "1:\tcmpb $0,(%0)\n\t"
+ "je 3f\n\t"
+ "incl %0\n"
+ "2:\tdecl %2\n\t"
+ "cmpl $-1,%2\n\t"
+ "jne 1b\n"
+ "3:\tsubl %1,%0"
+ :"=a" (__res):"c" (s),"d" (count));
+return __res;
+}
+/* end of additional stuff */
+
+/*
+ * This looks horribly ugly, but the compiler can optimize it totally,
+ * as we by now know that both pattern and count is constant..
+ */
+extern inline void * __constant_c_and_count_memset(void * s, unsigned long pattern, size_t count)
+{
+ switch (count) {
+ case 0:
+ return s;
+ case 1:
+ *(unsigned char *)s = pattern;
+ return s;
+ case 2:
+ *(unsigned short *)s = pattern;
+ return s;
+ case 3:
+ *(unsigned short *)s = pattern;
+ *(2+(unsigned char *)s) = pattern;
+ return s;
+ case 4:
+ *(unsigned long *)s = pattern;
+ return s;
+ }
+#define COMMON(x) \
+__asm__("cld\n\t" \
+ "rep ; stosl" \
+ x \
+ : /* no outputs */ \
+ : "a" (pattern),"c" (count/4),"D" ((long) s) \
+ : "cx","di","memory")
+
+ switch (count % 4) {
+ case 0: COMMON(""); return s;
+ case 1: COMMON("\n\tstosb"); return s;
+ case 2: COMMON("\n\tstosw"); return s;
+ case 3: COMMON("\n\tstosw\n\tstosb"); return s;
+ }
+#undef COMMON
+}
+
+#define __constant_c_x_memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_c_and_count_memset((s),(c),(count)) : \
+ __constant_c_memset((s),(c),(count)))
+
+#define __memset(s, c, count) \
+(__builtin_constant_p(count) ? \
+ __constant_count_memset((s),(c),(count)) : \
+ __memset_generic((s),(c),(count)))
+
+#define __HAVE_ARCH_MEMSET
+#define memset(s, c, count) \
+(__builtin_constant_p(c) ? \
+ __constant_c_x_memset((s),(0x01010101UL*(unsigned char)c),(count)) : \
+ __memset((s),(c),(count)))
+
+/*
+ * find the first occurrence of byte 'c', or 1 past the area if none
+ */
+#define __HAVE_ARCH_MEMSCAN
+extern inline void * memscan(void * addr, int c, size_t size)
+{
+ if (!size)
+ return addr;
+ __asm__("cld
+ repnz; scasb
+ jnz 1f
+ dec %%edi
+1: "
+ : "=D" (addr), "=c" (size)
+ : "0" (addr), "1" (size), "a" (c));
+ return addr;
+}
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/system.h b/i386/i386at/gpl/linux/include/asm/system.h
new file mode 100644
index 00000000..9c6f862a
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/system.h
@@ -0,0 +1,301 @@
+#ifndef __ASM_SYSTEM_H
+#define __ASM_SYSTEM_H
+
+#include <asm/segment.h>
+
+/*
+ * Entry into gdt where to find first TSS. GDT layout:
+ * 0 - nul
+ * 1 - kernel code segment
+ * 2 - kernel data segment
+ * 3 - user code segment
+ * 4 - user data segment
+ * ...
+ * 8 - TSS #0
+ * 9 - LDT #0
+ * 10 - TSS #1
+ * 11 - LDT #1
+ */
+#define FIRST_TSS_ENTRY 8
+#define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)
+#define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))
+#define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))
+#define load_TR(n) __asm__("ltr %%ax": /* no output */ :"a" (_TSS(n)))
+#define load_ldt(n) __asm__("lldt %%ax": /* no output */ :"a" (_LDT(n)))
+#define store_TR(n) \
+__asm__("str %%ax\n\t" \
+ "subl %2,%%eax\n\t" \
+ "shrl $4,%%eax" \
+ :"=a" (n) \
+ :"0" (0),"i" (FIRST_TSS_ENTRY<<3))
+
+/* This special macro can be used to load a debugging register */
+
+#define loaddebug(register) \
+ __asm__("movl %0,%%edx\n\t" \
+ "movl %%edx,%%db" #register "\n\t" \
+ : /* no output */ \
+ :"m" (current->debugreg[register]) \
+ :"dx");
+
+
+/*
+ * switch_to(n) should switch tasks to task nr n, first
+ * checking that n isn't the current task, in which case it does nothing.
+ * This also clears the TS-flag if the task we switched to has used
+ * the math co-processor latest.
+ *
+ * It also reloads the debug regs if necessary..
+ */
+
+
+#ifdef __SMP__
+ /*
+ * Keep the lock depth straight. If we switch on an interrupt from
+ * kernel->user task we need to lose a depth, and if we switch the
+ * other way we need to gain a depth. Same layer switches come out
+ * the same.
+ *
+ * We spot a switch in user mode because the kernel counter is the
+ * same as the interrupt counter depth. (We never switch during the
+ * message/invalidate IPI).
+ *
+ * We fsave/fwait so that an exception goes off at the right time
+ * (as a call from the fsave or fwait in effect) rather than to
+ * the wrong process.
+ */
+
+#define switch_to(tsk) do { \
+ cli();\
+ if(current->flags&PF_USEDFPU) \
+ { \
+ __asm__ __volatile__("fnsave %0":"=m" (current->tss.i387.hard)); \
+ __asm__ __volatile__("fwait"); \
+ current->flags&=~PF_USEDFPU; \
+ } \
+ current->lock_depth=syscall_count; \
+ kernel_counter+=next->lock_depth-current->lock_depth; \
+ syscall_count=next->lock_depth; \
+__asm__("pushl %%edx\n\t" \
+ "movl "SYMBOL_NAME_STR(apic_reg)",%%edx\n\t" \
+ "movl 0x20(%%edx), %%edx\n\t" \
+ "shrl $22,%%edx\n\t" \
+ "and $0x3C,%%edx\n\t" \
+ "xchgl %%ecx,"SYMBOL_NAME_STR(current_set)"(,%%edx)\n\t" \
+ "popl %%edx\n\t" \
+ "ljmp %0\n\t" \
+ "sti\n\t" \
+ : /* no output */ \
+ :"m" (*(((char *)&tsk->tss.tr)-4)), \
+ "c" (tsk) \
+ :"cx"); \
+ /* Now maybe reload the debug registers */ \
+ if(current->debugreg[7]){ \
+ loaddebug(0); \
+ loaddebug(1); \
+ loaddebug(2); \
+ loaddebug(3); \
+ loaddebug(6); \
+ } \
+} while (0)
+
+#else
+#define switch_to(tsk) do { \
+__asm__("cli\n\t" \
+ "xchgl %%ecx,"SYMBOL_NAME_STR(current_set)"\n\t" \
+ "ljmp %0\n\t" \
+ "sti\n\t" \
+ "cmpl %%ecx,"SYMBOL_NAME_STR(last_task_used_math)"\n\t" \
+ "jne 1f\n\t" \
+ "clts\n" \
+ "1:" \
+ : /* no output */ \
+ :"m" (*(((char *)&tsk->tss.tr)-4)), \
+ "c" (tsk) \
+ :"cx"); \
+ /* Now maybe reload the debug registers */ \
+ if(current->debugreg[7]){ \
+ loaddebug(0); \
+ loaddebug(1); \
+ loaddebug(2); \
+ loaddebug(3); \
+ loaddebug(6); \
+ } \
+} while (0)
+#endif
+
+#define _set_base(addr,base) \
+__asm__("movw %%dx,%0\n\t" \
+ "rorl $16,%%edx\n\t" \
+ "movb %%dl,%1\n\t" \
+ "movb %%dh,%2" \
+ : /* no output */ \
+ :"m" (*((addr)+2)), \
+ "m" (*((addr)+4)), \
+ "m" (*((addr)+7)), \
+ "d" (base) \
+ :"dx")
+
+#define _set_limit(addr,limit) \
+__asm__("movw %%dx,%0\n\t" \
+ "rorl $16,%%edx\n\t" \
+ "movb %1,%%dh\n\t" \
+ "andb $0xf0,%%dh\n\t" \
+ "orb %%dh,%%dl\n\t" \
+ "movb %%dl,%1" \
+ : /* no output */ \
+ :"m" (*(addr)), \
+ "m" (*((addr)+6)), \
+ "d" (limit) \
+ :"dx")
+
+#define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )
+#define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )
+
+static inline unsigned long _get_base(char * addr)
+{
+ unsigned long __base;
+ __asm__("movb %3,%%dh\n\t"
+ "movb %2,%%dl\n\t"
+ "shll $16,%%edx\n\t"
+ "movw %1,%%dx"
+ :"=&d" (__base)
+ :"m" (*((addr)+2)),
+ "m" (*((addr)+4)),
+ "m" (*((addr)+7)));
+ return __base;
+}
+
+#define get_base(ldt) _get_base( ((char *)&(ldt)) )
+
+static inline unsigned long get_limit(unsigned long segment)
+{
+ unsigned long __limit;
+ __asm__("lsll %1,%0"
+ :"=r" (__limit):"r" (segment));
+ return __limit+1;
+}
+
+#define nop() __asm__ __volatile__ ("nop")
+
+/*
+ * Clear and set 'TS' bit respectively
+ */
+#define clts() __asm__ __volatile__ ("clts")
+#define stts() \
+__asm__ __volatile__ ( \
+ "movl %%cr0,%%eax\n\t" \
+ "orl $8,%%eax\n\t" \
+ "movl %%eax,%%cr0" \
+ : /* no outputs */ \
+ : /* no inputs */ \
+ :"ax")
+
+
+#define xchg(ptr,x) ((__typeof__(*(ptr)))__xchg((unsigned long)(x),(ptr),sizeof(*(ptr))))
+#define tas(ptr) (xchg((ptr),1))
+
+struct __xchg_dummy { unsigned long a[100]; };
+#define __xg(x) ((volatile struct __xchg_dummy *)(x))
+
+static inline unsigned long __xchg(unsigned long x, volatile void * ptr, int size)
+{
+ switch (size) {
+ case 1:
+ __asm__("xchgb %b0,%1"
+ :"=q" (x), "=m" (*__xg(ptr))
+ :"0" (x), "m" (*__xg(ptr)));
+ break;
+ case 2:
+ __asm__("xchgw %w0,%1"
+ :"=r" (x), "=m" (*__xg(ptr))
+ :"0" (x), "m" (*__xg(ptr)));
+ break;
+ case 4:
+ __asm__("xchgl %0,%1"
+ :"=r" (x), "=m" (*__xg(ptr))
+ :"0" (x), "m" (*__xg(ptr)));
+ break;
+ }
+ return x;
+}
+
+#define mb() __asm__ __volatile__ ("" : : :"memory")
+#define sti() __asm__ __volatile__ ("sti": : :"memory")
+#define cli() __asm__ __volatile__ ("cli": : :"memory")
+
+#define save_flags(x) \
+__asm__ __volatile__("pushfl ; popl %0":"=r" (x): /* no input */ :"memory")
+
+#define restore_flags(x) \
+__asm__ __volatile__("pushl %0 ; popfl": /* no output */ :"r" (x):"memory")
+
+#define iret() __asm__ __volatile__ ("iret": : :"memory")
+
+#define _set_gate(gate_addr,type,dpl,addr) \
+__asm__ __volatile__ ("movw %%dx,%%ax\n\t" \
+ "movw %2,%%dx\n\t" \
+ "movl %%eax,%0\n\t" \
+ "movl %%edx,%1" \
+ :"=m" (*((long *) (gate_addr))), \
+ "=m" (*(1+(long *) (gate_addr))) \
+ :"i" ((short) (0x8000+(dpl<<13)+(type<<8))), \
+ "d" ((char *) (addr)),"a" (KERNEL_CS << 16) \
+ :"ax","dx")
+
+#define set_intr_gate(n,addr) \
+ _set_gate(&idt[n],14,0,addr)
+
+#define set_trap_gate(n,addr) \
+ _set_gate(&idt[n],15,0,addr)
+
+#define set_system_gate(n,addr) \
+ _set_gate(&idt[n],15,3,addr)
+
+#define set_call_gate(a,addr) \
+ _set_gate(a,12,3,addr)
+
+#define _set_seg_desc(gate_addr,type,dpl,base,limit) {\
+ *((gate_addr)+1) = ((base) & 0xff000000) | \
+ (((base) & 0x00ff0000)>>16) | \
+ ((limit) & 0xf0000) | \
+ ((dpl)<<13) | \
+ (0x00408000) | \
+ ((type)<<8); \
+ *(gate_addr) = (((base) & 0x0000ffff)<<16) | \
+ ((limit) & 0x0ffff); }
+
+#define _set_tssldt_desc(n,addr,limit,type) \
+__asm__ __volatile__ ("movw $" #limit ",%1\n\t" \
+ "movw %%ax,%2\n\t" \
+ "rorl $16,%%eax\n\t" \
+ "movb %%al,%3\n\t" \
+ "movb $" type ",%4\n\t" \
+ "movb $0x00,%5\n\t" \
+ "movb %%ah,%6\n\t" \
+ "rorl $16,%%eax" \
+ : /* no output */ \
+ :"a" (addr+0xc0000000), "m" (*(n)), "m" (*(n+2)), "m" (*(n+4)), \
+ "m" (*(n+5)), "m" (*(n+6)), "m" (*(n+7)) \
+ )
+
+#define set_tss_desc(n,addr) _set_tssldt_desc(((char *) (n)),((int)(addr)),235,"0x89")
+#define set_ldt_desc(n,addr,size) \
+ _set_tssldt_desc(((char *) (n)),((int)(addr)),((size << 3) - 1),"0x82")
+
+/*
+ * This is the ldt that every process will get unless we need
+ * something other than this.
+ */
+extern struct desc_struct default_ldt;
+
+/*
+ * disable hlt during certain critical i/o operations
+ */
+#ifndef MACH
+#define HAVE_DISABLE_HLT
+#endif
+void disable_hlt(void);
+void enable_hlt(void);
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/termios.h b/i386/i386at/gpl/linux/include/asm/termios.h
new file mode 100644
index 00000000..e9cbf14e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/termios.h
@@ -0,0 +1,304 @@
+#ifndef _I386_TERMIOS_H
+#define _I386_TERMIOS_H
+
+/* 0x54 is just a magic number to make these relatively unique ('T') */
+
+#define TCGETS 0x5401
+#define TCSETS 0x5402
+#define TCSETSW 0x5403
+#define TCSETSF 0x5404
+#define TCGETA 0x5405
+#define TCSETA 0x5406
+#define TCSETAW 0x5407
+#define TCSETAF 0x5408
+#define TCSBRK 0x5409
+#define TCXONC 0x540A
+#define TCFLSH 0x540B
+#define TIOCEXCL 0x540C
+#define TIOCNXCL 0x540D
+#define TIOCSCTTY 0x540E
+#define TIOCGPGRP 0x540F
+#define TIOCSPGRP 0x5410
+#define TIOCOUTQ 0x5411
+#define TIOCSTI 0x5412
+#define TIOCGWINSZ 0x5413
+#define TIOCSWINSZ 0x5414
+#define TIOCMGET 0x5415
+#define TIOCMBIS 0x5416
+#define TIOCMBIC 0x5417
+#define TIOCMSET 0x5418
+#define TIOCGSOFTCAR 0x5419
+#define TIOCSSOFTCAR 0x541A
+#define FIONREAD 0x541B
+#define TIOCINQ FIONREAD
+#define TIOCLINUX 0x541C
+#define TIOCCONS 0x541D
+#define TIOCGSERIAL 0x541E
+#define TIOCSSERIAL 0x541F
+#define TIOCPKT 0x5420
+#define FIONBIO 0x5421
+#define TIOCNOTTY 0x5422
+#define TIOCSETD 0x5423
+#define TIOCGETD 0x5424
+#define TCSBRKP 0x5425 /* Needed for POSIX tcsendbreak() */
+#define TIOCTTYGSTRUCT 0x5426 /* For debugging only */
+#define FIONCLEX 0x5450 /* these numbers need to be adjusted. */
+#define FIOCLEX 0x5451
+#define FIOASYNC 0x5452
+#define TIOCSERCONFIG 0x5453
+#define TIOCSERGWILD 0x5454
+#define TIOCSERSWILD 0x5455
+#define TIOCGLCKTRMIOS 0x5456
+#define TIOCSLCKTRMIOS 0x5457
+#define TIOCSERGSTRUCT 0x5458 /* For debugging only */
+#define TIOCSERGETLSR 0x5459 /* Get line status register */
+#define TIOCSERGETMULTI 0x545A /* Get multiport config */
+#define TIOCSERSETMULTI 0x545B /* Set multiport config */
+
+#define TIOCMIWAIT 0x545C /* wait for a change on serial input line(s) */
+#define TIOCGICOUNT 0x545D /* read serial port inline interrupt counts */
+
+/* Used for packet mode */
+#define TIOCPKT_DATA 0
+#define TIOCPKT_FLUSHREAD 1
+#define TIOCPKT_FLUSHWRITE 2
+#define TIOCPKT_STOP 4
+#define TIOCPKT_START 8
+#define TIOCPKT_NOSTOP 16
+#define TIOCPKT_DOSTOP 32
+
+struct winsize {
+ unsigned short ws_row;
+ unsigned short ws_col;
+ unsigned short ws_xpixel;
+ unsigned short ws_ypixel;
+};
+
+#define NCC 8
+struct termio {
+ unsigned short c_iflag; /* input mode flags */
+ unsigned short c_oflag; /* output mode flags */
+ unsigned short c_cflag; /* control mode flags */
+ unsigned short c_lflag; /* local mode flags */
+ unsigned char c_line; /* line discipline */
+ unsigned char c_cc[NCC]; /* control characters */
+};
+
+#define NCCS 19
+struct termios {
+ tcflag_t c_iflag; /* input mode flags */
+ tcflag_t c_oflag; /* output mode flags */
+ tcflag_t c_cflag; /* control mode flags */
+ tcflag_t c_lflag; /* local mode flags */
+ cc_t c_line; /* line discipline */
+ cc_t c_cc[NCCS]; /* control characters */
+};
+
+/* c_cc characters */
+#define VINTR 0
+#define VQUIT 1
+#define VERASE 2
+#define VKILL 3
+#define VEOF 4
+#define VTIME 5
+#define VMIN 6
+#define VSWTC 7
+#define VSTART 8
+#define VSTOP 9
+#define VSUSP 10
+#define VEOL 11
+#define VREPRINT 12
+#define VDISCARD 13
+#define VWERASE 14
+#define VLNEXT 15
+#define VEOL2 16
+
+#ifdef __KERNEL__
+/* intr=^C quit=^| erase=del kill=^U
+ eof=^D vtime=\0 vmin=\1 sxtc=\0
+ start=^Q stop=^S susp=^Z eol=\0
+ reprint=^R discard=^U werase=^W lnext=^V
+ eol2=\0
+*/
+#define INIT_C_CC "\003\034\177\025\004\0\1\0\021\023\032\0\022\017\027\026\0"
+#endif
+
+/* c_iflag bits */
+#define IGNBRK 0000001
+#define BRKINT 0000002
+#define IGNPAR 0000004
+#define PARMRK 0000010
+#define INPCK 0000020
+#define ISTRIP 0000040
+#define INLCR 0000100
+#define IGNCR 0000200
+#define ICRNL 0000400
+#define IUCLC 0001000
+#define IXON 0002000
+#define IXANY 0004000
+#define IXOFF 0010000
+#define IMAXBEL 0020000
+
+/* c_oflag bits */
+#define OPOST 0000001
+#define OLCUC 0000002
+#define ONLCR 0000004
+#define OCRNL 0000010
+#define ONOCR 0000020
+#define ONLRET 0000040
+#define OFILL 0000100
+#define OFDEL 0000200
+#define NLDLY 0000400
+#define NL0 0000000
+#define NL1 0000400
+#define CRDLY 0003000
+#define CR0 0000000
+#define CR1 0001000
+#define CR2 0002000
+#define CR3 0003000
+#define TABDLY 0014000
+#define TAB0 0000000
+#define TAB1 0004000
+#define TAB2 0010000
+#define TAB3 0014000
+#define XTABS 0014000
+#define BSDLY 0020000
+#define BS0 0000000
+#define BS1 0020000
+#define VTDLY 0040000
+#define VT0 0000000
+#define VT1 0040000
+#define FFDLY 0100000
+#define FF0 0000000
+#define FF1 0100000
+
+/* c_cflag bit meaning */
+#define CBAUD 0010017
+#define B0 0000000 /* hang up */
+#define B50 0000001
+#define B75 0000002
+#define B110 0000003
+#define B134 0000004
+#define B150 0000005
+#define B200 0000006
+#define B300 0000007
+#define B600 0000010
+#define B1200 0000011
+#define B1800 0000012
+#define B2400 0000013
+#define B4800 0000014
+#define B9600 0000015
+#define B19200 0000016
+#define B38400 0000017
+#define EXTA B19200
+#define EXTB B38400
+#define CSIZE 0000060
+#define CS5 0000000
+#define CS6 0000020
+#define CS7 0000040
+#define CS8 0000060
+#define CSTOPB 0000100
+#define CREAD 0000200
+#define PARENB 0000400
+#define PARODD 0001000
+#define HUPCL 0002000
+#define CLOCAL 0004000
+#define CBAUDEX 0010000
+#define B57600 0010001
+#define B115200 0010002
+#define B230400 0010003
+#define CIBAUD 002003600000 /* input baud rate (not used) */
+#define CRTSCTS 020000000000 /* flow control */
+
+/* c_lflag bits */
+#define ISIG 0000001
+#define ICANON 0000002
+#define XCASE 0000004
+#define ECHO 0000010
+#define ECHOE 0000020
+#define ECHOK 0000040
+#define ECHONL 0000100
+#define NOFLSH 0000200
+#define TOSTOP 0000400
+#define ECHOCTL 0001000
+#define ECHOPRT 0002000
+#define ECHOKE 0004000
+#define FLUSHO 0010000
+#define PENDIN 0040000
+#define IEXTEN 0100000
+
+/* modem lines */
+#define TIOCM_LE 0x001
+#define TIOCM_DTR 0x002
+#define TIOCM_RTS 0x004
+#define TIOCM_ST 0x008
+#define TIOCM_SR 0x010
+#define TIOCM_CTS 0x020
+#define TIOCM_CAR 0x040
+#define TIOCM_RNG 0x080
+#define TIOCM_DSR 0x100
+#define TIOCM_CD TIOCM_CAR
+#define TIOCM_RI TIOCM_RNG
+
+/* ioctl (fd, TIOCSERGETLSR, &result) where result may be as below */
+#define TIOCSER_TEMT 0x01 /* Transmitter physically empty */
+
+
+/* tcflow() and TCXONC use these */
+#define TCOOFF 0
+#define TCOON 1
+#define TCIOFF 2
+#define TCION 3
+
+/* tcflush() and TCFLSH use these */
+#define TCIFLUSH 0
+#define TCOFLUSH 1
+#define TCIOFLUSH 2
+
+/* tcsetattr uses these */
+#define TCSANOW 0
+#define TCSADRAIN 1
+#define TCSAFLUSH 2
+
+/* line disciplines */
+#define N_TTY 0
+#define N_SLIP 1
+#define N_MOUSE 2
+#define N_PPP 3
+
+#ifdef __KERNEL__
+
+#include <linux/string.h>
+
+/*
+ * Translate a "termio" structure into a "termios". Ugh.
+ */
+extern inline void trans_from_termio(struct termio * termio,
+ struct termios * termios)
+{
+#define SET_LOW_BITS(x,y) ((x) = (0xffff0000 & (x)) | (y))
+ SET_LOW_BITS(termios->c_iflag, termio->c_iflag);
+ SET_LOW_BITS(termios->c_oflag, termio->c_oflag);
+ SET_LOW_BITS(termios->c_cflag, termio->c_cflag);
+ SET_LOW_BITS(termios->c_lflag, termio->c_lflag);
+#undef SET_LOW_BITS
+ memcpy(termios->c_cc, termio->c_cc, NCC);
+}
+
+/*
+ * Translate a "termios" structure into a "termio". Ugh.
+ */
+extern inline void trans_to_termio(struct termios * termios,
+ struct termio * termio)
+{
+ termio->c_iflag = termios->c_iflag;
+ termio->c_oflag = termios->c_oflag;
+ termio->c_cflag = termios->c_cflag;
+ termio->c_lflag = termios->c_lflag;
+ termio->c_line = termios->c_line;
+ memcpy(termio->c_cc, termios->c_cc, NCC);
+}
+
+#endif /* __KERNEL__ */
+
+#endif /* _I386_TERMIOS_H */
diff --git a/i386/i386at/gpl/linux/include/asm/types.h b/i386/i386at/gpl/linux/include/asm/types.h
new file mode 100644
index 00000000..1b82bef4
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/types.h
@@ -0,0 +1,109 @@
+#ifndef _I386_TYPES_H
+#define _I386_TYPES_H
+
+#ifndef MACH_INCLUDE
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#ifndef _SSIZE_T
+#define _SSIZE_T
+typedef int ssize_t;
+#endif
+
+#ifndef _PTRDIFF_T
+#define _PTRDIFF_T
+typedef int ptrdiff_t;
+#endif
+
+#ifndef _TIME_T
+#define _TIME_T
+typedef long time_t;
+#endif
+
+#ifndef _CLOCK_T
+#define _CLOCK_T
+typedef long clock_t;
+#endif
+#endif /* ! MACH_INCLUDE */
+
+typedef int pid_t;
+#ifndef MACH_INCLUDE
+typedef unsigned short uid_t;
+typedef unsigned short gid_t;
+typedef unsigned short dev_t;
+typedef unsigned long ino_t;
+typedef unsigned short mode_t;
+#endif
+typedef unsigned short umode_t;
+#ifndef MACH_INCLUDE
+typedef unsigned short nlink_t;
+typedef int daddr_t;
+typedef long off_t;
+#endif
+
+/*
+ * __xx is ok: it doesn't pollute the POSIX namespace. Use these in the
+ * header files exported to user space
+ */
+
+typedef __signed__ char __s8;
+typedef unsigned char __u8;
+
+typedef __signed__ short __s16;
+typedef unsigned short __u16;
+
+typedef __signed__ int __s32;
+typedef unsigned int __u32;
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+typedef __signed__ long long __s64;
+typedef unsigned long long __u64;
+#endif
+
+/*
+ * These aren't exported outside the kernel to avoid name space clashes
+ */
+#ifdef __KERNEL__
+
+typedef signed char s8;
+typedef unsigned char u8;
+
+typedef signed short s16;
+typedef unsigned short u16;
+
+typedef signed int s32;
+typedef unsigned int u32;
+
+typedef signed long long s64;
+typedef unsigned long long u64;
+
+#endif /* __KERNEL__ */
+
+#undef __FD_SET
+#define __FD_SET(fd,fdsetp) \
+ __asm__ __volatile__("btsl %1,%0": \
+ "=m" (*(fd_set *) (fdsetp)):"r" ((int) (fd)))
+
+#undef __FD_CLR
+#define __FD_CLR(fd,fdsetp) \
+ __asm__ __volatile__("btrl %1,%0": \
+ "=m" (*(fd_set *) (fdsetp)):"r" ((int) (fd)))
+
+#undef __FD_ISSET
+#define __FD_ISSET(fd,fdsetp) (__extension__ ({ \
+ unsigned char __result; \
+ __asm__ __volatile__("btl %1,%2 ; setb %0" \
+ :"=q" (__result) :"r" ((int) (fd)), \
+ "m" (*(fd_set *) (fdsetp))); \
+ __result; }))
+
+#undef __FD_ZERO
+#define __FD_ZERO(fdsetp) \
+ __asm__ __volatile__("cld ; rep ; stosl" \
+ :"=m" (*(fd_set *) (fdsetp)) \
+ :"a" (0), "c" (__FDSET_INTS), \
+ "D" ((fd_set *) (fdsetp)) :"cx","di")
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/asm/unistd.h b/i386/i386at/gpl/linux/include/asm/unistd.h
new file mode 100644
index 00000000..1837f210
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/asm/unistd.h
@@ -0,0 +1,322 @@
+#ifndef _ASM_I386_UNISTD_H_
+#define _ASM_I386_UNISTD_H_
+
+/*
+ * This file contains the system call numbers.
+ */
+
+#define __NR_setup 0 /* used only by init, to get system going */
+#define __NR_exit 1
+#define __NR_fork 2
+#define __NR_read 3
+#define __NR_write 4
+#define __NR_open 5
+#define __NR_close 6
+#define __NR_waitpid 7
+#define __NR_creat 8
+#define __NR_link 9
+#define __NR_unlink 10
+#define __NR_execve 11
+#define __NR_chdir 12
+#define __NR_time 13
+#define __NR_mknod 14
+#define __NR_chmod 15
+#define __NR_chown 16
+#define __NR_break 17
+#define __NR_oldstat 18
+#define __NR_lseek 19
+#define __NR_getpid 20
+#define __NR_mount 21
+#define __NR_umount 22
+#define __NR_setuid 23
+#define __NR_getuid 24
+#define __NR_stime 25
+#define __NR_ptrace 26
+#define __NR_alarm 27
+#define __NR_oldfstat 28
+#define __NR_pause 29
+#define __NR_utime 30
+#define __NR_stty 31
+#define __NR_gtty 32
+#define __NR_access 33
+#define __NR_nice 34
+#define __NR_ftime 35
+#define __NR_sync 36
+#define __NR_kill 37
+#define __NR_rename 38
+#define __NR_mkdir 39
+#define __NR_rmdir 40
+#define __NR_dup 41
+#define __NR_pipe 42
+#define __NR_times 43
+#define __NR_prof 44
+#define __NR_brk 45
+#define __NR_setgid 46
+#define __NR_getgid 47
+#define __NR_signal 48
+#define __NR_geteuid 49
+#define __NR_getegid 50
+#define __NR_acct 51
+#define __NR_phys 52
+#define __NR_lock 53
+#define __NR_ioctl 54
+#define __NR_fcntl 55
+#define __NR_mpx 56
+#define __NR_setpgid 57
+#define __NR_ulimit 58
+#define __NR_oldolduname 59
+#define __NR_umask 60
+#define __NR_chroot 61
+#define __NR_ustat 62
+#define __NR_dup2 63
+#define __NR_getppid 64
+#define __NR_getpgrp 65
+#define __NR_setsid 66
+#define __NR_sigaction 67
+#define __NR_sgetmask 68
+#define __NR_ssetmask 69
+#define __NR_setreuid 70
+#define __NR_setregid 71
+#define __NR_sigsuspend 72
+#define __NR_sigpending 73
+#define __NR_sethostname 74
+#define __NR_setrlimit 75
+#define __NR_getrlimit 76
+#define __NR_getrusage 77
+#define __NR_gettimeofday 78
+#define __NR_settimeofday 79
+#define __NR_getgroups 80
+#define __NR_setgroups 81
+#define __NR_select 82
+#define __NR_symlink 83
+#define __NR_oldlstat 84
+#define __NR_readlink 85
+#define __NR_uselib 86
+#define __NR_swapon 87
+#define __NR_reboot 88
+#define __NR_readdir 89
+#define __NR_mmap 90
+#define __NR_munmap 91
+#define __NR_truncate 92
+#define __NR_ftruncate 93
+#define __NR_fchmod 94
+#define __NR_fchown 95
+#define __NR_getpriority 96
+#define __NR_setpriority 97
+#define __NR_profil 98
+#define __NR_statfs 99
+#define __NR_fstatfs 100
+#define __NR_ioperm 101
+#define __NR_socketcall 102
+#define __NR_syslog 103
+#define __NR_setitimer 104
+#define __NR_getitimer 105
+#define __NR_stat 106
+#define __NR_lstat 107
+#define __NR_fstat 108
+#define __NR_olduname 109
+#define __NR_iopl 110
+#define __NR_vhangup 111
+#define __NR_idle 112
+#define __NR_vm86 113
+#define __NR_wait4 114
+#define __NR_swapoff 115
+#define __NR_sysinfo 116
+#define __NR_ipc 117
+#define __NR_fsync 118
+#define __NR_sigreturn 119
+#define __NR_clone 120
+#define __NR_setdomainname 121
+#define __NR_uname 122
+#define __NR_modify_ldt 123
+#define __NR_adjtimex 124
+#define __NR_mprotect 125
+#define __NR_sigprocmask 126
+#define __NR_create_module 127
+#define __NR_init_module 128
+#define __NR_delete_module 129
+#define __NR_get_kernel_syms 130
+#define __NR_quotactl 131
+#define __NR_getpgid 132
+#define __NR_fchdir 133
+#define __NR_bdflush 134
+#define __NR_sysfs 135
+#define __NR_personality 136
+#define __NR_afs_syscall 137 /* Syscall for Andrew File System */
+#define __NR_setfsuid 138
+#define __NR_setfsgid 139
+#define __NR__llseek 140
+#define __NR_getdents 141
+#define __NR__newselect 142
+#define __NR_flock 143
+#define __NR_msync 144
+#define __NR_readv 145
+#define __NR_writev 146
+#define __NR_getsid 147
+#define __NR_fdatasync 148
+#define __NR__sysctl 149
+#define __NR_mlock 150
+#define __NR_munlock 151
+#define __NR_mlockall 152
+#define __NR_munlockall 153
+#define __NR_sched_setparam 154
+#define __NR_sched_getparam 155
+#define __NR_sched_setscheduler 156
+#define __NR_sched_getscheduler 157
+#define __NR_sched_yield 158
+#define __NR_sched_get_priority_max 159
+#define __NR_sched_get_priority_min 160
+#define __NR_sched_rr_get_interval 161
+#define __NR_nanosleep 162
+
+/* XXX - _foo needs to be __foo, while __NR_bar could be _NR_bar. */
+#define _syscall0(type,name) \
+type name(void) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name)); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall1(type,name,type1,arg1) \
+type name(type1 arg1) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1))); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall2(type,name,type1,arg1,type2,arg2) \
+type name(type1 arg1,type2 arg2) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2))); \
+if (__res >= 0) \
+ return (type) __res; \
+errno = -__res; \
+return -1; \
+}
+
+#define _syscall3(type,name,type1,arg1,type2,arg2,type3,arg3) \
+type name(type1 arg1,type2 arg2,type3 arg3) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3))); \
+if (__res>=0) \
+ return (type) __res; \
+errno=-__res; \
+return -1; \
+}
+
+#define _syscall4(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4) \
+type name (type1 arg1, type2 arg2, type3 arg3, type4 arg4) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3)),"S" ((long)(arg4))); \
+if (__res>=0) \
+ return (type) __res; \
+errno=-__res; \
+return -1; \
+}
+
+#define _syscall5(type,name,type1,arg1,type2,arg2,type3,arg3,type4,arg4, \
+ type5,arg5) \
+type name (type1 arg1,type2 arg2,type3 arg3,type4 arg4,type5 arg5) \
+{ \
+long __res; \
+__asm__ volatile ("int $0x80" \
+ : "=a" (__res) \
+ : "0" (__NR_##name),"b" ((long)(arg1)),"c" ((long)(arg2)), \
+ "d" ((long)(arg3)),"S" ((long)(arg4)),"D" ((long)(arg5))); \
+if (__res>=0) \
+ return (type) __res; \
+errno=-__res; \
+return -1; \
+}
+
+#ifdef __KERNEL_SYSCALLS__
+
+/*
+ * we need this inline - forking from kernel space will result
+ * in NO COPY ON WRITE (!!!), until an execve is executed. This
+ * is no problem, but for the stack. This is handled by not letting
+ * main() use the stack at all after fork(). Thus, no function
+ * calls - which means inline code for fork too, as otherwise we
+ * would use the stack upon exit from 'fork()'.
+ *
+ * Actually only pause and fork are needed inline, so that there
+ * won't be any messing with the stack from main(), but we define
+ * some others too.
+ */
+#define __NR__exit __NR_exit
+static inline _syscall0(int,idle)
+static inline _syscall0(int,fork)
+static inline _syscall2(int,clone,unsigned long,flags,char *,esp)
+static inline _syscall0(int,pause)
+static inline _syscall0(int,setup)
+static inline _syscall0(int,sync)
+static inline _syscall0(pid_t,setsid)
+static inline _syscall3(int,write,int,fd,const char *,buf,off_t,count)
+static inline _syscall1(int,dup,int,fd)
+static inline _syscall3(int,execve,const char *,file,char **,argv,char **,envp)
+static inline _syscall3(int,open,const char *,file,int,flag,int,mode)
+static inline _syscall1(int,close,int,fd)
+static inline _syscall1(int,_exit,int,exitcode)
+static inline _syscall3(pid_t,waitpid,pid_t,pid,int *,wait_stat,int,options)
+
+static inline pid_t wait(int * wait_stat)
+{
+ return waitpid(-1,wait_stat,0);
+}
+
+/*
+ * This is the mechanism for creating a new kernel thread.
+ *
+ * NOTE! Only a kernel-only process(ie the swapper or direct descendants
+ * who haven't done an "execve()") should use this: it will work within
+ * a system call from a "real" process, but the process memory space will
+ * not be free'd until both the parent and the child have exited.
+ */
+static inline pid_t kernel_thread(int (*fn)(void *), void * arg, unsigned long flags)
+{
+ long retval;
+
+ __asm__ __volatile__(
+ "movl %%esp,%%esi\n\t"
+ "int $0x80\n\t" /* Linux/i386 system call */
+ "cmpl %%esp,%%esi\n\t" /* child or parent? */
+ "je 1f\n\t" /* parent - jump */
+ "pushl %3\n\t" /* push argument */
+ "call *%4\n\t" /* call fn */
+ "movl %2,%0\n\t" /* exit */
+ "int $0x80\n"
+ "1:\t"
+ :"=a" (retval)
+ :"0" (__NR_clone), "i" (__NR_exit),
+ "r" (arg), "r" (fn),
+ "b" (flags | CLONE_VM)
+ :"si");
+ return retval;
+}
+
+#endif
+
+#endif /* _ASM_I386_UNISTD_H_ */
diff --git a/i386/i386at/gpl/linux/include/linux/autoconf.h b/i386/i386at/gpl/linux/include/linux/autoconf.h
new file mode 100644
index 00000000..1588347c
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/autoconf.h
@@ -0,0 +1,210 @@
+/*
+ * Automatically generated C config: don't edit
+ */
+
+/*
+ * Loadable module support
+ */
+#undef CONFIG_MODULES
+#undef CONFIG_MODVERSIONS
+#undef CONFIG_KERNELD
+
+/*
+ * General setup
+ */
+#undef CONFIG_MATH_EMULATION
+#undef CONFIG_NET
+#undef CONFIG_MAX_16M
+#define CONFIG_PCI
+#define CONFIG_SYSVIPC 1
+#define CONFIG_BINFMT_AOUT 1
+#define CONFIG_BINFMT_ELF 1
+#undef CONFIG_KERNEL_ELF
+#undef CONFIG_M386
+#define CONFIG_M486 1
+#undef CONFIG_M586
+#undef CONFIG_M686
+
+/*
+ * Floppy, IDE, and other block devices
+ */
+#define CONFIG_BLK_DEV_FD 1
+#define CONFIG_BLK_DEV_IDE 1
+
+/*
+ * Please see drivers/block/README.ide for help/info on IDE drives
+ */
+#define CONFIG_BLK_DEV_HD_IDE 1
+#define CONFIG_BLK_DEV_IDEATAPI 1
+#define CONFIG_BLK_DEV_IDECD 1
+#undef CONFIG_BLK_DEV_IDETAPE
+#define CONFIG_BLK_DEV_CMD640 1
+#define CONFIG_BLK_DEV_RZ1000 1
+#define CONFIG_BLK_DEV_TRITON 1
+#define CONFIG_IDE_CHIPSETS 1
+#undef CONFIG_BLK_DEV_RAM
+#undef CONFIG_BLK_DEV_LOOP
+#undef CONFIG_BLK_DEV_XD
+
+/*
+ * Networking options
+ */
+#undef CONFIG_FIREWALL
+#undef CONFIG_NET_ALIAS
+#define CONFIG_INET 1
+#undef CONFIG_IP_FORWARD
+#undef CONFIG_IP_MULTICAST
+#undef CONFIG_IP_ACCT
+
+/*
+ * (it is safe to leave these untouched)
+ */
+#undef CONFIG_INET_PCTCP
+#undef CONFIG_INET_RARP
+#undef CONFIG_NO_PATH_MTU_DISCOVERY
+#undef CONFIG_TCP_NAGLE_OFF
+#define CONFIG_IP_NOSR 1
+#define CONFIG_SKB_LARGE 1
+
+/*
+ *
+ */
+#undef CONFIG_IPX
+#undef CONFIG_ATALK
+#undef CONFIG_AX25
+#undef CONFIG_NETLINK
+
+/*
+ * SCSI support
+ */
+#define CONFIG_SCSI 1
+
+/*
+ * SCSI support type (disk, tape, CDrom)
+ */
+#define CONFIG_BLK_DEV_SD 1
+#undef CONFIG_CHR_DEV_ST
+#define CONFIG_BLK_DEV_SR 1
+#undef CONFIG_CHR_DEV_SG
+
+/*
+ * Some SCSI devices (e.g. CD jukebox) support multiple LUNs
+ */
+#undef CONFIG_SCSI_MULTI_LUN
+#undef CONFIG_SCSI_CONSTANTS
+
+/*
+ * SCSI low-level drivers
+ */
+#undef CONFIG_SCSI_ADVANSYS
+#define CONFIG_SCSI_AHA152X 1
+#define CONFIG_SCSI_AHA1542 1
+#define CONFIG_SCSI_AHA1740 1
+#define CONFIG_SCSI_AIC7XXX 1
+#undef CONFIG_SCSI_BUSLOGIC
+#undef CONFIG_SCSI_EATA_DMA
+#undef CONFIG_SCSI_EATA_PIO
+#define CONFIG_SCSI_U14_34F 1
+#undef CONFIG_SCSI_FUTURE_DOMAIN
+#undef CONFIG_SCSI_GENERIC_NCR5380
+#undef CONFIG_SCSI_IN2000
+#undef CONFIG_SCSI_PAS16
+#undef CONFIG_SCSI_QLOGIC
+#define CONFIG_SCSI_SEAGATE 1
+#undef CONFIG_SCSI_T128
+#undef CONFIG_SCSI_ULTRASTOR
+#undef CONFIG_SCSI_7000FASST
+#undef CONFIG_SCSI_EATA
+#undef CONFIG_SCSI_NCR53C406A
+#undef CONFIG_SCSI_AM53C974
+#define CONFIG_SCSI_NCR53C7xx 1
+
+/*
+ * Network device support
+ */
+#undef CONFIG_NETDEVICES
+#undef CONFIG_DUMMY
+#undef CONFIG_SLIP
+#undef CONFIG_SLIP_COMPRESSED
+#undef CONFIG_SLIP_SMART
+#undef CONFIG_PPP
+
+/*
+ * CCP compressors for PPP are only built as modules.
+ */
+#undef CONFIG_SCC
+#undef CONFIG_PLIP
+#undef CONFIG_EQUALIZER
+#undef CONFIG_NET_ALPHA
+#define CONFIG_NET_VENDOR_SMC 1
+#undef CONFIG_LANCE
+#undef CONFIG_NET_VENDOR_3COM
+#undef CONFIG_EL1
+#undef CONFIG_EL2
+#define CONFIG_EL3 1
+#undef CONFIG_VORTEX
+#define CONFIG_NET_ISA 1
+#undef CONFIG_E2100
+#undef CONFIG_DEPCA
+#undef CONFIG_EWRK3
+#define CONFIG_HPLAN_PLUS 1
+#undef CONFIG_HPLAN
+#undef CONFIG_HP100
+#define CONFIG_NE2000 1
+#undef CONFIG_SK_G16
+#undef CONFIG_NET_EISA
+#undef CONFIG_NET_POCKET
+#undef CONFIG_TR
+#undef CONFIG_ARCNET
+#define CONFIG_DE4X5 1
+#define CONFIG_ULTRA 1
+#define CONFIG_WD80x3 1
+
+/*
+ * CD-ROM drivers (not for SCSI or IDE/ATAPI drives)
+ */
+#undef CONFIG_CD_NO_IDESCSI
+
+/*
+ * Filesystems
+ */
+#undef CONFIG_QUOTA
+#define CONFIG_MINIX_FS 1
+#undef CONFIG_EXT_FS
+#define CONFIG_EXT2_FS 1
+#undef CONFIG_XIA_FS
+#define CONFIG_FAT_FS 1
+#define CONFIG_MSDOS_FS 1
+#undef CONFIG_VFAT_FS
+#undef CONFIG_UMSDOS_FS
+#define CONFIG_PROC_FS 1
+#define CONFIG_NFS_FS 1
+#undef CONFIG_ROOT_NFS
+#undef CONFIG_SMB_FS
+#define CONFIG_ISO9660_FS 1
+#undef CONFIG_HPFS_FS
+#undef CONFIG_SYSV_FS
+
+/*
+ * Character devices
+ */
+#undef CONFIG_CYCLADES
+#undef CONFIG_STALDRV
+#define CONFIG_PRINTER 1
+#undef CONFIG_BUSMOUSE
+#undef CONFIG_PSMOUSE
+#undef CONFIG_MS_BUSMOUSE
+#undef CONFIG_ATIXL_BUSMOUSE
+#undef CONFIG_QIC02_TAPE
+#undef CONFIG_APM
+#undef CONFIG_WATCHDOG
+
+/*
+ * Sound
+ */
+#undef CONFIG_SOUND
+
+/*
+ * Kernel hacking
+ */
+#undef CONFIG_PROFILE
diff --git a/i386/i386at/gpl/linux/include/linux/binfmts.h b/i386/i386at/gpl/linux/include/linux/binfmts.h
new file mode 100644
index 00000000..0d1c403a
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/binfmts.h
@@ -0,0 +1,60 @@
+#ifndef _LINUX_BINFMTS_H
+#define _LINUX_BINFMTS_H
+
+#include <linux/ptrace.h>
+
+/*
+ * MAX_ARG_PAGES defines the number of pages allocated for arguments
+ * and envelope for the new program. 32 should suffice, this gives
+ * a maximum env+arg of 128kB w/4KB pages!
+ */
+#define MAX_ARG_PAGES 32
+
+/*
+ * This structure is used to hold the arguments that are used when loading binaries.
+ */
+struct linux_binprm{
+ char buf[128];
+ unsigned long page[MAX_ARG_PAGES];
+ unsigned long p;
+ int sh_bang;
+ struct inode * inode;
+ int e_uid, e_gid;
+ int argc, envc;
+ char * filename; /* Name of binary */
+ unsigned long loader, exec;
+};
+
+/*
+ * This structure defines the functions that are used to load the binary formats that
+ * linux accepts.
+ */
+struct linux_binfmt {
+ struct linux_binfmt * next;
+ int *use_count;
+ int (*load_binary)(struct linux_binprm *, struct pt_regs * regs);
+ int (*load_shlib)(int fd);
+ int (*core_dump)(long signr, struct pt_regs * regs);
+};
+
+extern int register_binfmt(struct linux_binfmt *);
+extern int unregister_binfmt(struct linux_binfmt *);
+
+extern int read_exec(struct inode *inode, unsigned long offset,
+ char * addr, unsigned long count, int to_kmem);
+
+extern int open_inode(struct inode * inode, int mode);
+
+extern int init_elf_binfmt(void);
+extern int init_aout_binfmt(void);
+
+extern void flush_old_exec(struct linux_binprm * bprm);
+extern unsigned long setup_arg_pages(unsigned long text_size,unsigned long * page);
+extern unsigned long * create_tables(char * p,struct linux_binprm * bprm,int ibcs);
+extern unsigned long copy_strings(int argc,char ** argv,unsigned long *page,
+ unsigned long p, int from_kmem);
+
+/* this eventually goes away */
+#define change_ldt(a,b) setup_arg_pages(a,b)
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/bios32.h b/i386/i386at/gpl/linux/include/linux/bios32.h
new file mode 100644
index 00000000..f57398e5
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/bios32.h
@@ -0,0 +1,61 @@
+/*
+ * BIOS32, PCI BIOS functions and defines
+ * Copyright 1994, Drew Eckhardt
+ *
+ * For more information, please consult
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ *
+ * Manuals are $25 each or $50 for all three, plus $7 shipping
+ * within the United States, $35 abroad.
+ */
+
+#ifndef BIOS32_H
+#define BIOS32_H
+
+/*
+ * Error values that may be returned by the PCI bios. Use
+ * pcibios_strerror() to convert to a printable string.
+ */
+#define PCIBIOS_SUCCESSFUL 0x00
+#define PCIBIOS_FUNC_NOT_SUPPORTED 0x81
+#define PCIBIOS_BAD_VENDOR_ID 0x83
+#define PCIBIOS_DEVICE_NOT_FOUND 0x86
+#define PCIBIOS_BAD_REGISTER_NUMBER 0x87
+#define PCIBIOS_SET_FAILED 0x88
+#define PCIBIOS_BUFFER_TOO_SMALL 0x89
+
+extern int pcibios_present (void);
+extern unsigned long pcibios_init (unsigned long memory_start,
+ unsigned long memory_end);
+extern unsigned long pcibios_fixup (unsigned long memory_start,
+ unsigned long memory_end);
+extern int pcibios_find_class (unsigned int class_code, unsigned short index,
+ unsigned char *bus, unsigned char *dev_fn);
+extern int pcibios_find_device (unsigned short vendor, unsigned short dev_id,
+ unsigned short index, unsigned char *bus,
+ unsigned char *dev_fn);
+extern int pcibios_read_config_byte (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned char *val);
+extern int pcibios_read_config_word (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned short *val);
+extern int pcibios_read_config_dword (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned int *val);
+extern int pcibios_write_config_byte (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned char val);
+extern int pcibios_write_config_word (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned short val);
+extern pcibios_write_config_dword (unsigned char bus, unsigned char dev_fn,
+ unsigned char where, unsigned int val);
+extern const char *pcibios_strerror (int error);
+
+#endif /* BIOS32_H */
diff --git a/i386/i386at/gpl/linux/include/linux/blk.h b/i386/i386at/gpl/linux/include/linux/blk.h
new file mode 100644
index 00000000..d7801dde
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/blk.h
@@ -0,0 +1,424 @@
+#ifndef _BLK_H
+#define _BLK_H
+
+#include <linux/blkdev.h>
+#include <linux/locks.h>
+#include <linux/config.h>
+
+/*
+ * This is used in the elevator algorithm. We don't prioritise reads
+ * over writes any more --- although reads are more time-critical than
+ * writes, by treating them equally we increase filesystem throughput.
+ * This turns out to give better overall performance. -- sct
+ */
+#define IN_ORDER(s1,s2) \
+((s1)->rq_dev < (s2)->rq_dev || (((s1)->rq_dev == (s2)->rq_dev && \
+(s1)->sector < (s2)->sector)))
+
+/*
+ * These will have to be changed to be aware of different buffer
+ * sizes etc.. It actually needs a major cleanup.
+ */
+#ifdef IDE_DRIVER
+#define SECTOR_MASK ((BLOCK_SIZE >> 9) - 1)
+#else
+#define SECTOR_MASK (blksize_size[MAJOR_NR] && \
+ blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] ? \
+ ((blksize_size[MAJOR_NR][MINOR(CURRENT->rq_dev)] >> 9) - 1) : \
+ ((BLOCK_SIZE >> 9) - 1))
+#endif /* IDE_DRIVER */
+
+#define SUBSECTOR(block) (CURRENT->current_nr_sectors > 0)
+
+#ifdef CONFIG_CDU31A
+extern int cdu31a_init(void);
+#endif CONFIG_CDU31A
+#ifdef CONFIG_MCD
+extern int mcd_init(void);
+#endif CONFIG_MCD
+#ifdef CONFIG_MCDX
+extern int mcdx_init(void);
+#endif CONFIG_MCDX
+#ifdef CONFIG_SBPCD
+extern int sbpcd_init(void);
+#endif CONFIG_SBPCD
+#ifdef CONFIG_AZTCD
+extern int aztcd_init(void);
+#endif CONFIG_AZTCD
+#ifdef CONFIG_CDU535
+extern int sony535_init(void);
+#endif CONFIG_CDU535
+#ifdef CONFIG_GSCD
+extern int gscd_init(void);
+#endif CONFIG_GSCD
+#ifdef CONFIG_CM206
+extern int cm206_init(void);
+#endif CONFIG_CM206
+#ifdef CONFIG_OPTCD
+extern int optcd_init(void);
+#endif CONFIG_OPTCD
+#ifdef CONFIG_SJCD
+extern int sjcd_init(void);
+#endif CONFIG_SJCD
+#ifdef CONFIG_CDI_INIT
+extern int cdi_init(void);
+#endif CONFIG_CDI_INIT
+#ifdef CONFIG_BLK_DEV_HD
+extern int hd_init(void);
+#endif
+#ifdef CONFIG_BLK_DEV_IDE
+extern int ide_init(void);
+#endif
+#ifdef CONFIG_BLK_DEV_XD
+extern int xd_init(void);
+#endif
+
+extern void set_device_ro(kdev_t dev,int flag);
+void add_blkdev_randomness(int major);
+
+extern int floppy_init(void);
+extern void rd_load(void);
+extern int rd_init(void);
+extern int rd_doload; /* 1 = load ramdisk, 0 = don't load */
+extern int rd_prompt; /* 1 = prompt for ramdisk, 0 = don't prompt */
+extern int rd_image_start; /* starting block # of image */
+
+#define RO_IOCTLS(dev,where) \
+ case BLKROSET: if (!suser()) return -EACCES; \
+ set_device_ro((dev),get_fs_long((long *) (where))); return 0; \
+ case BLKROGET: { int __err = verify_area(VERIFY_WRITE, (void *) (where), sizeof(long)); \
+ if (!__err) put_fs_long(0!=is_read_only(dev),(long *) (where)); return __err; }
+
+#if defined(MAJOR_NR) || defined(IDE_DRIVER)
+
+/*
+ * Add entries as needed.
+ */
+
+#ifdef IDE_DRIVER
+
+#define DEVICE_NR(device) (MINOR(device) >> PARTN_BITS)
+#define DEVICE_ON(device) /* nothing */
+#define DEVICE_OFF(device) /* nothing */
+
+#elif (MAJOR_NR == RAMDISK_MAJOR)
+
+/* ram disk */
+#define DEVICE_NAME "ramdisk"
+#define DEVICE_REQUEST rd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+#define DEVICE_NO_RANDOM
+
+#elif (MAJOR_NR == FLOPPY_MAJOR)
+
+static void floppy_off(unsigned int nr);
+
+#define DEVICE_NAME "floppy"
+#define DEVICE_INTR do_floppy
+#define DEVICE_REQUEST do_fd_request
+#define DEVICE_NR(device) ( (MINOR(device) & 3) | ((MINOR(device) & 0x80 ) >> 5 ))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device) floppy_off(DEVICE_NR(device))
+
+#elif (MAJOR_NR == HD_MAJOR)
+
+/* harddisk: timeout is 6 seconds.. */
+#define DEVICE_NAME "harddisk"
+#define DEVICE_INTR do_hd
+#define DEVICE_TIMEOUT HD_TIMER
+#define TIMEOUT_VALUE (6*HZ)
+#define DEVICE_REQUEST do_hd_request
+#define DEVICE_NR(device) (MINOR(device)>>6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_DISK_MAJOR)
+
+#define DEVICE_NAME "scsidisk"
+#define DEVICE_INTR do_sd
+#define TIMEOUT_VALUE (2*HZ)
+#define DEVICE_REQUEST do_sd_request
+#define DEVICE_NR(device) (MINOR(device) >> 4)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_TAPE_MAJOR)
+
+#define DEVICE_NAME "scsitape"
+#define DEVICE_INTR do_st
+#define DEVICE_NR(device) (MINOR(device) & 0x7f)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SCSI_CDROM_MAJOR)
+
+#define DEVICE_NAME "CD-ROM"
+#define DEVICE_INTR do_sr
+#define DEVICE_REQUEST do_sr_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == XT_DISK_MAJOR)
+
+#define DEVICE_NAME "xt disk"
+#define DEVICE_REQUEST do_xd_request
+#define DEVICE_NR(device) (MINOR(device) >> 6)
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU31A_CDROM_MAJOR)
+
+#define DEVICE_NAME "CDU31A"
+#define DEVICE_REQUEST do_cdu31a_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcd */
+#define DEVICE_REQUEST do_mcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MITSUMI_X_CDROM_MAJOR)
+
+#define DEVICE_NAME "Mitsumi CD-ROM"
+/* #define DEVICE_INTR do_mcdx */
+#define DEVICE_REQUEST do_mcdx_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #1"
+#define DEVICE_REQUEST do_sbpcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM2_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #2"
+#define DEVICE_REQUEST do_sbpcd2_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM3_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #3"
+#define DEVICE_REQUEST do_sbpcd3_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == MATSUSHITA_CDROM4_MAJOR)
+
+#define DEVICE_NAME "Matsushita CD-ROM controller #4"
+#define DEVICE_REQUEST do_sbpcd4_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == AZTECH_CDROM_MAJOR)
+
+#define DEVICE_NAME "Aztech CD-ROM"
+#define DEVICE_REQUEST do_aztcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CDU535_CDROM_MAJOR)
+
+#define DEVICE_NAME "SONY-CDU535"
+#define DEVICE_INTR do_cdu535
+#define DEVICE_REQUEST do_cdu535_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == GOLDSTAR_CDROM_MAJOR)
+
+#define DEVICE_NAME "Goldstar R420"
+#define DEVICE_REQUEST do_gscd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == CM206_CDROM_MAJOR)
+#define DEVICE_NAME "Philips/LMS cd-rom cm206"
+#define DEVICE_REQUEST do_cm206_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == OPTICS_CDROM_MAJOR)
+
+#define DEVICE_NAME "DOLPHIN 8000AT CD-ROM"
+#define DEVICE_REQUEST do_optcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#elif (MAJOR_NR == SANYO_CDROM_MAJOR)
+
+#define DEVICE_NAME "Sanyo H94A CD-ROM"
+#define DEVICE_REQUEST do_sjcd_request
+#define DEVICE_NR(device) (MINOR(device))
+#define DEVICE_ON(device)
+#define DEVICE_OFF(device)
+
+#endif /* MAJOR_NR == whatever */
+
+#if (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER)
+
+#ifndef CURRENT
+#define CURRENT (blk_dev[MAJOR_NR].current_request)
+#endif
+
+#define CURRENT_DEV DEVICE_NR(CURRENT->rq_dev)
+
+#ifdef DEVICE_INTR
+void (*DEVICE_INTR)(void) = NULL;
+#endif
+#ifdef DEVICE_TIMEOUT
+
+#define SET_TIMER \
+((timer_table[DEVICE_TIMEOUT].expires = jiffies + TIMEOUT_VALUE), \
+(timer_active |= 1<<DEVICE_TIMEOUT))
+
+#define CLEAR_TIMER \
+timer_active &= ~(1<<DEVICE_TIMEOUT)
+
+#define SET_INTR(x) \
+if ((DEVICE_INTR = (x)) != NULL) \
+ SET_TIMER; \
+else \
+ CLEAR_TIMER;
+
+#else
+
+#define SET_INTR(x) (DEVICE_INTR = (x))
+
+#endif /* DEVICE_TIMEOUT */
+
+static void (DEVICE_REQUEST)(void);
+
+#ifdef DEVICE_INTR
+#define CLEAR_INTR SET_INTR(NULL)
+#else
+#define CLEAR_INTR
+#endif
+
+#define INIT_REQUEST \
+ if (!CURRENT) {\
+ CLEAR_INTR; \
+ return; \
+ } \
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+ panic(DEVICE_NAME ": request list destroyed"); \
+ if (CURRENT->bh) { \
+ if (!buffer_locked(CURRENT->bh)) \
+ panic(DEVICE_NAME ": block not locked"); \
+ }
+
+#endif /* (MAJOR_NR != SCSI_TAPE_MAJOR) && !defined(IDE_DRIVER) */
+
+/* end_request() - SCSI devices have their own version */
+/* - IDE drivers have their own copy too */
+
+#if ! SCSI_MAJOR(MAJOR_NR)
+
+#if defined(IDE_DRIVER) && !defined(_IDE_C) /* shared copy for IDE modules */
+void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup);
+#else
+
+#ifdef IDE_DRIVER
+void ide_end_request(byte uptodate, ide_hwgroup_t *hwgroup) {
+ struct request *req = hwgroup->rq;
+#else
+static void end_request(int uptodate) {
+ struct request *req = CURRENT;
+#endif /* IDE_DRIVER */
+ struct buffer_head * bh;
+
+ if (!uptodate) {
+ printk("end_request: I/O error, dev %s, sector %lu\n",
+ kdevname(req->rq_dev), req->sector);
+#ifdef MACH
+ req->errors = 1;
+ while (req->bh) {
+ bh = req->bh;
+ req->bh = bh->b_reqnext;
+ mark_buffer_uptodate(bh, 0);
+ unlock_buffer(bh);
+ }
+ goto done;
+#else
+ req->nr_sectors--;
+ req->nr_sectors &= ~SECTOR_MASK;
+ req->sector += (BLOCK_SIZE / 512);
+ req->sector &= ~SECTOR_MASK;
+#endif
+ }
+
+ if ((bh = req->bh) != NULL) {
+ req->bh = bh->b_reqnext;
+ bh->b_reqnext = NULL;
+ mark_buffer_uptodate(bh, uptodate);
+ unlock_buffer(bh);
+ if ((bh = req->bh) != NULL) {
+ req->current_nr_sectors = bh->b_size >> 9;
+ if (req->nr_sectors < req->current_nr_sectors) {
+ req->nr_sectors = req->current_nr_sectors;
+ printk("end_request: buffer-list destroyed\n");
+ }
+ req->buffer = bh->b_data;
+ return;
+ }
+ }
+#ifdef MACH
+ req->errors = 0;
+
+done:
+#endif
+#ifndef DEVICE_NO_RANDOM
+ add_blkdev_randomness(MAJOR(req->rq_dev));
+#endif
+#ifdef IDE_DRIVER
+ blk_dev[MAJOR(req->rq_dev)].current_request = req->next;
+ hwgroup->rq = NULL;
+#else
+ DEVICE_OFF(req->rq_dev);
+ CURRENT = req->next;
+#endif /* IDE_DRIVER */
+ if (req->sem != NULL)
+ up(req->sem);
+ req->rq_status = RQ_INACTIVE;
+#ifndef MACH
+ wake_up(&wait_for_request);
+#endif
+#ifdef MACH
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ (*blk_dev[MAJOR(req->rq_dev)].request_fn)();
+ restore_flags(flags);
+ }
+#endif
+}
+#endif /* defined(IDE_DRIVER) && !defined(_IDE_C) */
+#endif /* ! SCSI_MAJOR(MAJOR_NR) */
+
+#endif /* defined(MAJOR_NR) || defined(IDE_DRIVER) */
+
+#endif /* _BLK_H */
diff --git a/i386/i386at/gpl/linux/include/linux/blkdev.h b/i386/i386at/gpl/linux/include/linux/blkdev.h
new file mode 100644
index 00000000..ba4d08af
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/blkdev.h
@@ -0,0 +1,56 @@
+#ifndef _LINUX_BLKDEV_H
+#define _LINUX_BLKDEV_H
+
+#include <linux/major.h>
+#include <linux/sched.h>
+#include <linux/genhd.h>
+
+/*
+ * Ok, this is an expanded form so that we can use the same
+ * request for paging requests when that is implemented. In
+ * paging, 'bh' is NULL, and the semaphore is used to wait
+ * for read/write completion.
+ */
+struct request {
+ volatile int rq_status; /* should split this into a few status bits */
+#define RQ_INACTIVE (-1)
+#define RQ_ACTIVE 1
+#define RQ_SCSI_BUSY 0xffff
+#define RQ_SCSI_DONE 0xfffe
+#define RQ_SCSI_DISCONNECTING 0xffe0
+
+ kdev_t rq_dev;
+ int cmd; /* READ or WRITE */
+ int errors;
+ unsigned long sector;
+ unsigned long nr_sectors;
+ unsigned long current_nr_sectors;
+ char * buffer;
+ struct semaphore * sem;
+ struct buffer_head * bh;
+ struct buffer_head * bhtail;
+ struct request * next;
+};
+
+struct blk_dev_struct {
+ void (*request_fn)(void);
+ struct request * current_request;
+};
+
+struct sec_size {
+ unsigned block_size;
+ unsigned block_size_bits;
+};
+
+extern struct sec_size * blk_sec[MAX_BLKDEV];
+extern struct blk_dev_struct blk_dev[MAX_BLKDEV];
+extern struct wait_queue * wait_for_request;
+extern void resetup_one_dev(struct gendisk *dev, int drive);
+
+extern int * blk_size[MAX_BLKDEV];
+
+extern int * blksize_size[MAX_BLKDEV];
+
+extern int * hardsect_size[MAX_BLKDEV];
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/cdrom.h b/i386/i386at/gpl/linux/include/linux/cdrom.h
new file mode 100644
index 00000000..5811ff0f
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/cdrom.h
@@ -0,0 +1,465 @@
+/*
+ * -- <linux/cdrom.h>
+ * general (not only SCSI) header library for linux CDROM drivers
+ * (C) 1992 David Giller rafetmad@oxy.edu
+ * 1994, 1995 Eberhard Moenkeberg emoenke@gwdg.de
+ *
+ */
+
+#ifndef _LINUX_CDROM_H
+#define _LINUX_CDROM_H
+
+/*
+ * some fix numbers
+ */
+#define CD_MINS 74 /* max. minutes per CD, not really a limit */
+#define CD_SECS 60 /* seconds per minute */
+#define CD_FRAMES 75 /* frames per second */
+
+#define CD_SYNC_SIZE 12 /* 12 sync bytes per raw data frame, not transfered by the drive */
+#define CD_HEAD_SIZE 4 /* header (address) bytes per raw data frame */
+#define CD_SUBHEAD_SIZE 8 /* subheader bytes per raw XA data frame */
+#define CD_XA_HEAD (CD_HEAD_SIZE+CD_SUBHEAD_SIZE) /* "before data" part of raw XA frame */
+#define CD_XA_SYNC_HEAD (CD_SYNC_SIZE+CD_XA_HEAD)/* sync bytes + header of XA frame */
+
+#define CD_FRAMESIZE 2048 /* bytes per frame, "cooked" mode */
+#define CD_FRAMESIZE_RAW 2352 /* bytes per frame, "raw" mode */
+/* most drives don't deliver everything: */
+#define CD_FRAMESIZE_RAW1 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE) /* 2340 */
+#define CD_FRAMESIZE_RAW0 (CD_FRAMESIZE_RAW-CD_SYNC_SIZE-CD_HEAD_SIZE) /* 2336 */
+
+#define CD_EDC_SIZE 4 /* bytes EDC per most raw data frame types */
+#define CD_ZERO_SIZE 8 /* bytes zero per yellow book mode 1 frame */
+#define CD_ECC_SIZE 276 /* bytes ECC per most raw data frame types */
+#define CD_XA_TAIL (CD_EDC_SIZE+CD_ECC_SIZE) /* "after data" part of raw XA frame */
+
+#define CD_FRAMESIZE_SUB 96 /* subchannel data "frame" size */
+#define CD_MSF_OFFSET 150 /* MSF numbering offset of first frame */
+
+#define CD_CHUNK_SIZE 24 /* lowest-level "data bytes piece" */
+#define CD_NUM_OF_CHUNKS 98 /* chunks per frame */
+
+#define CD_FRAMESIZE_XA CD_FRAMESIZE_RAW1 /* obsolete name */
+#define CD_BLOCK_OFFSET CD_MSF_OFFSET /* obsolete name */
+
+/*
+ * the raw frame layout:
+ *
+ * - audio (red): | audio_sample_bytes |
+ * | 2352 |
+ *
+ * - data (yellow, mode1): | sync - head - data - EDC - zero - ECC |
+ * | 12 - 4 - 2048 - 4 - 8 - 276 |
+ *
+ * - data (yellow, mode2): | sync - head - data |
+ * | 12 - 4 - 2336 |
+ *
+ * - XA data (green, mode2 form1): | sync - head - sub - data - EDC - ECC |
+ * | 12 - 4 - 8 - 2048 - 4 - 276 |
+ *
+ * - XA data (green, mode2 form2): | sync - head - sub - data - EDC |
+ * | 12 - 4 - 8 - 2324 - 4 |
+ */
+
+/*
+ * CDROM IOCTL structures
+ */
+
+struct cdrom_blk
+{
+ unsigned from;
+ unsigned short len;
+};
+
+
+struct cdrom_msf
+{
+ u_char cdmsf_min0; /* start minute */
+ u_char cdmsf_sec0; /* start second */
+ u_char cdmsf_frame0; /* start frame */
+ u_char cdmsf_min1; /* end minute */
+ u_char cdmsf_sec1; /* end second */
+ u_char cdmsf_frame1; /* end frame */
+};
+
+struct cdrom_ti
+{
+ u_char cdti_trk0; /* start track */
+ u_char cdti_ind0; /* start index */
+ u_char cdti_trk1; /* end track */
+ u_char cdti_ind1; /* end index */
+};
+
+struct cdrom_tochdr
+{
+ u_char cdth_trk0; /* start track */
+ u_char cdth_trk1; /* end track */
+};
+
+struct cdrom_tocentry
+{
+ u_char cdte_track;
+ u_char cdte_adr :4;
+ u_char cdte_ctrl :4;
+ u_char cdte_format;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } cdte_addr;
+ u_char cdte_datamode;
+};
+
+/*
+ * CD-ROM address types (cdrom_tocentry.cdte_format)
+ */
+#define CDROM_LBA 0x01 /* "logical block": first frame is #0 */
+#define CDROM_MSF 0x02 /* "minute-second-frame": binary, not bcd here! */
+
+/*
+ * bit to tell whether track is data or audio (cdrom_tocentry.cdte_ctrl)
+ */
+#define CDROM_DATA_TRACK 0x04
+
+/*
+ * The leadout track is always 0xAA, regardless of # of tracks on disc
+ */
+#define CDROM_LEADOUT 0xAA
+
+struct cdrom_subchnl
+{
+ u_char cdsc_format;
+ u_char cdsc_audiostatus;
+ u_char cdsc_adr: 4;
+ u_char cdsc_ctrl: 4;
+ u_char cdsc_trk;
+ u_char cdsc_ind;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } cdsc_absaddr;
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } cdsc_reladdr;
+};
+
+/*
+ * audio states (from SCSI-2, but seen with other drives, too)
+ */
+#define CDROM_AUDIO_INVALID 0x00 /* audio status not supported */
+#define CDROM_AUDIO_PLAY 0x11 /* audio play operation in progress */
+#define CDROM_AUDIO_PAUSED 0x12 /* audio play operation paused */
+#define CDROM_AUDIO_COMPLETED 0x13 /* audio play successfully completed */
+#define CDROM_AUDIO_ERROR 0x14 /* audio play stopped due to error */
+#define CDROM_AUDIO_NO_STATUS 0x15 /* no current audio status to return */
+
+struct cdrom_volctrl
+{
+ u_char channel0;
+ u_char channel1;
+ u_char channel2;
+ u_char channel3;
+};
+
+struct cdrom_read
+{
+ int cdread_lba;
+ caddr_t cdread_bufaddr;
+ int cdread_buflen;
+};
+
+/*
+ * extensions for transfering audio frames
+ * currently used by sbpcd.c, cdu31a.c, ide-cd.c
+ */
+struct cdrom_read_audio
+{
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } addr; /* frame address */
+ u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+ int nframes; /* number of 2352-byte-frames to read at once, limited by the drivers */
+ u_char *buf; /* frame buffer (size: nframes*2352 bytes) */
+};
+
+/*
+ * this has to be the "arg" of the CDROMMULTISESSION ioctl
+ * for obtaining multi session info.
+ * The returned "addr" is valid only if "xa_flag" is true.
+ */
+struct cdrom_multisession
+{
+ union
+ {
+ struct
+ {
+ u_char minute;
+ u_char second;
+ u_char frame;
+ } msf;
+ int lba;
+ } addr; /* frame address: start-of-last-session (not the new "frame 16"!)*/
+ u_char xa_flag; /* 1: "is XA disk" */
+ u_char addr_format; /* CDROM_LBA or CDROM_MSF */
+};
+
+#ifdef FIVETWELVE
+#define CDROM_MODE1_SIZE 512
+#else
+#define CDROM_MODE1_SIZE 2048
+#endif FIVETWELVE
+#define CDROM_MODE2_SIZE 2336
+
+/*
+ * CD-ROM IOCTL commands
+ * For IOCTL calls, we will commandeer byte 0x53, or 'S'.
+ */
+
+#define CDROMPAUSE 0x5301
+#define CDROMRESUME 0x5302
+#define CDROMPLAYMSF 0x5303 /* (struct cdrom_msf) */
+#define CDROMPLAYTRKIND 0x5304 /* (struct cdrom_ti) */
+
+#define CDROMREADTOCHDR 0x5305 /* (struct cdrom_tochdr) */
+#define CDROMREADTOCENTRY 0x5306 /* (struct cdrom_tocentry) */
+
+#define CDROMSTOP 0x5307 /* stop the drive motor */
+#define CDROMSTART 0x5308 /* turn the motor on */
+
+#define CDROMEJECT 0x5309 /* eject CD-ROM media */
+
+#define CDROMVOLCTRL 0x530a /* (struct cdrom_volctrl) */
+
+#define CDROMSUBCHNL 0x530b /* (struct cdrom_subchnl) */
+
+#define CDROMREADMODE2 0x530c /* (struct cdrom_read) */
+ /* read type-2 data */
+
+#define CDROMREADMODE1 0x530d /* (struct cdrom_read) */
+ /* read type-1 data */
+
+#define CDROMREADAUDIO 0x530e /* (struct cdrom_read_audio) */
+
+/*
+ * enable (1) / disable (0) auto-ejecting
+ */
+#define CDROMEJECT_SW 0x530f /* arg: 0 or 1 */
+
+/*
+ * obtain the start-of-last-session address of multi session disks
+ */
+#define CDROMMULTISESSION 0x5310 /* (struct cdrom_multisession) */
+
+/*
+ * obtain the "universal product code" number
+ * (only some data disks have it coded)
+ */
+#define CDROM_GET_UPC 0x5311 /* 8 bytes returned */
+
+#define CDROMRESET 0x5312 /* hard-reset the drive */
+#define CDROMVOLREAD 0x5313 /* let the drive tell its volume setting */
+ /* (struct cdrom_volctrl) */
+
+/*
+ * these ioctls are used in aztcd.c
+ */
+#define CDROMREADRAW 0x5314 /* read data in raw mode */
+#define CDROMREADCOOKED 0x5315 /* read data in cooked mode */
+#define CDROMSEEK 0x5316 /* seek msf address */
+
+/*
+ * for playing audio in logical block addressing mode
+ */
+#define CDROMPLAYBLK 0x5317 /* (struct cdrom_blk) */
+
+
+/*
+ * CD-ROM-specific SCSI command opcodes
+ */
+
+/*
+ * Group 2 (10-byte). All of these are called 'optional' by SCSI-II.
+ */
+#define SCMD_READ_TOC 0x43 /* read table of contents */
+#define SCMD_PLAYAUDIO_MSF 0x47 /* play data at time offset */
+#define SCMD_PLAYAUDIO_TI 0x48 /* play data at track/index */
+#define SCMD_PAUSE_RESUME 0x4B /* pause/resume audio */
+#define SCMD_READ_SUBCHANNEL 0x42 /* read SC info on playing disc */
+#define SCMD_PLAYAUDIO10 0x45 /* play data at logical block */
+#define SCMD_READ_HEADER 0x44 /* read TOC header */
+
+/*
+ * Group 5
+ */
+#define SCMD_PLAYAUDIO12 0xA5 /* play data at logical block */
+#define SCMD_PLAYTRACK_REL12 0xA9 /* play track at relative offset */
+
+/*
+ * Group 6 Commands
+ */
+#define SCMD_CD_PLAYBACK_CONTROL 0xC9 /* Sony vendor-specific audio */
+#define SCMD_CD_PLAYBACK_STATUS 0xC4 /* control opcodes */
+
+/*
+ * CD-ROM capacity structure.
+ */
+struct scsi_capacity
+{
+ u_long capacity;
+ u_long lbasize;
+};
+
+/*
+ * CD-ROM MODE_SENSE/MODE_SELECT parameters
+ */
+#define ERR_RECOVERY_PARMS 0x01
+#define DISCO_RECO_PARMS 0x02
+#define FORMAT_PARMS 0x03
+#define GEOMETRY_PARMS 0x04
+#define CERTIFICATION_PARMS 0x06
+#define CACHE_PARMS 0x38
+
+/*
+ * standard mode-select header prepended to all mode-select commands
+ */
+struct ccs_modesel_head
+{
+ u_char _r1; /* reserved */
+ u_char medium; /* device-specific medium type */
+ u_char _r2; /* reserved */
+ u_char block_desc_length; /* block descriptor length */
+ u_char density; /* device-specific density code */
+ u_char number_blocks_hi; /* number of blocks in this block desc */
+ u_char number_blocks_med;
+ u_char number_blocks_lo;
+ u_char _r3;
+ u_char block_length_hi; /* block length for blocks in this desc */
+ u_short block_length;
+};
+
+/*
+ * error recovery parameters
+ */
+struct ccs_err_recovery
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char awre : 1; /* auto write realloc enabled */
+ u_char arre : 1; /* auto read realloc enabled */
+ u_char tb : 1; /* transfer block */
+ u_char rc : 1; /* read continuous */
+ u_char eec : 1; /* enable early correction */
+ u_char per : 1; /* post error */
+ u_char dte : 1; /* disable transfer on error */
+ u_char dcr : 1; /* disable correction */
+ u_char retry_count; /* error retry count */
+ u_char correction_span; /* largest recov. to be attempted, bits */
+ u_char head_offset_count; /* head offset (2's C) for each retry */
+ u_char strobe_offset_count; /* data strobe */
+ u_char recovery_time_limit; /* time limit on recovery attempts */
+};
+
+/*
+ * disco/reco parameters
+ */
+struct ccs_disco_reco
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char buffer_full_ratio; /* write buffer reconnect threshold */
+ u_char buffer_empty_ratio; /* read */
+ u_short bus_inactivity_limit; /* limit on bus inactivity time */
+ u_short disconnect_time_limit; /* minimum disconnect time */
+ u_short connect_time_limit; /* minimum connect time */
+ u_short _r2; /* reserved */
+};
+
+/*
+ * drive geometry parameters
+ */
+struct ccs_geometry
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char cyl_ub; /* #cyls */
+ u_char cyl_mb;
+ u_char cyl_lb;
+ u_char heads; /* #heads */
+ u_char precomp_cyl_ub; /* precomp start */
+ u_char precomp_cyl_mb;
+ u_char precomp_cyl_lb;
+ u_char current_cyl_ub; /* reduced current start */
+ u_char current_cyl_mb;
+ u_char current_cyl_lb;
+ u_short step_rate; /* stepping motor rate */
+ u_char landing_cyl_ub; /* landing zone */
+ u_char landing_cyl_mb;
+ u_char landing_cyl_lb;
+ u_char _r2;
+ u_char _r3;
+ u_char _r4;
+};
+
+/*
+ * cache parameters
+ */
+struct ccs_cache
+{
+ u_char _r1 : 2; /* reserved */
+ u_char page_code : 6; /* page code */
+ u_char page_length; /* page length */
+ u_char mode; /* cache control byte */
+ u_char threshold; /* prefetch threshold */
+ u_char max_prefetch; /* maximum prefetch size */
+ u_char max_multiplier; /* maximum prefetch multiplier */
+ u_char min_prefetch; /* minimum prefetch size */
+ u_char min_multiplier; /* minimum prefetch multiplier */
+ u_char _r2[8];
+};
+
+#endif _LINUX_CDROM_H
+/*==========================================================================*/
+/*
+ * 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: 8
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -8
+ * c-argdecl-indent: 8
+ * c-label-offset: -8
+ * c-continued-statement-offset: 8
+ * c-continued-brace-offset: 0
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/include/linux/config.h b/i386/i386at/gpl/linux/include/linux/config.h
new file mode 100644
index 00000000..a54cdff2
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/config.h
@@ -0,0 +1,41 @@
+#ifndef _LINUX_CONFIG_H
+#define _LINUX_CONFIG_H
+
+#include <linux/autoconf.h>
+
+/*
+ * Defines for what uname() should return
+ */
+#ifndef UTS_SYSNAME
+#define UTS_SYSNAME "Linux"
+#endif
+
+#ifndef UTS_MACHINE
+#define UTS_MACHINE "unknown"
+#endif
+
+#ifndef UTS_NODENAME
+#define UTS_NODENAME "(none)" /* set by sethostname() */
+#endif
+
+#ifndef UTS_DOMAINNAME
+#define UTS_DOMAINNAME "(none)" /* set by setdomainname() */
+#endif
+
+/*
+ * The definitions for UTS_RELEASE and UTS_VERSION are now defined
+ * in linux/version.h, and should only be used by linux/version.c
+ */
+
+/* Don't touch these, unless you really know what your doing. */
+#define DEF_INITSEG 0x9000
+#define DEF_SYSSEG 0x1000
+#define DEF_SETUPSEG 0x9020
+#define DEF_SYSSIZE 0x7F00
+
+/* internal svga startup constants */
+#define NORMAL_VGA 0xffff /* 80x25 mode */
+#define EXTENDED_VGA 0xfffe /* 80x50 mode */
+#define ASK_VGA 0xfffd /* ask for it at bootup */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/delay.h b/i386/i386at/gpl/linux/include/linux/delay.h
new file mode 100644
index 00000000..50b5d0b1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/delay.h
@@ -0,0 +1,14 @@
+#ifndef _LINUX_DELAY_H
+#define _LINUX_DELAY_H
+
+/*
+ * Copyright (C) 1993 Linus Torvalds
+ *
+ * Delay routines, using a pre-computed "loops_per_second" value.
+ */
+
+extern unsigned long loops_per_sec;
+
+#include <asm/delay.h>
+
+#endif /* defined(_LINUX_DELAY_H) */
diff --git a/i386/i386at/gpl/linux/include/linux/errno.h b/i386/i386at/gpl/linux/include/linux/errno.h
new file mode 100644
index 00000000..ac212844
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/errno.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_ERRNO_H
+#define _LINUX_ERRNO_H
+
+#include <asm/errno.h>
+
+#ifdef __KERNEL__
+
+/* Should never be seen by user programs */
+#define ERESTARTSYS 512
+#define ERESTARTNOINTR 513
+#define ERESTARTNOHAND 514 /* restart if no handler.. */
+#define ENOIOCTLCMD 515 /* No ioctl command */
+
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/etherdevice.h b/i386/i386at/gpl/linux/include/linux/etherdevice.h
new file mode 100644
index 00000000..b29b76b9
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/etherdevice.h
@@ -0,0 +1,55 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Ethernet handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Relocated to include/linux where it belongs by Alan Cox
+ * <gw4pts@gw4pts.ampr.org>
+ *
+ * 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.
+ *
+ * WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_ETHERDEVICE_H
+#define _LINUX_ETHERDEVICE_H
+
+
+#include <linux/if_ether.h>
+
+#ifdef __KERNEL__
+extern int eth_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr,
+ void *saddr, unsigned len);
+extern int eth_rebuild_header(void *buff, struct device *dev,
+ unsigned long dst, struct sk_buff *skb);
+#ifdef MACH
+#define eth_type_trans(skb, dev) 0
+#else
+extern unsigned short eth_type_trans(struct sk_buff *skb, struct device *dev);
+#endif
+extern void eth_header_cache_bind(struct hh_cache ** hhp, struct device *dev,
+ unsigned short htype, __u32 daddr);
+extern void eth_header_cache_update(struct hh_cache *hh, struct device *dev, unsigned char * haddr);
+#ifdef MACH
+#define eth_copy_and_sum(skb, src, length, base) \
+ memcpy ((skb)->data, src, length)
+#else
+extern void eth_copy_and_sum(struct sk_buff *dest,
+ unsigned char *src, int length, int base);
+#endif
+extern struct device * init_etherdev(struct device *, int);
+
+#endif
+
+#endif /* _LINUX_ETHERDEVICE_H */
diff --git a/i386/i386at/gpl/linux/include/linux/fcntl.h b/i386/i386at/gpl/linux/include/linux/fcntl.h
new file mode 100644
index 00000000..9de3512e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/fcntl.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_FCNTL_H
+#define _LINUX_FCNTL_H
+
+#include <asm/fcntl.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/fd.h b/i386/i386at/gpl/linux/include/linux/fd.h
new file mode 100644
index 00000000..87a837b6
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/fd.h
@@ -0,0 +1,368 @@
+#ifndef _LINUX_FD_H
+#define _LINUX_FD_H
+
+#include <linux/ioctl.h>
+
+/* New file layout: Now the ioctl definitions immediately follow the
+ * definitions of the structures that they use */
+
+/*
+ * Geometry
+ */
+struct floppy_struct {
+ unsigned int size, /* nr of sectors total */
+ sect, /* sectors per track */
+ head, /* nr of heads */
+ track, /* nr of tracks */
+ stretch; /* !=0 means double track steps */
+#define FD_STRETCH 1
+#define FD_SWAPSIDES 2
+
+ unsigned char gap, /* gap1 size */
+
+ rate, /* data rate. |= 0x40 for perpendicular */
+#define FD_2M 0x4
+#define FD_SIZECODEMASK 0x38
+#define FD_SIZECODE(floppy) (((((floppy)->rate&FD_SIZECODEMASK)>> 3)+ 2) %8)
+#define FD_SECTSIZE(floppy) ( (floppy)->rate & FD_2M ? \
+ 512 : 128 << FD_SIZECODE(floppy) )
+#define FD_PERP 0x40
+
+ spec1, /* stepping rate, head unload time */
+ fmt_gap; /* gap2 size */
+ const char * name; /* used only for predefined formats */
+};
+
+
+/* commands needing write access have 0x40 set */
+/* commands needing super user access have 0x80 set */
+
+#define FDCLRPRM _IO(2, 0x41)
+/* clear user-defined parameters */
+
+#define FDSETPRM _IOW(2, 0x42, struct floppy_struct)
+#define FDSETMEDIAPRM FDSETPRM
+/* set user-defined parameters for current media */
+
+#define FDDEFPRM _IOW(2, 0x43, struct floppy_struct)
+#define FDGETPRM _IOR(2, 0x04, struct floppy_struct)
+#define FDDEFMEDIAPRM FDDEFPRM
+#define FDGETMEDIAPRM FDGETPRM
+/* set/get disk parameters */
+
+
+#define FDMSGON _IO(2,0x45)
+#define FDMSGOFF _IO(2,0x46)
+/* issue/don't issue kernel messages on media type change */
+
+
+/*
+ * Formatting (obsolete)
+ */
+#define FD_FILL_BYTE 0xF6 /* format fill byte. */
+
+struct format_descr {
+ unsigned int device,head,track;
+};
+
+#define FDFMTBEG _IO(2,0x47)
+/* begin formatting a disk */
+#define FDFMTTRK _IOW(2,0x48, struct format_descr)
+/* format the specified track */
+#define FDFMTEND _IO(2,0x49)
+/* end formatting a disk */
+
+
+/*
+ * Error thresholds
+ */
+struct floppy_max_errors {
+ unsigned int
+ abort, /* number of errors to be reached before aborting */
+ read_track, /* maximal number of errors permitted to read an
+ * entire track at once */
+ reset, /* maximal number of errors before a reset is tried */
+ recal, /* maximal number of errors before a recalibrate is
+ * tried */
+
+ /*
+ * Threshold for reporting FDC errors to the console.
+ * Setting this to zero may flood your screen when using
+ * ultra cheap floppies ;-)
+ */
+ reporting;
+
+};
+
+#define FDSETEMSGTRESH _IO(2,0x4a)
+/* set fdc error reporting threshold */
+
+#define FDFLUSH _IO(2,0x4b)
+/* flush buffers for media; either for verifying media, or for
+ * handling a media change without closing the file descriptor */
+
+#define FDSETMAXERRS _IOW(2, 0x4c, struct floppy_max_errors)
+#define FDGETMAXERRS _IOR(2, 0x0e, struct floppy_max_errors)
+/* set/get abortion and read_track threshold. See also floppy_drive_params
+ * structure */
+
+
+typedef char floppy_drive_name[16];
+#define FDGETDRVTYP _IOR(2, 0x0f, floppy_drive_name)
+/* get drive type: 5 1/4 or 3 1/2 */
+
+
+/*
+ * Drive parameters (user modifyable)
+ */
+struct floppy_drive_params {
+ char cmos; /* cmos type */
+
+ /* Spec2 is (HLD<<1 | ND), where HLD is head load time (1=2ms, 2=4 ms
+ * etc) and ND is set means no DMA. Hardcoded to 6 (HLD=6ms, use DMA).
+ */
+ unsigned long max_dtr; /* Step rate, usec */
+ unsigned long hlt; /* Head load/settle time, msec */
+ unsigned long hut; /* Head unload time (remnant of
+ * 8" drives) */
+ unsigned long srt; /* Step rate, usec */
+
+ unsigned long spinup; /* time needed for spinup (expressed
+ * in jiffies) */
+ unsigned long spindown; /* timeout needed for spindown */
+ unsigned char spindown_offset; /* decides in which position the disk
+ * will stop */
+ unsigned char select_delay; /* delay to wait after select */
+ unsigned char rps; /* rotations per second */
+ unsigned char tracks; /* maximum number of tracks */
+ unsigned long timeout; /* timeout for interrupt requests */
+
+ unsigned char interleave_sect; /* if there are more sectors, use
+ * interleave */
+
+ struct floppy_max_errors max_errors;
+
+ char flags; /* various flags, including ftd_msg */
+/*
+ * Announce successful media type detection and media information loss after
+ * disk changes.
+ * Also used to enable/disable printing of overrun warnings.
+ */
+
+#define FTD_MSG 0x10
+#define FD_BROKEN_DCL 0x20
+#define FD_DEBUG 0x02
+#define FD_SILENT_DCL_CLEAR 0x4
+#define FD_INVERTED_DCL 0x80
+
+ char read_track; /* use readtrack during probing? */
+
+/*
+ * Auto-detection. Each drive type has eight formats which are
+ * used in succession to try to read the disk. If the FDC cannot lock onto
+ * the disk, the next format is tried. This uses the variable 'probing'.
+ */
+ short autodetect[8]; /* autodetected formats */
+
+ int checkfreq; /* how often should the drive be checked for disk
+ * changes */
+ int native_format; /* native format of this drive */
+};
+
+enum {
+ FD_NEED_TWADDLE_BIT, /* more magic */
+ FD_VERIFY_BIT, /* inquire for write protection */
+ FD_DISK_NEWCHANGE_BIT, /* change detected, and no action undertaken yet
+ * to clear media change status */
+ FD_UNUSED_BIT,
+ FD_DISK_CHANGED_BIT, /* disk has been changed since last i/o */
+ FD_DISK_WRITABLE_BIT /* disk is writable */
+};
+
+#define FDSETDRVPRM _IOW(2, 0x90, struct floppy_drive_params)
+#define FDGETDRVPRM _IOR(2, 0x11, struct floppy_drive_params)
+/* set/get drive parameters */
+
+
+/*
+ * Current drive state (not directly modifyable by user, readonly)
+ */
+struct floppy_drive_struct {
+ signed char flags;
+/* values for these flags */
+#define FD_NEED_TWADDLE (1 << FD_NEED_TWADDLE_BIT)
+#define FD_VERIFY (1 << FD_VERIFY_BIT)
+#define FD_DISK_NEWCHANGE (1 << FD_DISK_NEWCHANGE_BIT)
+#define FD_DISK_CHANGED (1 << FD_DISK_CHANGED_BIT)
+#define FD_DISK_WRITABLE (1 << FD_DISK_WRITABLE_BIT)
+
+ unsigned long spinup_date;
+ unsigned long select_date;
+ unsigned long first_read_date;
+ short probed_format;
+ short track; /* current track */
+ short maxblock; /* id of highest block read */
+ short maxtrack; /* id of highest half track read */
+ int generation; /* how many diskchanges? */
+
+/*
+ * (User-provided) media information is _not_ discarded after a media change
+ * if the corresponding keep_data flag is non-zero. Positive values are
+ * decremented after each probe.
+ */
+ int keep_data;
+
+ /* Prevent "aliased" accesses. */
+ int fd_ref;
+ int fd_device;
+ int last_checked; /* when was the drive last checked for a disk
+ * change? */
+
+ char *dmabuf;
+ int bufblocks;
+};
+
+#define FDGETDRVSTAT _IOR(2, 0x12, struct floppy_drive_struct)
+#define FDPOLLDRVSTAT _IOR(2, 0x13, struct floppy_drive_struct)
+/* get drive state: GET returns the cached state, POLL polls for new state */
+
+
+/*
+ * reset FDC
+ */
+enum reset_mode {
+ FD_RESET_IF_NEEDED, /* reset only if the reset flags is set */
+ FD_RESET_IF_RAWCMD, /* obsolete */
+ FD_RESET_ALWAYS /* reset always */
+};
+#define FDRESET _IO(2, 0x54)
+
+
+/*
+ * FDC state
+ */
+struct floppy_fdc_state {
+ int spec1; /* spec1 value last used */
+ int spec2; /* spec2 value last used */
+ int dtr;
+ unsigned char version; /* FDC version code */
+ unsigned char dor;
+ int address; /* io address */
+ unsigned int rawcmd:2;
+ unsigned int reset:1;
+ unsigned int need_configure:1;
+ unsigned int perp_mode:2;
+ unsigned int has_fifo:1;
+ unsigned int driver_version; /* version code for floppy driver */
+#define FD_DRIVER_VERSION 0x100
+/* user programs using the floppy API should use floppy_fdc_state to
+ * get the version number of the floppy driver that they are running
+ * on. If this version number is bigger than the one compiled into the
+ * user program (the FD_DRIVER_VERSION define), it should be prepared
+ * to bigger structures
+ */
+
+ unsigned char track[4];
+ /* Position of the heads of the 4 units attached to this FDC,
+ * as stored on the FDC. In the future, the position as stored
+ * on the FDC might not agree with the actual physical
+ * position of these drive heads. By allowing such
+ * disagreement, it will be possible to reset the FDC without
+ * incurring the expensive cost of repositioning all heads.
+ * Right now, these positions are hard wired to 0. */
+
+};
+
+#define FDGETFDCSTAT _IOR(2, 0x15, struct floppy_fdc_state)
+
+
+/*
+ * Asynchronous Write error tracking
+ */
+struct floppy_write_errors {
+ /* Write error logging.
+ *
+ * These fields can be cleared with the FDWERRORCLR ioctl.
+ * Only writes that were attempted but failed due to a physical media
+ * error are logged. write(2) calls that fail and return an error code
+ * to the user process are not counted.
+ */
+
+ unsigned int write_errors; /* number of physical write errors
+ * encountered */
+
+ /* position of first and last write errors */
+ unsigned long first_error_sector;
+ int first_error_generation;
+ unsigned long last_error_sector;
+ int last_error_generation;
+
+ unsigned int badness; /* highest retry count for a read or write
+ * operation */
+};
+
+#define FDWERRORCLR _IO(2, 0x56)
+/* clear write error and badness information */
+#define FDWERRORGET _IOR(2, 0x17, struct floppy_write_errors)
+/* get write error and badness information */
+
+
+/*
+ * Raw commands
+ */
+/* new interface flag: now we can do them in batches */
+#define FDHAVEBATCHEDRAWCMD
+
+struct floppy_raw_cmd {
+ unsigned int flags;
+#define FD_RAW_READ 1
+#define FD_RAW_WRITE 2
+#define FD_RAW_NO_MOTOR 4
+#define FD_RAW_DISK_CHANGE 4 /* out: disk change flag was set */
+#define FD_RAW_INTR 8 /* wait for an interrupt */
+#define FD_RAW_SPIN 0x10 /* spin up the disk for this command */
+#define FD_RAW_NO_MOTOR_AFTER 0x20 /* switch the motor off after command
+ * completion */
+#define FD_RAW_NEED_DISK 0x40 /* this command needs a disk to be present */
+#define FD_RAW_NEED_SEEK 0x80 /* this command uses an implied seek (soft) */
+
+/* more "in" flags */
+#define FD_RAW_MORE 0x100 /* more records follow */
+#define FD_RAW_STOP_IF_FAILURE 0x200 /* stop if we encounter a failure */
+#define FD_RAW_STOP_IF_SUCCESS 0x400 /* stop if command successful */
+#define FD_RAW_SOFTFAILURE 0x800 /* consider the return value for failure
+ * detection too */
+
+/* more "out" flags */
+#define FD_RAW_FAILURE 0x10000 /* command sent to fdc, fdc returned error */
+#define FD_RAW_HARDFAILURE 0x20000 /* fdc had to be reset, or timed out */
+
+ void *data;
+ char *kernel_data; /* location of data buffer in the kernel */
+ struct floppy_raw_cmd *next; /* used for chaining of raw cmd's
+ * withing the kernel */
+ long length; /* in: length of dma transfer. out: remaining bytes */
+ long phys_length; /* physical length, if different from dma length */
+ int buffer_length; /* length of allocated buffer */
+
+ unsigned char rate;
+ unsigned char cmd_count;
+ unsigned char cmd[16];
+ unsigned char reply_count;
+ unsigned char reply[16];
+ int track;
+ int resultcode;
+
+ int reserved1;
+ int reserved2;
+};
+
+#define FDRAWCMD _IO(2, 0x58)
+/* send a raw command to the fdc. Structure size not included, because of
+ * batches */
+
+#define FDTWADDLE _IO(2, 0x59)
+/* flicker motor-on bit before reading a sector. Experimental */
+
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/fdreg.h b/i386/i386at/gpl/linux/include/linux/fdreg.h
new file mode 100644
index 00000000..03d8893d
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/fdreg.h
@@ -0,0 +1,127 @@
+#ifndef _LINUX_FDREG_H
+#define _LINUX_FDREG_H
+/*
+ * This file contains some defines for the floppy disk controller.
+ * Various sources. Mostly "IBM Microcomputers: A Programmers
+ * Handbook", Sanches and Canton.
+ */
+
+#ifdef FDPATCHES
+
+#define FD_IOPORT fdc_state[fdc].address
+
+/* Fd controller regs. S&C, about page 340 */
+#define FD_STATUS (4 + FD_IOPORT )
+#define FD_DATA (5 + FD_IOPORT )
+
+/* Digital Output Register */
+#define FD_DOR (2 + FD_IOPORT )
+
+/* Digital Input Register (read) */
+#define FD_DIR (7 + FD_IOPORT )
+
+/* Diskette Control Register (write)*/
+#define FD_DCR (7 + FD_IOPORT )
+
+#else
+
+#define FD_STATUS 0x3f4
+#define FD_DATA 0x3f5
+#define FD_DOR 0x3f2 /* Digital Output Register */
+#define FD_DIR 0x3f7 /* Digital Input Register (read) */
+#define FD_DCR 0x3f7 /* Diskette Control Register (write)*/
+
+#endif
+
+/* Bits of main status register */
+#define STATUS_BUSYMASK 0x0F /* drive busy mask */
+#define STATUS_BUSY 0x10 /* FDC busy */
+#define STATUS_DMA 0x20 /* 0- DMA mode */
+#define STATUS_DIR 0x40 /* 0- cpu->fdc */
+#define STATUS_READY 0x80 /* Data reg ready */
+
+/* Bits of FD_ST0 */
+#define ST0_DS 0x03 /* drive select mask */
+#define ST0_HA 0x04 /* Head (Address) */
+#define ST0_NR 0x08 /* Not Ready */
+#define ST0_ECE 0x10 /* Equipment check error */
+#define ST0_SE 0x20 /* Seek end */
+#define ST0_INTR 0xC0 /* Interrupt code mask */
+
+/* Bits of FD_ST1 */
+#define ST1_MAM 0x01 /* Missing Address Mark */
+#define ST1_WP 0x02 /* Write Protect */
+#define ST1_ND 0x04 /* No Data - unreadable */
+#define ST1_OR 0x10 /* OverRun */
+#define ST1_CRC 0x20 /* CRC error in data or addr */
+#define ST1_EOC 0x80 /* End Of Cylinder */
+
+/* Bits of FD_ST2 */
+#define ST2_MAM 0x01 /* Missing Address Mark (again) */
+#define ST2_BC 0x02 /* Bad Cylinder */
+#define ST2_SNS 0x04 /* Scan Not Satisfied */
+#define ST2_SEH 0x08 /* Scan Equal Hit */
+#define ST2_WC 0x10 /* Wrong Cylinder */
+#define ST2_CRC 0x20 /* CRC error in data field */
+#define ST2_CM 0x40 /* Control Mark = deleted */
+
+/* Bits of FD_ST3 */
+#define ST3_HA 0x04 /* Head (Address) */
+#define ST3_DS 0x08 /* drive is double-sided */
+#define ST3_TZ 0x10 /* Track Zero signal (1=track 0) */
+#define ST3_RY 0x20 /* drive is ready */
+#define ST3_WP 0x40 /* Write Protect */
+#define ST3_FT 0x80 /* Drive Fault */
+
+/* Values for FD_COMMAND */
+#define FD_RECALIBRATE 0x07 /* move to track 0 */
+#define FD_SEEK 0x0F /* seek track */
+#define FD_READ 0xE6 /* read with MT, MFM, SKip deleted */
+#define FD_WRITE 0xC5 /* write with MT, MFM */
+#define FD_SENSEI 0x08 /* Sense Interrupt Status */
+#define FD_SPECIFY 0x03 /* specify HUT etc */
+#define FD_FORMAT 0x4D /* format one track */
+#define FD_VERSION 0x10 /* get version code */
+#define FD_CONFIGURE 0x13 /* configure FIFO operation */
+#define FD_PERPENDICULAR 0x12 /* perpendicular r/w mode */
+#define FD_GETSTATUS 0x04 /* read ST3 */
+#define FD_DUMPREGS 0x0E /* dump the contents of the fdc regs */
+#define FD_READID 0xEA /* prints the header of a sector */
+#define FD_UNLOCK 0x14 /* Fifo config unlock */
+#define FD_LOCK 0x94 /* Fifo config lock */
+#define FD_RSEEK_OUT 0x8f /* seek out (i.e. to lower tracks) */
+#define FD_RSEEK_IN 0xcf /* seek in (i.e. to higher tracks) */
+#define FD_PARTID 0x18 /* part id ("extended" version cmd) */
+#define FD_SAVE 0x2e /* save fdc regs for later restore */
+
+/* DMA commands */
+#define DMA_READ 0x46
+#define DMA_WRITE 0x4A
+
+/* FDC version return types */
+#define FDC_NONE 0x00
+#define FDC_UNKNOWN 0x10 /* DO NOT USE THIS TYPE EXCEPT IF IDENTIFICATION
+ FAILS EARLY */
+#define FDC_8272A 0x20 /* Intel 8272a, NEC 765 */
+#define FDC_765ED 0x30 /* Non-Intel 1MB-compatible FDC, can't detect */
+#define FDC_82072 0x40 /* Intel 82072; 8272a + FIFO + DUMPREGS */
+#define FDC_82077_ORIG 0x50 /* Original version of 82077AA, sans LOCK */
+#define FDC_82077 0x52 /* 82077AA-1 */
+#define FDC_82077_UNKN 0x53 /* Unknown 82077 variant */
+#define FDC_82078 0x60 /* 44pin 82078 or 64pin 82078SL */
+#define FDC_82078_1 0x61 /* 82078-1 (2Mbps fdc) */
+#define FDC_S82078B 0x62 /* S82078B (first seen on Adaptec AVA-2825 VLB
+ * SCSI/EIDE/Floppy controller) */
+#define FDC_87306 0x63 /* National Semiconductor PC 87306 */
+
+/*
+ * Beware: the fdc type list is roughly sorted by increasing features.
+ * Presence of features is tested by comparing the FDC version id with the
+ * "oldest" version that has the needed feature.
+ * If during FDC detection, an obscure test fails late in the sequence, don't
+ * assign FDC_UNKNOWN. Else the FDC will be treated as a dumb 8272a, or worse.
+ * This is especially true if the tests are unneeded.
+ */
+
+#define FD_RESET_DELAY 20
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/fs.h b/i386/i386at/gpl/linux/include/linux/fs.h
new file mode 100644
index 00000000..d5bc62b1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/fs.h
@@ -0,0 +1,720 @@
+#ifndef _LINUX_FS_H
+#define _LINUX_FS_H
+
+/*
+ * This file has definitions for some important file table
+ * structures etc.
+ */
+
+#include <linux/linkage.h>
+#include <linux/limits.h>
+#include <linux/wait.h>
+#include <linux/types.h>
+#include <linux/vfs.h>
+#include <linux/net.h>
+#include <linux/kdev_t.h>
+#include <linux/ioctl.h>
+
+/*
+ * It's silly to have NR_OPEN bigger than NR_FILE, but I'll fix
+ * that later. Anyway, now the file code is no longer dependent
+ * on bitmaps in unsigned longs, but uses the new fd_set structure..
+ *
+ * Some programs (notably those using select()) may have to be
+ * recompiled to take full advantage of the new limits..
+ */
+
+/* Fixed constants first: */
+#undef NR_OPEN
+#define NR_OPEN 256
+
+#define NR_SUPER 64
+#define NR_IHASH 131
+#define BLOCK_SIZE 1024
+#define BLOCK_SIZE_BITS 10
+
+/* And dynamically-tunable limits and defaults: */
+extern int max_inodes, nr_inodes;
+extern int max_files, nr_files;
+#define NR_INODE 2048 /* this should be bigger than NR_FILE */
+#define NR_FILE 1024 /* this can well be larger on a larger system */
+
+#define MAY_EXEC 1
+#define MAY_WRITE 2
+#define MAY_READ 4
+
+#define FMODE_READ 1
+#define FMODE_WRITE 2
+
+#define READ 0
+#define WRITE 1
+#define READA 2 /* read-ahead - don't pause */
+#define WRITEA 3 /* "write-ahead" - silly, but somewhat useful */
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#define NIL_FILP ((struct file *)0)
+#define SEL_IN 1
+#define SEL_OUT 2
+#define SEL_EX 4
+
+/*
+ * These are the fs-independent mount-flags: up to 16 flags are supported
+ */
+#define MS_RDONLY 1 /* Mount read-only */
+#define MS_NOSUID 2 /* Ignore suid and sgid bits */
+#define MS_NODEV 4 /* Disallow access to device special files */
+#define MS_NOEXEC 8 /* Disallow program execution */
+#define MS_SYNCHRONOUS 16 /* Writes are synced at once */
+#define MS_REMOUNT 32 /* Alter flags of a mounted FS */
+#define S_WRITE 128 /* Write on file/directory/symlink */
+#define S_APPEND 256 /* Append-only file */
+#define S_IMMUTABLE 512 /* Immutable file */
+
+/*
+ * Flags that can be altered by MS_REMOUNT
+ */
+#define MS_RMT_MASK (MS_RDONLY)
+
+/*
+ * Magic mount flag number. Has to be or-ed to the flag values.
+ */
+#define MS_MGC_VAL 0xC0ED0000 /* magic flag number to indicate "new" flags */
+#define MS_MGC_MSK 0xffff0000 /* magic flag number mask */
+
+/*
+ * Note that read-only etc flags are inode-specific: setting some file-system
+ * flags just means all the inodes inherit those flags by default. It might be
+ * possible to override it selectively if you really wanted to with some
+ * ioctl() that is not currently implemented.
+ *
+ * Exception: MS_RDONLY is always applied to the entire file system.
+ */
+#define IS_RDONLY(inode) (((inode)->i_sb) && ((inode)->i_sb->s_flags & MS_RDONLY))
+#define IS_NOSUID(inode) ((inode)->i_flags & MS_NOSUID)
+#define IS_NODEV(inode) ((inode)->i_flags & MS_NODEV)
+#define IS_NOEXEC(inode) ((inode)->i_flags & MS_NOEXEC)
+#define IS_SYNC(inode) ((inode)->i_flags & MS_SYNCHRONOUS)
+
+#define IS_WRITABLE(inode) ((inode)->i_flags & S_WRITE)
+#define IS_APPEND(inode) ((inode)->i_flags & S_APPEND)
+#define IS_IMMUTABLE(inode) ((inode)->i_flags & S_IMMUTABLE)
+
+/* the read-only stuff doesn't really belong here, but any other place is
+ probably as bad and I don't want to create yet another include file. */
+
+#define BLKROSET _IO(0x12,93) /* set device read-only (0 = read-write) */
+#define BLKROGET _IO(0x12,94) /* get read-only status (0 = read_write) */
+#define BLKRRPART _IO(0x12,95) /* re-read partition table */
+#define BLKGETSIZE _IO(0x12,96) /* return device size */
+#define BLKFLSBUF _IO(0x12,97) /* flush buffer cache */
+#define BLKRASET _IO(0x12,98) /* Set read ahead for block device */
+#define BLKRAGET _IO(0x12,99) /* get current read ahead setting */
+
+#define BMAP_IOCTL 1 /* obsolete - kept for compatibility */
+#define FIBMAP _IO(0x00,1) /* bmap access */
+#define FIGETBSZ _IO(0x00,2) /* get the block size used for bmap */
+
+#ifdef __KERNEL__
+
+#include <asm/bitops.h>
+
+extern void buffer_init(void);
+extern unsigned long inode_init(unsigned long start, unsigned long end);
+extern unsigned long file_table_init(unsigned long start, unsigned long end);
+extern unsigned long name_cache_init(unsigned long start, unsigned long end);
+
+typedef char buffer_block[BLOCK_SIZE];
+
+/* bh state bits */
+#define BH_Uptodate 0 /* 1 if the buffer contains valid data */
+#define BH_Dirty 1 /* 1 if the buffer is dirty */
+#define BH_Lock 2 /* 1 if the buffer is locked */
+#define BH_Req 3 /* 0 if the buffer has been invalidated */
+#define BH_Touched 4 /* 1 if the buffer has been touched (aging) */
+#define BH_Has_aged 5 /* 1 if the buffer has been aged (aging) */
+#define BH_Protected 6 /* 1 if the buffer is protected */
+#define BH_FreeOnIO 7 /* 1 to discard the buffer_head after IO */
+
+/*
+ * Try to keep the most commonly used fields in single cache lines (16
+ * bytes) to improve performance. This ordering should be
+ * particularly beneficial on 32-bit processors.
+ *
+ * We use the first 16 bytes for the data which is used in searches
+ * over the block hash lists (ie. getblk(), find_buffer() and
+ * friends).
+ *
+ * The second 16 bytes we use for lru buffer scans, as used by
+ * sync_buffers() and refill_freelist(). -- sct
+ */
+#ifdef MACH
+struct buffer_head
+{
+ unsigned long b_blocknr;
+ kdev_t b_dev;
+ unsigned long b_state;
+ unsigned long b_size;
+ char *b_data;
+ struct wait_queue *b_wait;
+ struct buffer_head *b_reqnext;
+ void *b_page_list;
+ int b_index;
+ int b_off;
+ int b_usrcnt;
+ struct request *b_request;
+ struct semaphore *b_sem;
+};
+#else /* ! MACH */
+struct buffer_head {
+ /* First cache line: */
+ unsigned long b_blocknr; /* block number */
+ kdev_t b_dev; /* device (B_FREE = free) */
+ struct buffer_head * b_next; /* Hash queue list */
+ struct buffer_head * b_this_page; /* circular list of buffers in one page */
+
+ /* Second cache line: */
+ unsigned long b_state; /* buffer state bitmap (see above) */
+ struct buffer_head * b_next_free;
+ unsigned int b_count; /* users using this block */
+ unsigned long b_size; /* block size */
+
+ /* Non-performance-critical data follows. */
+ char * b_data; /* pointer to data block (1024 bytes) */
+ unsigned int b_list; /* List that this buffer appears */
+ unsigned long b_flushtime; /* Time when this (dirty) buffer
+ * should be written */
+ unsigned long b_lru_time; /* Time when this buffer was
+ * last used. */
+ struct wait_queue * b_wait;
+ struct buffer_head * b_prev; /* doubly linked list of hash-queue */
+ struct buffer_head * b_prev_free; /* doubly linked list of buffers */
+ struct buffer_head * b_reqnext; /* request queue */
+ char *b_usrbuf;
+ struct request *b_request;
+ struct semaphore *b_sem;
+};
+#endif /* ! MACH */
+
+static inline int buffer_uptodate(struct buffer_head * bh)
+{
+ return test_bit(BH_Uptodate, &bh->b_state);
+}
+
+static inline int buffer_dirty(struct buffer_head * bh)
+{
+ return test_bit(BH_Dirty, &bh->b_state);
+}
+
+static inline int buffer_locked(struct buffer_head * bh)
+{
+ return test_bit(BH_Lock, &bh->b_state);
+}
+
+static inline int buffer_req(struct buffer_head * bh)
+{
+ return test_bit(BH_Req, &bh->b_state);
+}
+
+static inline int buffer_touched(struct buffer_head * bh)
+{
+ return test_bit(BH_Touched, &bh->b_state);
+}
+
+static inline int buffer_has_aged(struct buffer_head * bh)
+{
+ return test_bit(BH_Has_aged, &bh->b_state);
+}
+
+static inline int buffer_protected(struct buffer_head * bh)
+{
+ return test_bit(BH_Protected, &bh->b_state);
+}
+
+#ifndef MACH
+#include <linux/pipe_fs_i.h>
+#include <linux/minix_fs_i.h>
+#include <linux/ext_fs_i.h>
+#include <linux/ext2_fs_i.h>
+#include <linux/hpfs_fs_i.h>
+#include <linux/msdos_fs_i.h>
+#include <linux/umsdos_fs_i.h>
+#include <linux/iso_fs_i.h>
+#include <linux/nfs_fs_i.h>
+#include <linux/xia_fs_i.h>
+#include <linux/sysv_fs_i.h>
+#endif
+
+/*
+ * Attribute flags. These should be or-ed together to figure out what
+ * has been changed!
+ */
+#define ATTR_MODE 1
+#define ATTR_UID 2
+#define ATTR_GID 4
+#define ATTR_SIZE 8
+#define ATTR_ATIME 16
+#define ATTR_MTIME 32
+#define ATTR_CTIME 64
+#define ATTR_ATIME_SET 128
+#define ATTR_MTIME_SET 256
+#define ATTR_FORCE 512 /* Not a change, but a change it */
+
+/*
+ * This is the Inode Attributes structure, used for notify_change(). It
+ * uses the above definitions as flags, to know which values have changed.
+ * Also, in this manner, a Filesystem can look at only the values it cares
+ * about. Basically, these are the attributes that the VFS layer can
+ * request to change from the FS layer.
+ *
+ * Derek Atkins <warlord@MIT.EDU> 94-10-20
+ */
+struct iattr {
+ unsigned int ia_valid;
+ umode_t ia_mode;
+ uid_t ia_uid;
+ gid_t ia_gid;
+ off_t ia_size;
+ time_t ia_atime;
+ time_t ia_mtime;
+ time_t ia_ctime;
+};
+
+#include <linux/quota.h>
+
+#ifdef MACH
+struct inode
+{
+ umode_t i_mode;
+ kdev_t i_rdev;
+};
+
+struct file
+{
+ mode_t f_mode;
+ loff_t f_pos;
+ unsigned short f_flags;
+ int f_resid;
+ void *f_object;
+ void *f_np;
+};
+
+struct vm_area_struct;
+struct page;
+#else /* ! MACH */
+struct inode {
+ kdev_t i_dev;
+ unsigned long i_ino;
+ umode_t i_mode;
+ nlink_t i_nlink;
+ uid_t i_uid;
+ gid_t i_gid;
+ kdev_t i_rdev;
+ off_t i_size;
+ time_t i_atime;
+ time_t i_mtime;
+ time_t i_ctime;
+ unsigned long i_blksize;
+ unsigned long i_blocks;
+ unsigned long i_version;
+ unsigned long i_nrpages;
+ struct semaphore i_sem;
+ struct inode_operations *i_op;
+ struct super_block *i_sb;
+ struct wait_queue *i_wait;
+ struct file_lock *i_flock;
+ struct vm_area_struct *i_mmap;
+ struct page *i_pages;
+ struct dquot *i_dquot[MAXQUOTAS];
+ struct inode *i_next, *i_prev;
+ struct inode *i_hash_next, *i_hash_prev;
+ struct inode *i_bound_to, *i_bound_by;
+ struct inode *i_mount;
+ unsigned short i_count;
+ unsigned short i_flags;
+ unsigned char i_lock;
+ unsigned char i_dirt;
+ unsigned char i_pipe;
+ unsigned char i_sock;
+ unsigned char i_seek;
+ unsigned char i_update;
+ unsigned short i_writecount;
+ union {
+ struct pipe_inode_info pipe_i;
+ struct minix_inode_info minix_i;
+ struct ext_inode_info ext_i;
+ struct ext2_inode_info ext2_i;
+ struct hpfs_inode_info hpfs_i;
+ struct msdos_inode_info msdos_i;
+ struct umsdos_inode_info umsdos_i;
+ struct iso_inode_info isofs_i;
+ struct nfs_inode_info nfs_i;
+ struct xiafs_inode_info xiafs_i;
+ struct sysv_inode_info sysv_i;
+ struct socket socket_i;
+ void * generic_ip;
+ } u;
+};
+
+struct file {
+ mode_t f_mode;
+ loff_t f_pos;
+ unsigned short f_flags;
+ unsigned short f_count;
+ off_t f_reada;
+ struct file *f_next, *f_prev;
+ int f_owner; /* pid or -pgrp where SIGIO should be sent */
+ struct inode * f_inode;
+ struct file_operations * f_op;
+ unsigned long f_version;
+ void *private_data; /* needed for tty driver, and maybe others */
+};
+#endif /* ! MACH */
+
+struct file_lock {
+ struct file_lock *fl_next; /* singly linked list for this inode */
+ struct file_lock *fl_nextlink; /* doubly linked list of all locks */
+ struct file_lock *fl_prevlink; /* used to simplify lock removal */
+ struct file_lock *fl_block;
+ struct task_struct *fl_owner;
+ struct wait_queue *fl_wait;
+ struct file *fl_file;
+ char fl_flags;
+ char fl_type;
+ off_t fl_start;
+ off_t fl_end;
+};
+
+struct fasync_struct {
+ int magic;
+ struct fasync_struct *fa_next; /* singly linked list */
+ struct file *fa_file;
+};
+
+#define FASYNC_MAGIC 0x4601
+
+extern int fasync_helper(struct inode *, struct file *, int, struct fasync_struct **);
+
+#ifndef MACH
+#include <linux/minix_fs_sb.h>
+#include <linux/ext_fs_sb.h>
+#include <linux/ext2_fs_sb.h>
+#include <linux/hpfs_fs_sb.h>
+#include <linux/msdos_fs_sb.h>
+#include <linux/iso_fs_sb.h>
+#include <linux/nfs_fs_sb.h>
+#include <linux/xia_fs_sb.h>
+#include <linux/sysv_fs_sb.h>
+
+struct super_block {
+ kdev_t s_dev;
+ unsigned long s_blocksize;
+ unsigned char s_blocksize_bits;
+ unsigned char s_lock;
+ unsigned char s_rd_only;
+ unsigned char s_dirt;
+ struct file_system_type *s_type;
+ struct super_operations *s_op;
+ struct dquot_operations *dq_op;
+ unsigned long s_flags;
+ unsigned long s_magic;
+ unsigned long s_time;
+ struct inode * s_covered;
+ struct inode * s_mounted;
+ struct wait_queue * s_wait;
+ union {
+ struct minix_sb_info minix_sb;
+ struct ext_sb_info ext_sb;
+ struct ext2_sb_info ext2_sb;
+ struct hpfs_sb_info hpfs_sb;
+ struct msdos_sb_info msdos_sb;
+ struct isofs_sb_info isofs_sb;
+ struct nfs_sb_info nfs_sb;
+ struct xiafs_sb_info xiafs_sb;
+ struct sysv_sb_info sysv_sb;
+ void *generic_sbp;
+ } u;
+};
+#endif /* ! MACH */
+
+/*
+ * This is the "filldir" function type, used by readdir() to let
+ * the kernel specify what kind of dirent layout it wants to have.
+ * This allows the kernel to read directories into kernel space or
+ * to have different dirent layouts depending on the binary type.
+ */
+typedef int (*filldir_t)(void *, const char *, int, off_t, ino_t);
+
+struct file_operations {
+ int (*lseek) (struct inode *, struct file *, off_t, int);
+ int (*read) (struct inode *, struct file *, char *, int);
+ int (*write) (struct inode *, struct file *, const char *, int);
+ int (*readdir) (struct inode *, struct file *, void *, filldir_t);
+ int (*select) (struct inode *, struct file *, int, select_table *);
+ int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned long);
+ int (*mmap) (struct inode *, struct file *, struct vm_area_struct *);
+ int (*open) (struct inode *, struct file *);
+ void (*release) (struct inode *, struct file *);
+ int (*fsync) (struct inode *, struct file *);
+ int (*fasync) (struct inode *, struct file *, int);
+ int (*check_media_change) (kdev_t dev);
+ int (*revalidate) (kdev_t dev);
+};
+
+struct inode_operations {
+ struct file_operations * default_file_ops;
+ int (*create) (struct inode *,const char *,int,int,struct inode **);
+ int (*lookup) (struct inode *,const char *,int,struct inode **);
+ int (*link) (struct inode *,struct inode *,const char *,int);
+ int (*unlink) (struct inode *,const char *,int);
+ int (*symlink) (struct inode *,const char *,int,const char *);
+ int (*mkdir) (struct inode *,const char *,int,int);
+ int (*rmdir) (struct inode *,const char *,int);
+ int (*mknod) (struct inode *,const char *,int,int,int);
+ int (*rename) (struct inode *,const char *,int,struct inode *,const char *,int);
+ int (*readlink) (struct inode *,char *,int);
+ int (*follow_link) (struct inode *,struct inode *,int,int,struct inode **);
+ int (*readpage) (struct inode *, struct page *);
+ int (*writepage) (struct inode *, struct page *);
+ int (*bmap) (struct inode *,int);
+ void (*truncate) (struct inode *);
+ int (*permission) (struct inode *, int);
+ int (*smap) (struct inode *,int);
+};
+
+struct super_operations {
+ void (*read_inode) (struct inode *);
+ int (*notify_change) (struct inode *, struct iattr *);
+ void (*write_inode) (struct inode *);
+ void (*put_inode) (struct inode *);
+ void (*put_super) (struct super_block *);
+ void (*write_super) (struct super_block *);
+ void (*statfs) (struct super_block *, struct statfs *, int);
+ int (*remount_fs) (struct super_block *, int *, char *);
+};
+
+struct dquot_operations {
+ void (*initialize) (struct inode *, short);
+ void (*drop) (struct inode *);
+ int (*alloc_block) (const struct inode *, unsigned long);
+ int (*alloc_inode) (const struct inode *, unsigned long);
+ void (*free_block) (const struct inode *, unsigned long);
+ void (*free_inode) (const struct inode *, unsigned long);
+ int (*transfer) (struct inode *, struct iattr *, char);
+};
+
+struct file_system_type {
+ struct super_block *(*read_super) (struct super_block *, void *, int);
+ const char *name;
+ int requires_dev;
+ struct file_system_type * next;
+};
+
+extern int register_filesystem(struct file_system_type *);
+extern int unregister_filesystem(struct file_system_type *);
+
+asmlinkage int sys_open(const char *, int, int);
+asmlinkage int sys_close(unsigned int); /* yes, it's really unsigned */
+
+extern void kill_fasync(struct fasync_struct *fa, int sig);
+
+extern int getname(const char * filename, char **result);
+extern void putname(char * name);
+extern int do_truncate(struct inode *, unsigned long);
+extern int register_blkdev(unsigned int, const char *, struct file_operations *);
+extern int unregister_blkdev(unsigned int major, const char * name);
+extern int blkdev_open(struct inode * inode, struct file * filp);
+extern struct file_operations def_blk_fops;
+extern struct inode_operations blkdev_inode_operations;
+
+extern int register_chrdev(unsigned int, const char *, struct file_operations *);
+extern int unregister_chrdev(unsigned int major, const char * name);
+extern int chrdev_open(struct inode * inode, struct file * filp);
+extern struct file_operations def_chr_fops;
+extern struct inode_operations chrdev_inode_operations;
+
+extern void init_fifo(struct inode * inode);
+
+extern struct file_operations connecting_fifo_fops;
+extern struct file_operations read_fifo_fops;
+extern struct file_operations write_fifo_fops;
+extern struct file_operations rdwr_fifo_fops;
+extern struct file_operations read_pipe_fops;
+extern struct file_operations write_pipe_fops;
+extern struct file_operations rdwr_pipe_fops;
+
+extern struct file_system_type *get_fs_type(const char *name);
+
+extern int fs_may_mount(kdev_t dev);
+extern int fs_may_umount(kdev_t dev, struct inode * mount_root);
+extern int fs_may_remount_ro(kdev_t dev);
+
+extern struct file *first_file;
+extern struct super_block super_blocks[NR_SUPER];
+
+extern void refile_buffer(struct buffer_head * buf);
+extern void set_writetime(struct buffer_head * buf, int flag);
+extern void refill_freelist(int size);
+extern int try_to_free_buffer(struct buffer_head*, struct buffer_head**, int);
+
+extern struct buffer_head ** buffer_pages;
+extern int nr_buffers;
+extern int buffermem;
+extern int nr_buffer_heads;
+
+#define BUF_CLEAN 0
+#define BUF_UNSHARED 1 /* Buffers that were shared but are not any more */
+#define BUF_LOCKED 2 /* Buffers scheduled for write */
+#define BUF_LOCKED1 3 /* Supers, inodes */
+#define BUF_DIRTY 4 /* Dirty buffers, not yet scheduled for write */
+#define BUF_SHARED 5 /* Buffers shared */
+#define NR_LIST 6
+
+#ifdef MACH
+extern inline void
+mark_buffer_uptodate (struct buffer_head *bh, int on)
+{
+ if (on)
+ set_bit (BH_Uptodate, &bh->b_state);
+ else
+ clear_bit (BH_Uptodate, &bh->b_state);
+}
+#else
+void mark_buffer_uptodate(struct buffer_head * bh, int on);
+#endif
+
+extern inline void mark_buffer_clean(struct buffer_head * bh)
+{
+#ifdef MACH
+ clear_bit(BH_Dirty, &bh->b_state);
+#else
+ if (clear_bit(BH_Dirty, &bh->b_state)) {
+ if (bh->b_list == BUF_DIRTY)
+ refile_buffer(bh);
+ }
+#endif
+}
+
+extern inline void mark_buffer_dirty(struct buffer_head * bh, int flag)
+{
+#ifdef MACH
+ set_bit(BH_Dirty, &bh->b_state);
+#else
+ if (!set_bit(BH_Dirty, &bh->b_state)) {
+ set_writetime(bh, flag);
+ if (bh->b_list != BUF_DIRTY)
+ refile_buffer(bh);
+ }
+#endif
+}
+
+extern int check_disk_change(kdev_t dev);
+#ifdef MACH
+#define invalidate_inodes(dev)
+#else
+extern void invalidate_inodes(kdev_t dev);
+#endif
+extern void invalidate_inode_pages(struct inode *, unsigned long);
+#ifdef MACH
+#define invalidate_buffers(dev)
+#else
+extern void invalidate_buffers(kdev_t dev);
+#endif
+extern int floppy_is_wp(int minor);
+extern void sync_inodes(kdev_t dev);
+#ifdef MACH
+#define sync_dev(dev)
+#define fsync_dev(dev)
+#else
+extern void sync_dev(kdev_t dev);
+extern int fsync_dev(kdev_t dev);
+#endif
+extern void sync_supers(kdev_t dev);
+extern int bmap(struct inode * inode,int block);
+extern int notify_change(struct inode *, struct iattr *);
+extern int namei(const char * pathname, struct inode ** res_inode);
+extern int lnamei(const char * pathname, struct inode ** res_inode);
+#ifdef MACH
+#define permission(i, m) 0
+#else
+extern int permission(struct inode * inode,int mask);
+#endif
+extern int get_write_access(struct inode *inode);
+extern void put_write_access(struct inode *inode);
+extern int open_namei(const char * pathname, int flag, int mode,
+ struct inode ** res_inode, struct inode * base);
+extern int do_mknod(const char * filename, int mode, dev_t dev);
+extern int do_pipe(int *);
+extern void iput(struct inode * inode);
+extern struct inode * __iget(struct super_block * sb,int nr,int crsmnt);
+extern struct inode * get_empty_inode(void);
+extern void insert_inode_hash(struct inode *);
+extern void clear_inode(struct inode *);
+extern struct inode * get_pipe_inode(void);
+extern struct file * get_empty_filp(void);
+extern int close_fp(struct file *filp);
+extern struct buffer_head * get_hash_table(kdev_t dev, int block, int size);
+extern struct buffer_head * getblk(kdev_t dev, int block, int size);
+extern void ll_rw_block(int rw, int nr, struct buffer_head * bh[]);
+extern void ll_rw_page(int rw, kdev_t dev, unsigned long nr, char * buffer);
+extern void ll_rw_swap_file(int rw, kdev_t dev, unsigned int *b, int nb, char *buffer);
+extern int is_read_only(kdev_t dev);
+extern void __brelse(struct buffer_head *buf);
+extern inline void brelse(struct buffer_head *buf)
+{
+ if (buf)
+ __brelse(buf);
+}
+extern void __bforget(struct buffer_head *buf);
+extern inline void bforget(struct buffer_head *buf)
+{
+ if (buf)
+ __bforget(buf);
+}
+extern void set_blocksize(kdev_t dev, int size);
+extern struct buffer_head * bread(kdev_t dev, int block, int size);
+extern struct buffer_head * breada(kdev_t dev,int block, int size,
+ unsigned int pos, unsigned int filesize);
+
+extern int generic_readpage(struct inode *, struct page *);
+extern int generic_file_read(struct inode *, struct file *, char *, int);
+extern int generic_mmap(struct inode *, struct file *, struct vm_area_struct *);
+extern int brw_page(int, unsigned long, kdev_t, int [], int, int);
+
+extern void put_super(kdev_t dev);
+unsigned long generate_cluster(kdev_t dev, int b[], int size);
+extern kdev_t ROOT_DEV;
+
+extern void show_buffers(void);
+extern void mount_root(void);
+
+extern int char_read(struct inode *, struct file *, char *, int);
+extern int block_read(struct inode *, struct file *, char *, int);
+extern int read_ahead[];
+
+extern int char_write(struct inode *, struct file *, const char *, int);
+extern int block_write(struct inode *, struct file *, const char *, int);
+
+extern int block_fsync(struct inode *, struct file *);
+extern int file_fsync(struct inode *, struct file *);
+
+extern void dcache_add(struct inode *, const char *, int, unsigned long);
+extern int dcache_lookup(struct inode *, const char *, int, unsigned long *);
+
+extern int inode_change_ok(struct inode *, struct iattr *);
+extern void inode_setattr(struct inode *, struct iattr *);
+
+extern inline struct inode * iget(struct super_block * sb,int nr)
+{
+ return __iget(sb, nr, 1);
+}
+
+/* kludge to get SCSI modules working */
+#include <linux/minix_fs.h>
+#include <linux/minix_fs_sb.h>
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/genhd.h b/i386/i386at/gpl/linux/include/linux/genhd.h
new file mode 100644
index 00000000..e1c5888d
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/genhd.h
@@ -0,0 +1,73 @@
+#ifndef _LINUX_GENHD_H
+#define _LINUX_GENHD_H
+
+/*
+ * genhd.h Copyright (C) 1992 Drew Eckhardt
+ * Generic hard disk header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#define CONFIG_MSDOS_PARTITION 1
+
+#ifdef __alpha__
+#define CONFIG_OSF_PARTITION 1
+#endif
+
+#ifdef __sparc__
+#define CONFIG_SUN_PARTITION 1
+#endif
+
+/* These two have identical behaviour; use the second one if DOS fdisk gets
+ confused about extended/logical partitions starting past cylinder 1023. */
+#define DOS_EXTENDED_PARTITION 5
+#define LINUX_EXTENDED_PARTITION 0x85
+
+#define DM6_PARTITION 0x54 /* has DDO: use xlated geom & offset */
+#define EZD_PARTITION 0x55 /* EZ-DRIVE: same as DM6 (we think) */
+#define DM6_AUX1PARTITION 0x51 /* no DDO: use xlated geom */
+#define DM6_AUX3PARTITION 0x53 /* no DDO: use xlated geom */
+
+#ifdef MACH_INCLUDE
+struct linux_partition {
+#else
+struct partition {
+#endif
+ unsigned char boot_ind; /* 0x80 - active */
+ unsigned char head; /* starting head */
+ unsigned char sector; /* starting sector */
+ unsigned char cyl; /* starting cylinder */
+ unsigned char sys_ind; /* What partition type */
+ unsigned char end_head; /* end head */
+ unsigned char end_sector; /* end sector */
+ unsigned char end_cyl; /* end cylinder */
+ unsigned int start_sect; /* starting sector counting from 0 */
+ unsigned int nr_sects; /* nr of sectors in partition */
+};
+
+struct hd_struct {
+ long start_sect;
+ long nr_sects;
+};
+
+struct gendisk {
+ int major; /* major number of driver */
+ const char *major_name; /* name of major driver */
+ int minor_shift; /* number of times minor is shifted to
+ get real minor */
+ int max_p; /* maximum partitions per device */
+ int max_nr; /* maximum number of real devices */
+
+ void (*init)(struct gendisk *); /* Initialization called before we do our thing */
+ struct hd_struct *part; /* partition table */
+ int *sizes; /* device size in blocks, copied to blk_size[] */
+ int nr_real; /* number of real devices */
+
+ void *real_devices; /* internal use */
+ struct gendisk *next;
+};
+
+extern struct gendisk *gendisk_head; /* linked list of disks */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/hdreg.h b/i386/i386at/gpl/linux/include/linux/hdreg.h
new file mode 100644
index 00000000..58a9a5df
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/hdreg.h
@@ -0,0 +1,171 @@
+#ifndef _LINUX_HDREG_H
+#define _LINUX_HDREG_H
+
+/*
+ * This file contains some defines for the AT-hd-controller.
+ * Various sources.
+ */
+
+#define HD_IRQ 14 /* the standard disk interrupt */
+
+/* ide.c has it's own port definitions in "ide.h" */
+
+/* Hd controller regs. Ref: IBM AT Bios-listing */
+#define HD_DATA 0x1f0 /* _CTL when writing */
+#define HD_ERROR 0x1f1 /* see err-bits */
+#define HD_NSECTOR 0x1f2 /* nr of sectors to read/write */
+#define HD_SECTOR 0x1f3 /* starting sector */
+#define HD_LCYL 0x1f4 /* starting cylinder */
+#define HD_HCYL 0x1f5 /* high byte of starting cyl */
+#define HD_CURRENT 0x1f6 /* 101dhhhh , d=drive, hhhh=head */
+#define HD_STATUS 0x1f7 /* see status-bits */
+#define HD_FEATURE HD_ERROR /* same io address, read=error, write=feature */
+#define HD_PRECOMP HD_FEATURE /* obsolete use of this port - predates IDE */
+#define HD_COMMAND HD_STATUS /* same io address, read=status, write=cmd */
+
+#define HD_CMD 0x3f6 /* used for resets */
+#define HD_ALTSTATUS 0x3f6 /* same as HD_STATUS but doesn't clear irq */
+
+/* remainder is shared between hd.c, ide.c, ide-cd.c, and the hdparm utility */
+
+/* Bits of HD_STATUS */
+#define ERR_STAT 0x01
+#define INDEX_STAT 0x02
+#define ECC_STAT 0x04 /* Corrected error */
+#define DRQ_STAT 0x08
+#define SEEK_STAT 0x10
+#define WRERR_STAT 0x20
+#define READY_STAT 0x40
+#define BUSY_STAT 0x80
+
+/* Values for HD_COMMAND */
+#define WIN_RESTORE 0x10
+#define WIN_READ 0x20
+#define WIN_WRITE 0x30
+#define WIN_VERIFY 0x40
+#define WIN_FORMAT 0x50
+#define WIN_INIT 0x60
+#define WIN_SEEK 0x70
+#define WIN_DIAGNOSE 0x90
+#define WIN_SPECIFY 0x91 /* set drive geometry translation */
+#define WIN_SETIDLE1 0xE3
+#define WIN_SETIDLE2 0x97
+
+#define WIN_DOORLOCK 0xde /* lock door on removeable drives */
+#define WIN_DOORUNLOCK 0xdf /* unlock door on removeable drives */
+
+#define WIN_MULTREAD 0xC4 /* read sectors using multiple mode */
+#define WIN_MULTWRITE 0xC5 /* write sectors using multiple mode */
+#define WIN_SETMULT 0xC6 /* enable/disable multiple mode */
+#define WIN_IDENTIFY 0xEC /* ask drive to identify itself */
+#define WIN_SETFEATURES 0xEF /* set special drive features */
+#define WIN_READDMA 0xc8 /* read sectors using DMA transfers */
+#define WIN_WRITEDMA 0xca /* write sectors using DMA transfers */
+
+/* Additional drive command codes used by ATAPI devices. */
+#define WIN_PIDENTIFY 0xA1 /* identify ATAPI device */
+#define WIN_SRST 0x08 /* ATAPI soft reset command */
+#define WIN_PACKETCMD 0xa0 /* Send a packet command. */
+
+/* Bits for HD_ERROR */
+#define MARK_ERR 0x01 /* Bad address mark */
+#define TRK0_ERR 0x02 /* couldn't find track 0 */
+#define ABRT_ERR 0x04 /* Command aborted */
+#define ID_ERR 0x10 /* ID field not found */
+#define ECC_ERR 0x40 /* Uncorrectable ECC error */
+#define BBD_ERR 0x80 /* block marked bad */
+
+struct hd_geometry {
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned short cylinders;
+ unsigned long start;
+};
+
+/* hd/ide ctl's that pass (arg) ptrs to user space are numbered 0x030n/0x031n */
+#define HDIO_GETGEO 0x0301 /* get device geometry */
+#define HDIO_GET_UNMASKINTR 0x0302 /* get current unmask setting */
+#define HDIO_GET_MULTCOUNT 0x0304 /* get current IDE blockmode setting */
+#define HDIO_GET_IDENTITY 0x0307 /* get IDE identification info */
+#define HDIO_GET_KEEPSETTINGS 0x0308 /* get keep-settings-on-reset flag */
+#define HDIO_GET_32BIT 0x0309 /* get current io_32bit setting */
+#define HDIO_GET_NOWERR 0x030a /* get ignore-write-error flag */
+#define HDIO_GET_DMA 0x030b /* get use-dma flag */
+#define HDIO_DRIVE_CMD 0x031f /* execute a special drive command */
+
+/* hd/ide ctl's that pass (arg) non-ptr values are numbered 0x032n/0x033n */
+#define HDIO_SET_MULTCOUNT 0x0321 /* change IDE blockmode */
+#define HDIO_SET_UNMASKINTR 0x0322 /* permit other irqs during I/O */
+#define HDIO_SET_KEEPSETTINGS 0x0323 /* keep ioctl settings on reset */
+#define HDIO_SET_32BIT 0x0324 /* change io_32bit flags */
+#define HDIO_SET_NOWERR 0x0325 /* change ignore-write-error flag */
+#define HDIO_SET_DMA 0x0326 /* change use-dma flag */
+#define HDIO_SET_PIO_MODE 0x0327 /* reconfig interface to new speed */
+
+/* structure returned by HDIO_GET_IDENTITY, as per ANSI ATA2 rev.2f spec */
+struct hd_driveid {
+ unsigned short config; /* lots of obsolete bit flags */
+ unsigned short cyls; /* "physical" cyls */
+ unsigned short reserved2; /* reserved (word 2) */
+ unsigned short heads; /* "physical" heads */
+ unsigned short track_bytes; /* unformatted bytes per track */
+ unsigned short sector_bytes; /* unformatted bytes per sector */
+ unsigned short sectors; /* "physical" sectors per track */
+ unsigned short vendor0; /* vendor unique */
+ unsigned short vendor1; /* vendor unique */
+ unsigned short vendor2; /* vendor unique */
+ unsigned char serial_no[20]; /* 0 = not_specified */
+ unsigned short buf_type;
+ unsigned short buf_size; /* 512 byte increments; 0 = not_specified */
+ unsigned short ecc_bytes; /* for r/w long cmds; 0 = not_specified */
+ unsigned char fw_rev[8]; /* 0 = not_specified */
+ unsigned char model[40]; /* 0 = not_specified */
+ unsigned char max_multsect; /* 0=not_implemented */
+ unsigned char vendor3; /* vendor unique */
+ unsigned short dword_io; /* 0=not_implemented; 1=implemented */
+ unsigned char vendor4; /* vendor unique */
+ unsigned char capability; /* bits 0:DMA 1:LBA 2:IORDYsw 3:IORDYsup*/
+ unsigned short reserved50; /* reserved (word 50) */
+ unsigned char vendor5; /* vendor unique */
+ unsigned char tPIO; /* 0=slow, 1=medium, 2=fast */
+ unsigned char vendor6; /* vendor unique */
+ unsigned char tDMA; /* 0=slow, 1=medium, 2=fast */
+ unsigned short field_valid; /* bits 0:cur_ok 1:eide_ok */
+ unsigned short cur_cyls; /* logical cylinders */
+ unsigned short cur_heads; /* logical heads */
+ unsigned short cur_sectors; /* logical sectors per track */
+ unsigned short cur_capacity0; /* logical total sectors on drive */
+ unsigned short cur_capacity1; /* (2 words, misaligned int) */
+ unsigned char multsect; /* current multiple sector count */
+ unsigned char multsect_valid; /* when (bit0==1) multsect is ok */
+ unsigned int lba_capacity; /* total number of sectors */
+ unsigned short dma_1word; /* single-word dma info */
+ unsigned short dma_mword; /* multiple-word dma info */
+ unsigned short eide_pio_modes; /* bits 0:mode3 1:mode4 */
+ unsigned short eide_dma_min; /* min mword dma cycle time (ns) */
+ unsigned short eide_dma_time; /* recommended mword dma cycle time (ns) */
+ unsigned short eide_pio; /* min cycle time (ns), no IORDY */
+ unsigned short eide_pio_iordy; /* min cycle time (ns), with IORDY */
+ unsigned short reserved69; /* reserved (word 69) */
+ unsigned short reserved70; /* reserved (word 70) */
+ /* unsigned short reservedxx[57];*/ /* reserved (words 71-127) */
+ /* unsigned short vendor7 [32];*/ /* vendor unique (words 128-159) */
+ /* unsigned short reservedyy[96];*/ /* reserved (words 160-255) */
+};
+
+#ifdef __KERNEL__
+/*
+ * These routines are used for kernel command line parameters from main.c:
+ */
+#include <linux/config.h>
+
+#ifdef CONFIG_BLK_DEV_HD
+void hd_setup(char *, int *);
+#endif /* CONFIG_BLK_DEV_HD */
+#ifdef CONFIG_BLK_DEV_IDE
+void ide_setup(char *);
+#endif /* CONFIG_BLK_DEV_IDE */
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_HDREG_H */
diff --git a/i386/i386at/gpl/linux/include/linux/head.h b/i386/i386at/gpl/linux/include/linux/head.h
new file mode 100644
index 00000000..3829b1c3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/head.h
@@ -0,0 +1,19 @@
+#ifndef _LINUX_HEAD_H
+#define _LINUX_HEAD_H
+
+typedef struct desc_struct {
+ unsigned long a,b;
+} desc_table[256];
+
+extern desc_table idt,gdt;
+
+#define GDT_NUL 0
+#define GDT_CODE 1
+#define GDT_DATA 2
+#define GDT_TMP 3
+
+#define LDT_NUL 0
+#define LDT_CODE 1
+#define LDT_DATA 2
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/if.h b/i386/i386at/gpl/linux/include/linux/if.h
new file mode 100644
index 00000000..73ef7feb
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/if.h
@@ -0,0 +1,167 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the INET interface module.
+ *
+ * Version: @(#)if.h 1.0.2 04/18/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1982-1988
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_H
+#define _LINUX_IF_H
+
+#include <linux/types.h> /* for "caddr_t" et al */
+#include <linux/socket.h> /* for "struct sockaddr" et al */
+
+/* Standard interface flags. */
+#define LINUX_IFF_UP 0x1 /* interface is up */
+#define LINUX_IFF_BROADCAST 0x2 /* broadcast address valid */
+#define LINUX_IFF_DEBUG 0x4 /* turn on debugging */
+#define LINUX_IFF_LOOPBACK 0x8 /* is a loopback net */
+#define LINUX_IFF_POINTOPOINT 0x10 /* interface is has p-p link */
+#define LINUX_IFF_NOTRAILERS 0x20 /* avoid use of trailers */
+#define LINUX_IFF_RUNNING 0x40 /* resources allocated */
+#define LINUX_IFF_NOARP 0x80 /* no ARP protocol */
+#define LINUX_IFF_PROMISC 0x100 /* receive all packets */
+/* Not supported */
+#define LINUX_IFF_ALLMULTI 0x200 /* receive all multicast packets*/
+
+#define LINUX_IFF_MASTER 0x400 /* master of a load balancer */
+#define LINUX_IFF_SLAVE 0x800 /* slave of a load balancer */
+
+#define LINUX_IFF_MULTICAST 0x1000 /* Supports multicast */
+
+#ifdef MACH
+#ifndef MACH_INCLUDE
+#define IFF_UP LINUX_IFF_UP
+#define IFF_BROADCAST LINUX_IFF_BROADCAST
+#define IFF_DEBUG LINUX_IFF_DEBUG
+#define IFF_LOOPBACK LINUX_IFF_LOOPBACK
+#define IFF_POINTOPOINT LINUX_IFF_POINTOPOINT
+#define IFF_NOTRAILERS LINUX_IFF_NOTRAILERS
+#define IFF_RUNNING LINUX_IFF_RUNNING
+#define IFF_NOARP LINUX_IFF_NOARP
+#define IFF_PROMISC LINUX_IFF_PROMISC
+#define IFF_ALLMULTI LINUX_IFF_ALLMULTI
+#define IFF_MASTER LINUX_IFF_MASTER
+#define IFF_SLAVE LINUX_IFF_SLAVE
+#define IFF_MULTICAST LINUX_IFF_MULTICAST
+#endif
+#endif
+
+/*
+ * The ifaddr structure contains information about one address
+ * of an interface. They are maintained by the different address
+ * families, are allocated and attached when an address is set,
+ * and are linked together so all addresses for an interface can
+ * be located.
+ */
+
+struct ifaddr
+{
+ struct sockaddr ifa_addr; /* address of interface */
+ union {
+ struct sockaddr ifu_broadaddr;
+ struct sockaddr ifu_dstaddr;
+ } ifa_ifu;
+ struct iface *ifa_ifp; /* back-pointer to interface */
+ struct ifaddr *ifa_next; /* next address for interface */
+};
+
+#define ifa_broadaddr ifa_ifu.ifu_broadaddr /* broadcast address */
+#define ifa_dstaddr ifa_ifu.ifu_dstaddr /* other end of link */
+
+/*
+ * Device mapping structure. I'd just gone off and designed a
+ * beautiful scheme using only loadable modules with arguments
+ * for driver options and along come the PCMCIA people 8)
+ *
+ * Ah well. The get() side of this is good for WDSETUP, and it'll
+ * be handy for debugging things. The set side is fine for now and
+ * being very small might be worth keeping for clean configuration.
+ */
+
+struct ifmap
+{
+ unsigned long mem_start;
+ unsigned long mem_end;
+ unsigned short base_addr;
+ unsigned char irq;
+ unsigned char dma;
+ unsigned char port;
+ /* 3 bytes spare */
+};
+
+/*
+ * Interface request structure used for socket
+ * ioctl's. All interface ioctl's must have parameter
+ * definitions which begin with ifr_name. The
+ * remainder may be interface specific.
+ */
+
+struct ifreq
+{
+#define IFHWADDRLEN 6
+#define IFNAMSIZ 16
+ union
+ {
+ char ifrn_name[IFNAMSIZ]; /* if name, e.g. "en0" */
+ } ifr_ifrn;
+
+ union {
+ struct sockaddr ifru_addr;
+ struct sockaddr ifru_dstaddr;
+ struct sockaddr ifru_broadaddr;
+ struct sockaddr ifru_netmask;
+ struct sockaddr ifru_hwaddr;
+ short ifru_flags;
+ int ifru_metric;
+ int ifru_mtu;
+ struct ifmap ifru_map;
+ char ifru_slave[IFNAMSIZ]; /* Just fits the size */
+ caddr_t ifru_data;
+ } ifr_ifru;
+};
+
+#define ifr_name ifr_ifrn.ifrn_name /* interface name */
+#define ifr_hwaddr ifr_ifru.ifru_hwaddr /* MAC address */
+#define ifr_addr ifr_ifru.ifru_addr /* address */
+#define ifr_dstaddr ifr_ifru.ifru_dstaddr /* other end of p-p lnk */
+#define ifr_broadaddr ifr_ifru.ifru_broadaddr /* broadcast address */
+#define ifr_netmask ifr_ifru.ifru_netmask /* interface net mask */
+#define ifr_flags ifr_ifru.ifru_flags /* flags */
+#define ifr_metric ifr_ifru.ifru_metric /* metric */
+#define ifr_mtu ifr_ifru.ifru_mtu /* mtu */
+#define ifr_map ifr_ifru.ifru_map /* device map */
+#define ifr_slave ifr_ifru.ifru_slave /* slave device */
+#define ifr_data ifr_ifru.ifru_data /* for use by interface */
+
+/*
+ * Structure used in SIOCGIFCONF request.
+ * Used to retrieve interface configuration
+ * for machine (useful for programs which
+ * must know all networks accessible).
+ */
+
+struct ifconf
+{
+ int ifc_len; /* size of buffer */
+ union
+ {
+ caddr_t ifcu_buf;
+ struct ifreq *ifcu_req;
+ } ifc_ifcu;
+};
+#define ifc_buf ifc_ifcu.ifcu_buf /* buffer address */
+#define ifc_req ifc_ifcu.ifcu_req /* array of structures */
+
+#endif /* _LINUX_IF_H */
diff --git a/i386/i386at/gpl/linux/include/linux/if_arp.h b/i386/i386at/gpl/linux/include/linux/if_arp.h
new file mode 100644
index 00000000..fa350688
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/if_arp.h
@@ -0,0 +1,103 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the ARP (RFC 826) protocol.
+ *
+ * Version: @(#)if_arp.h 1.0.1 04/16/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+ * Portions taken from the KA9Q/NOS (v2.00m PA0GRI) source.
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Florian La Roche.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_ARP_H
+#define _LINUX_IF_ARP_H
+
+/* ARP protocol HARDWARE identifiers. */
+#define ARPHRD_NETROM 0 /* from KA9Q: NET/ROM pseudo */
+#define ARPHRD_ETHER 1 /* Ethernet 10Mbps */
+#define ARPHRD_EETHER 2 /* Experimental Ethernet */
+#define ARPHRD_AX25 3 /* AX.25 Level 2 */
+#define ARPHRD_PRONET 4 /* PROnet token ring */
+#define ARPHRD_CHAOS 5 /* Chaosnet */
+#define ARPHRD_IEEE802 6 /* IEEE 802.2 Ethernet/TR/TB */
+#define ARPHRD_ARCNET 7 /* ARCnet */
+#define ARPHRD_APPLETLK 8 /* APPLEtalk */
+/* Dummy types for non ARP hardware */
+#define ARPHRD_SLIP 256
+#define ARPHRD_CSLIP 257
+#define ARPHRD_SLIP6 258
+#define ARPHRD_CSLIP6 259
+#define ARPHRD_RSRVD 260 /* Notional KISS type */
+#define ARPHRD_ADAPT 264
+#define ARPHRD_PPP 512
+#define ARPHRD_TUNNEL 768 /* IPIP tunnel */
+#define ARPHRD_TUNNEL6 769 /* IPIP6 tunnel */
+#define ARPHRD_FRAD 770 /* Frame Relay */
+#define ARPHRD_SKIP 771 /* SKIP vif */
+#define ARPHRD_LOOPBACK 772 /* Loopback device */
+
+/* ARP protocol opcodes. */
+#define ARPOP_REQUEST 1 /* ARP request */
+#define ARPOP_REPLY 2 /* ARP reply */
+#define ARPOP_RREQUEST 3 /* RARP request */
+#define ARPOP_RREPLY 4 /* RARP reply */
+
+
+/* ARP ioctl request. */
+struct arpreq {
+ struct sockaddr arp_pa; /* protocol address */
+ struct sockaddr arp_ha; /* hardware address */
+ int arp_flags; /* flags */
+ struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
+ char arp_dev[16];
+};
+
+struct arpreq_old {
+ struct sockaddr arp_pa; /* protocol address */
+ struct sockaddr arp_ha; /* hardware address */
+ int arp_flags; /* flags */
+ struct sockaddr arp_netmask; /* netmask (only for proxy arps) */
+};
+
+/* ARP Flag values. */
+#define ATF_COM 0x02 /* completed entry (ha valid) */
+#define ATF_PERM 0x04 /* permanent entry */
+#define ATF_PUBL 0x08 /* publish entry */
+#define ATF_USETRAILERS 0x10 /* has requested trailers */
+#define ATF_NETMASK 0x20 /* want to use a netmask (only
+ for proxy entries) */
+
+/*
+ * This structure defines an ethernet arp header.
+ */
+
+struct arphdr
+{
+ unsigned short ar_hrd; /* format of hardware address */
+ unsigned short ar_pro; /* format of protocol address */
+ unsigned char ar_hln; /* length of hardware address */
+ unsigned char ar_pln; /* length of protocol address */
+ unsigned short ar_op; /* ARP opcode (command) */
+
+#if 0
+ /*
+ * Ethernet looks like this : This bit is variable sized however...
+ */
+ unsigned char ar_sha[ETH_ALEN]; /* sender hardware address */
+ unsigned char ar_sip[4]; /* sender IP address */
+ unsigned char ar_tha[ETH_ALEN]; /* target hardware address */
+ unsigned char ar_tip[4]; /* target IP address */
+#endif
+
+};
+
+#endif /* _LINUX_IF_ARP_H */
diff --git a/i386/i386at/gpl/linux/include/linux/if_ether.h b/i386/i386at/gpl/linux/include/linux/if_ether.h
new file mode 100644
index 00000000..14078472
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/if_ether.h
@@ -0,0 +1,96 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Ethernet IEEE 802.3 interface.
+ *
+ * Version: @(#)if_ether.h 1.0.1a 02/08/94
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Alan Cox, <alan@cymru.net>
+ * Steve Whitehouse, <gw7rrm@eeshack3.swan.ac.uk>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_ETHER_H
+#define _LINUX_IF_ETHER_H
+
+/* IEEE 802.3 Ethernet magic constants. The frame sizes omit the preamble
+ and FCS/CRC (frame check sequence). */
+#define ETH_ALEN 6 /* Octets in one ethernet addr */
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+
+
+/* These are the defined Ethernet Protocol ID's. */
+#define ETH_P_LOOP 0x0060 /* Ethernet Loopback packet */
+#define ETH_P_ECHO 0x0200 /* Ethernet Echo packet */
+#define ETH_P_PUP 0x0400 /* Xerox PUP packet */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_X25 0x0805 /* CCITT X.25 */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#define ETH_P_BPQ 0x08FF /* G8BPQ AX.25 Ethernet Packet [ NOT AN OFFICIALLY REGISTERED ID ] */
+#define ETH_P_DEC 0x6000 /* DEC Assigned proto */
+#define ETH_P_DNA_DL 0x6001 /* DEC DNA Dump/Load */
+#define ETH_P_DNA_RC 0x6002 /* DEC DNA Remote Console */
+#define ETH_P_DNA_RT 0x6003 /* DEC DNA Routing */
+#define ETH_P_LAT 0x6004 /* DEC LAT */
+#define ETH_P_DIAG 0x6005 /* DEC Diagnostics */
+#define ETH_P_CUST 0x6006 /* DEC Customer use */
+#define ETH_P_SCA 0x6007 /* DEC Systems Comms Arch */
+#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
+#define ETH_P_ATALK 0x809B /* Appletalk DDP */
+#define ETH_P_AARP 0x80F3 /* Appletalk AARP */
+#define ETH_P_IPX 0x8137 /* IPX over DIX */
+#define ETH_P_IPV6 0x86DD /* IPv6 over bluebook */
+#define ETH_P_802_3 0x0001 /* Dummy type for 802.3 frames */
+#define ETH_P_AX25 0x0002 /* Dummy protocol id for AX.25 */
+#define ETH_P_ALL 0x0003 /* Every packet (be careful!!!) */
+#define ETH_P_802_2 0x0004 /* 802.2 frames */
+#define ETH_P_SNAP 0x0005 /* Internal only */
+#define ETH_P_DDCMP 0x0006 /* DEC DDCMP: Internal only */
+#define ETH_P_WAN_PPP 0x0007 /* Dummy type for WAN PPP frames*/
+#define ETH_P_PPP_MP 0x0008 /* Dummy type for PPP MP frames */
+
+/* This is an Ethernet frame header. */
+struct ethhdr {
+ unsigned char h_dest[ETH_ALEN]; /* destination eth addr */
+ unsigned char h_source[ETH_ALEN]; /* source ether addr */
+ unsigned short h_proto; /* packet type ID field */
+};
+
+/* Ethernet statistics collection data. */
+struct enet_statistics{
+ int rx_packets; /* total packets received */
+ int tx_packets; /* total packets transmitted */
+ int rx_errors; /* bad packets received */
+ int tx_errors; /* packet transmit problems */
+ int rx_dropped; /* no space in linux buffers */
+ int tx_dropped; /* no space available in linux */
+ int multicast; /* multicast packets received */
+ int collisions;
+
+ /* detailed rx_errors: */
+ int rx_length_errors;
+ int rx_over_errors; /* receiver ring buff overflow */
+ int rx_crc_errors; /* recved pkt with crc error */
+ int rx_frame_errors; /* recv'd frame alignment error */
+ int rx_fifo_errors; /* recv'r fifo overrun */
+ int rx_missed_errors; /* receiver missed packet */
+
+ /* detailed tx_errors */
+ int tx_aborted_errors;
+ int tx_carrier_errors;
+ int tx_fifo_errors;
+ int tx_heartbeat_errors;
+ int tx_window_errors;
+};
+
+#endif /* _LINUX_IF_ETHER_H */
diff --git a/i386/i386at/gpl/linux/include/linux/if_tr.h b/i386/i386at/gpl/linux/include/linux/if_tr.h
new file mode 100644
index 00000000..61629332
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/if_tr.h
@@ -0,0 +1,104 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the Token-Ring IEEE 802.5 interface.
+ *
+ * Version: @(#)if_tr.h 0.0 07/11/94
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald Becker, <becker@super.org>
+ * Peter De Schrijver, <stud11@cc4.kuleuven.ac.be>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IF_TR_H
+#define _LINUX_IF_TR_H
+
+
+/* IEEE 802.5 Token-Ring magic constants. The frame sizes omit the preamble
+ and FCS/CRC (frame check sequence). */
+#define TR_ALEN 6 /* Octets in one ethernet addr */
+#define TR_HLEN (sizeof(struct trh_hdr)+sizeof(struct trllc))
+#define AC 0x10
+#define LLC_FRAME 0x40
+#if 0
+#define ETH_HLEN 14 /* Total octets in header. */
+#define ETH_ZLEN 60 /* Min. octets in frame sans FCS */
+#define ETH_DATA_LEN 1500 /* Max. octets in payload */
+#define ETH_FRAME_LEN 1514 /* Max. octets in frame sans FCS */
+#endif
+
+
+/* These are some defined Ethernet Protocol ID's. */
+#define ETH_P_IP 0x0800 /* Internet Protocol packet */
+#define ETH_P_ARP 0x0806 /* Address Resolution packet */
+#define ETH_P_RARP 0x8035 /* Reverse Addr Res packet */
+
+/* LLC and SNAP constants */
+#define EXTENDED_SAP 0xAA
+#define UI_CMD 0x03
+
+/* This is an Token-Ring frame header. */
+struct trh_hdr {
+ unsigned char ac; /* access control field */
+ unsigned char fc; /* frame control field */
+ unsigned char daddr[TR_ALEN]; /* destination address */
+ unsigned char saddr[TR_ALEN]; /* source address */
+ unsigned short rcf; /* route control field */
+ unsigned short rseg[8];/* routing registers */
+};
+
+/* This is an Token-Ring LLC structure */
+struct trllc {
+ unsigned char dsap; /* destination SAP */
+ unsigned char ssap; /* source SAP */
+ unsigned char llc; /* LLC control field */
+ unsigned char protid[3]; /* protocol id */
+ unsigned short ethertype; /* ether type field */
+};
+
+
+/* Token-Ring statistics collection data. */
+struct tr_statistics{
+ int rx_packets; /* total packets received */
+ int tx_packets; /* total packets transmitted */
+ int rx_errors; /* bad packets received */
+ int tx_errors; /* packet transmit problems */
+ int rx_dropped; /* no space in linux buffers */
+ int tx_dropped; /* no space available in linux */
+ int multicast; /* multicast packets received */
+ int transmit_collision;
+
+ /* detailed Token-Ring errors. See IBM Token-Ring Network Architecture
+ for more info */
+
+ int line_errors;
+ int internal_errors;
+ int burst_errors;
+ int A_C_errors;
+ int abort_delimiters;
+ int lost_frames;
+ int recv_congest_count;
+ int frame_copied_errors;
+ int frequency_errors;
+ int token_errors;
+ int dummy1;
+
+};
+
+/* source routing stuff */
+
+#define TR_RII 0x80
+#define TR_RCF_DIR_BIT 0x80
+#define TR_RCF_LEN_MASK 0x1f00
+#define TR_RCF_BROADCAST 0x8000
+#define TR_RCF_LIMITED_BROADCAST 0xA000
+#define TR_RCF_FRAME2K 0x20
+#define TR_RCF_BROADCAST_MASK 0xC000
+
+#endif /* _LINUX_IF_TR_H */
diff --git a/i386/i386at/gpl/linux/include/linux/igmp.h b/i386/i386at/gpl/linux/include/linux/igmp.h
new file mode 100644
index 00000000..161528f1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/igmp.h
@@ -0,0 +1,117 @@
+/*
+ * Linux NET3: Internet Gateway Management Protocol [IGMP]
+ *
+ * Authors:
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ * Extended to talk the BSD extended IGMP protocol of mrouted 3.6
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_IGMP_H
+#define _LINUX_IGMP_H
+
+/*
+ * IGMP protocol structures
+ */
+
+/*
+ * Header in on cable format
+ */
+
+struct igmphdr
+{
+ __u8 type;
+ __u8 code; /* For newer IGMP */
+ __u16 csum;
+ __u32 group;
+};
+
+#define IGMP_HOST_MEMBERSHIP_QUERY 0x11 /* From RFC1112 */
+#define IGMP_HOST_MEMBERSHIP_REPORT 0x12 /* Ditto */
+#define IGMP_DVMRP 0x13 /* DVMRP routing */
+#define IGMP_PIM 0x14 /* PIM routing */
+#define IGMP_HOST_NEW_MEMBERSHIP_REPORT 0x16 /* New version of 0x11 */
+#define IGMP_HOST_LEAVE_MESSAGE 0x17 /* An extra BSD seems to send */
+
+#define IGMP_MTRACE_RESP 0x1e
+#define IGMP_MTRACE 0x1f
+
+
+/*
+ * Use the BSD names for these for compatibility
+ */
+
+#define IGMP_DELAYING_MEMBER 0x01
+#define IGMP_IDLE_MEMBER 0x02
+#define IGMP_LAZY_MEMBER 0x03
+#define IGMP_SLEEPING_MEMBER 0x04
+#define IGMP_AWAKENING_MEMBER 0x05
+
+#define IGMP_OLD_ROUTER 0x00
+#define IGMP_NEW_ROUTER 0x01
+
+#define IGMP_MINLEN 8
+
+#define IGMP_MAX_HOST_REPORT_DELAY 10 /* max delay for response to */
+ /* query (in seconds) */
+
+#define IGMP_TIMER_SCALE 10 /* denotes that the igmphdr->timer field */
+ /* specifies time in 10th of seconds */
+
+#define IGMP_AGE_THRESHOLD 540 /* If this host don't hear any IGMP V1 */
+ /* message in this period of time, */
+ /* revert to IGMP v2 router. */
+
+#define IGMP_ALL_HOSTS htonl(0xE0000001L)
+#define IGMP_ALL_ROUTER htonl(0xE0000002L)
+#define IGMP_LOCAL_GROUP htonl(0xE0000000L)
+#define IGMP_LOCAL_GROUP_MASK htonl(0xFFFFFF00L)
+
+/*
+ * struct for keeping the multicast list in
+ */
+
+#ifdef __KERNEL__
+struct ip_mc_socklist
+{
+ unsigned long multiaddr[IP_MAX_MEMBERSHIPS]; /* This is a speed trade off */
+ struct device *multidev[IP_MAX_MEMBERSHIPS];
+};
+
+struct ip_mc_list
+{
+ struct device *interface;
+ unsigned long multiaddr;
+ struct ip_mc_list *next;
+ struct timer_list timer;
+ int tm_running;
+ int users;
+};
+
+struct ip_router_info
+{
+ struct device *dev;
+ int type; /* type of router which is querier on this interface */
+ int time; /* # of slow timeouts since last old query */
+ struct timer_list timer;
+ struct ip_router_info *next;
+};
+
+extern struct ip_mc_list *ip_mc_head;
+
+
+extern int igmp_rcv(struct sk_buff *, struct device *, struct options *, __u32, unsigned short,
+ __u32, int , struct inet_protocol *);
+extern void ip_mc_drop_device(struct device *dev);
+extern int ip_mc_join_group(struct sock *sk, struct device *dev, unsigned long addr);
+extern int ip_mc_leave_group(struct sock *sk, struct device *dev,unsigned long addr);
+extern void ip_mc_drop_socket(struct sock *sk);
+extern void ip_mr_init(void);
+#endif
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/in.h b/i386/i386at/gpl/linux/include/linux/in.h
new file mode 100644
index 00000000..c8e156e8
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/in.h
@@ -0,0 +1,149 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions of the Internet Protocol.
+ *
+ * Version: @(#)in.h 1.0.1 04/21/93
+ *
+ * Authors: Original taken from the GNU Project <netinet/in.h> file.
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IN_H
+#define _LINUX_IN_H
+
+#include <linux/types.h>
+
+/* Standard well-defined IP protocols. */
+enum {
+ IPPROTO_IP = 0, /* Dummy protocol for TCP */
+ IPPROTO_ICMP = 1, /* Internet Control Message Protocol */
+ IPPROTO_IGMP = 2, /* Internet Gateway Management Protocol */
+ IPPROTO_IPIP = 4, /* IPIP tunnels (older KA9Q tunnels use 94) */
+ IPPROTO_TCP = 6, /* Transmission Control Protocol */
+ IPPROTO_EGP = 8, /* Exterior Gateway Protocol */
+ IPPROTO_PUP = 12, /* PUP protocol */
+ IPPROTO_UDP = 17, /* User Datagram Protocol */
+ IPPROTO_IDP = 22, /* XNS IDP protocol */
+
+ IPPROTO_RAW = 255, /* Raw IP packets */
+ IPPROTO_MAX
+};
+
+
+/* Internet address. */
+struct in_addr {
+ __u32 s_addr;
+};
+
+/* Request struct for multicast socket ops */
+
+struct ip_mreq
+{
+ struct in_addr imr_multiaddr; /* IP multicast address of group */
+ struct in_addr imr_interface; /* local IP address of interface */
+};
+
+
+/* Structure describing an Internet (IP) socket address. */
+#define __SOCK_SIZE__ 16 /* sizeof(struct sockaddr) */
+struct sockaddr_in {
+ short int sin_family; /* Address family */
+ unsigned short int sin_port; /* Port number */
+ struct in_addr sin_addr; /* Internet address */
+
+ /* Pad to size of `struct sockaddr'. */
+ unsigned char __pad[__SOCK_SIZE__ - sizeof(short int) -
+ sizeof(unsigned short int) - sizeof(struct in_addr)];
+};
+#define sin_zero __pad /* for BSD UNIX comp. -FvK */
+
+
+/*
+ * Definitions of the bits in an Internet address integer.
+ * On subnets, host and network parts are found according
+ * to the subnet mask, not these masks.
+ */
+#define IN_CLASSA(a) ((((long int) (a)) & 0x80000000) == 0)
+#define IN_CLASSA_NET 0xff000000
+#define IN_CLASSA_NSHIFT 24
+#define IN_CLASSA_HOST (0xffffffff & ~IN_CLASSA_NET)
+#define IN_CLASSA_MAX 128
+
+#define IN_CLASSB(a) ((((long int) (a)) & 0xc0000000) == 0x80000000)
+#define IN_CLASSB_NET 0xffff0000
+#define IN_CLASSB_NSHIFT 16
+#define IN_CLASSB_HOST (0xffffffff & ~IN_CLASSB_NET)
+#define IN_CLASSB_MAX 65536
+
+#define IN_CLASSC(a) ((((long int) (a)) & 0xe0000000) == 0xc0000000)
+#define IN_CLASSC_NET 0xffffff00
+#define IN_CLASSC_NSHIFT 8
+#define IN_CLASSC_HOST (0xffffffff & ~IN_CLASSC_NET)
+
+#define IN_CLASSD(a) ((((long int) (a)) & 0xf0000000) == 0xe0000000)
+#define IN_MULTICAST(a) IN_CLASSD(a)
+#define IN_MULTICAST_NET 0xF0000000
+
+#define IN_EXPERIMENTAL(a) ((((long int) (a)) & 0xe0000000) == 0xe0000000)
+#define IN_BADCLASS(a) ((((long int) (a)) & 0xf0000000) == 0xf0000000)
+
+/* Address to accept any incoming messages. */
+#define INADDR_ANY ((unsigned long int) 0x00000000)
+
+/* Address to send to all hosts. */
+#define INADDR_BROADCAST ((unsigned long int) 0xffffffff)
+
+/* Address indicating an error return. */
+#define INADDR_NONE 0xffffffff
+
+/* Network number for local host loopback. */
+#define IN_LOOPBACKNET 127
+
+/* Address to loopback in software to local host. */
+#define INADDR_LOOPBACK 0x7f000001 /* 127.0.0.1 */
+#define IN_LOOPBACK(a) ((((long int) (a)) & 0xff000000) == 0x7f000000)
+
+/* Defines for Multicast INADDR */
+#define INADDR_UNSPEC_GROUP 0xe0000000 /* 224.0.0.0 */
+#define INADDR_ALLHOSTS_GROUP 0xe0000001 /* 224.0.0.1 */
+#define INADDR_MAX_LOCAL_GROUP 0xe00000ff /* 224.0.0.255 */
+
+/* <asm/byteorder.h> contains the htonl type stuff.. */
+
+#include <asm/byteorder.h>
+
+/* Some random defines to make it easier in the kernel.. */
+#ifdef __KERNEL__
+
+#define LOOPBACK(x) (((x) & htonl(0xff000000)) == htonl(0x7f000000))
+#define MULTICAST(x) (((x) & htonl(0xf0000000)) == htonl(0xe0000000))
+
+#endif
+
+/*
+ * IPv6 definitions as we start to include them. This is just
+ * a beginning dont get excited 8)
+ */
+
+struct in_addr6
+{
+ unsigned char s6_addr[16];
+};
+
+struct sockaddr_in6
+{
+ unsigned short sin6_family;
+ unsigned short sin6_port;
+ unsigned long sin6_flowinfo;
+ struct in_addr6 sin6_addr;
+};
+
+
+#endif /* _LINUX_IN_H */
diff --git a/i386/i386at/gpl/linux/include/linux/inet.h b/i386/i386at/gpl/linux/include/linux/inet.h
new file mode 100644
index 00000000..9ecc9cb3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/inet.h
@@ -0,0 +1,52 @@
+/*
+ * Swansea University Computer Society NET3
+ *
+ * This work is derived from NET2Debugged, which is in turn derived
+ * from NET2D which was written by:
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This work was derived from Ross Biro's inspirational work
+ * for the LINUX operating system. His version numbers were:
+ *
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ * $Id: inet.h,v 1.1.1.1 1997/02/25 21:27:28 thomas Exp $
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_INET_H
+#define _LINUX_INET_H
+
+#ifdef __KERNEL__
+
+extern void inet_proto_init(struct net_proto *pro);
+extern char *in_ntoa(unsigned long in);
+extern unsigned long in_aton(const char *str);
+
+#endif
+#endif /* _LINUX_INET_H */
diff --git a/i386/i386at/gpl/linux/include/linux/interrupt.h b/i386/i386at/gpl/linux/include/linux/interrupt.h
new file mode 100644
index 00000000..a20cbe8e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/interrupt.h
@@ -0,0 +1,91 @@
+/* interrupt.h */
+#ifndef _LINUX_INTERRUPT_H
+#define _LINUX_INTERRUPT_H
+
+#include <linux/kernel.h>
+#include <asm/bitops.h>
+
+struct bh_struct {
+ void (*routine)(void *);
+ void *data;
+};
+
+extern unsigned long bh_active;
+extern unsigned long bh_mask;
+extern struct bh_struct bh_base[32];
+
+asmlinkage void do_bottom_half(void);
+
+/* Who gets which entry in bh_base. Things which will occur most often
+ should come first - in which case NET should be up the top with SERIAL/TQUEUE! */
+
+enum {
+ TIMER_BH = 0,
+ CONSOLE_BH,
+ TQUEUE_BH,
+ SERIAL_BH,
+ NET_BH,
+ IMMEDIATE_BH,
+ KEYBOARD_BH,
+ CYCLADES_BH,
+ CM206_BH
+};
+
+extern inline void mark_bh(int nr)
+{
+ set_bit(nr, &bh_active);
+}
+
+extern inline void disable_bh(int nr)
+{
+ clear_bit(nr, &bh_mask);
+}
+
+extern inline void enable_bh(int nr)
+{
+ set_bit(nr, &bh_mask);
+}
+
+extern inline void start_bh_atomic(void)
+{
+ intr_count++;
+ barrier();
+}
+
+extern inline void end_bh_atomic(void)
+{
+ barrier();
+ intr_count--;
+}
+
+/*
+ * Autoprobing for irqs:
+ *
+ * probe_irq_on() and probe_irq_off() provide robust primitives
+ * for accurate IRQ probing during kernel initialization. They are
+ * reasonably simple to use, are not "fooled" by spurious interrupts,
+ * and, unlike other attempts at IRQ probing, they do not get hung on
+ * stuck interrupts (such as unused PS2 mouse interfaces on ASUS boards).
+ *
+ * For reasonably foolproof probing, use them as follows:
+ *
+ * 1. clear and/or mask the device's internal interrupt.
+ * 2. sti();
+ * 3. irqs = probe_irq_on(); // "take over" all unassigned idle IRQs
+ * 4. enable the device and cause it to trigger an interrupt.
+ * 5. wait for the device to interrupt, using non-intrusive polling or a delay.
+ * 6. irq = probe_irq_off(irqs); // get IRQ number, 0=none, negative=multiple
+ * 7. service the device to clear its pending interrupt.
+ * 8. loop again if paranoia is required.
+ *
+ * probe_irq_on() returns a mask of allocated irq's.
+ *
+ * probe_irq_off() takes the mask as a parameter,
+ * and returns the irq number which occurred,
+ * or zero if none occurred, or a negative irq number
+ * if more than one irq occurred.
+ */
+extern unsigned long probe_irq_on(void); /* returns 0 on failure */
+extern int probe_irq_off(unsigned long); /* returns 0 or negative on failure */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/ioctl.h b/i386/i386at/gpl/linux/include/linux/ioctl.h
new file mode 100644
index 00000000..aa91eb39
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/ioctl.h
@@ -0,0 +1,7 @@
+#ifndef _LINUX_IOCTL_H
+#define _LINUX_IOCTL_H
+
+#include <asm/ioctl.h>
+
+#endif /* _LINUX_IOCTL_H */
+
diff --git a/i386/i386at/gpl/linux/include/linux/ioport.h b/i386/i386at/gpl/linux/include/linux/ioport.h
new file mode 100644
index 00000000..335e3b65
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/ioport.h
@@ -0,0 +1,31 @@
+/*
+ * portio.h Definitions of routines for detecting, reserving and
+ * allocating system resources.
+ *
+ * Version: 0.01 8/30/93
+ *
+ * Author: Donald Becker (becker@super.org)
+ */
+
+#ifndef _LINUX_PORTIO_H
+#define _LINUX_PORTIO_H
+
+#define HAVE_PORTRESERVE
+/*
+ * Call check_region() before probing for your hardware.
+ * Once you have found you hardware, register it with request_region().
+ * If you unload the driver, use release_region to free ports.
+ */
+extern void reserve_setup(char *str, int *ints);
+extern int check_region(unsigned int from, unsigned int extent);
+extern void request_region(unsigned int from, unsigned int extent,const char *name);
+extern void release_region(unsigned int from, unsigned int extent);
+extern int get_ioport_list(char *);
+
+
+#define HAVE_AUTOIRQ
+extern void *irq2dev_map[16]; /* Use only if you own the IRQ. */
+extern int autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+
+#endif /* _LINUX_PORTIO_H */
diff --git a/i386/i386at/gpl/linux/include/linux/ip.h b/i386/i386at/gpl/linux/include/linux/ip.h
new file mode 100644
index 00000000..4d5d70c1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/ip.h
@@ -0,0 +1,113 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP protocol.
+ *
+ * Version: @(#)ip.h 1.0.2 04/28/93
+ *
+ * Authors: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_IP_H
+#define _LINUX_IP_H
+#include <asm/byteorder.h>
+
+#define IPOPT_END 0
+#define IPOPT_NOOP 1
+#define IPOPT_SEC 130
+#define IPOPT_LSRR 131
+#define IPOPT_SSRR 137
+#define IPOPT_RR 7
+#define IPOPT_SID 136
+#define IPOPT_TIMESTAMP 68
+
+
+#define MAXTTL 255
+
+struct timestamp {
+ __u8 len;
+ __u8 ptr;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 flags:4,
+ overflow:4;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u8 overflow:4,
+ flags:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u32 data[9];
+};
+
+
+#define MAX_ROUTE 16
+
+struct route {
+ char route_size;
+ char pointer;
+ unsigned long route[MAX_ROUTE];
+};
+
+#define IPOPT_OPTVAL 0
+#define IPOPT_OLEN 1
+#define IPOPT_OFFSET 2
+#define IPOPT_MINOFF 4
+#define MAX_IPOPTLEN 40
+#define IPOPT_NOP IPOPT_NOOP
+#define IPOPT_EOL IPOPT_END
+#define IPOPT_TS IPOPT_TIMESTAMP
+
+#define IPOPT_TS_TSONLY 0 /* timestamps only */
+#define IPOPT_TS_TSANDADDR 1 /* timestamps and addresses */
+#define IPOPT_TS_PRESPEC 2 /* specified modules only */
+
+struct options {
+ __u32 faddr; /* Saved first hop address */
+ unsigned char optlen;
+ unsigned char srr;
+ unsigned char rr;
+ unsigned char ts;
+ unsigned char is_setbyuser:1, /* Set by setsockopt? */
+ is_data:1, /* Options in __data, rather than skb */
+ is_strictroute:1, /* Strict source route */
+ srr_is_hit:1, /* Packet destination addr was our one */
+ is_changed:1, /* IP checksum more not valid */
+ rr_needaddr:1, /* Need to record addr of outgoing dev */
+ ts_needtime:1, /* Need to record timestamp */
+ ts_needaddr:1; /* Need to record addr of outgoing dev */
+ unsigned char __pad1;
+ unsigned char __pad2;
+ unsigned char __pad3;
+ unsigned char __data[0];
+};
+
+struct iphdr {
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u8 ihl:4,
+ version:4;
+#elif defined (__BIG_ENDIAN_BITFIELD)
+ __u8 version:4,
+ ihl:4;
+#else
+#error "Please fix <asm/byteorder.h>"
+#endif
+ __u8 tos;
+ __u16 tot_len;
+ __u16 id;
+ __u16 frag_off;
+ __u8 ttl;
+ __u8 protocol;
+ __u16 check;
+ __u32 saddr;
+ __u32 daddr;
+ /*The options start here. */
+};
+
+
+#endif /* _LINUX_IP_H */
diff --git a/i386/i386at/gpl/linux/include/linux/ipc.h b/i386/i386at/gpl/linux/include/linux/ipc.h
new file mode 100644
index 00000000..3878e020
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/ipc.h
@@ -0,0 +1,67 @@
+#ifndef _LINUX_IPC_H
+#define _LINUX_IPC_H
+#include <linux/types.h>
+
+typedef int key_t; /* should go in <types.h> type for IPC key */
+#define IPC_PRIVATE ((key_t) 0)
+
+struct ipc_perm
+{
+ key_t key;
+ ushort uid; /* owner euid and egid */
+ ushort gid;
+ ushort cuid; /* creator euid and egid */
+ ushort cgid;
+ ushort mode; /* access modes see mode flags below */
+ ushort seq; /* sequence number */
+};
+
+
+/* resource get request flags */
+#define IPC_CREAT 00001000 /* create if key is nonexistent */
+#define IPC_EXCL 00002000 /* fail if key exists */
+#define IPC_NOWAIT 00004000 /* return error on wait */
+
+
+/*
+ * Control commands used with semctl, msgctl and shmctl
+ * see also specific commands in sem.h, msg.h and shm.h
+ */
+#define IPC_RMID 0 /* remove resource */
+#define IPC_SET 1 /* set ipc_perm options */
+#define IPC_STAT 2 /* get ipc_perm options */
+#define IPC_INFO 3 /* see ipcs */
+
+#ifdef __KERNEL__
+
+/* special shmsegs[id], msgque[id] or semary[id] values */
+#define IPC_UNUSED ((void *) -1)
+#define IPC_NOID ((void *) -2) /* being allocated/destroyed */
+
+/*
+ * These are used to wrap system calls. See ipc/util.c.
+ */
+struct ipc_kludge {
+ struct msgbuf *msgp;
+ long msgtyp;
+};
+
+#define SEMOP 1
+#define SEMGET 2
+#define SEMCTL 3
+#define MSGSND 11
+#define MSGRCV 12
+#define MSGGET 13
+#define MSGCTL 14
+#define SHMAT 21
+#define SHMDT 22
+#define SHMGET 23
+#define SHMCTL 24
+
+#define IPCCALL(version,op) ((version)<<16 | (op))
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_IPC_H */
+
+
diff --git a/i386/i386at/gpl/linux/include/linux/kdev_t.h b/i386/i386at/gpl/linux/include/linux/kdev_t.h
new file mode 100644
index 00000000..0497ea8c
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/kdev_t.h
@@ -0,0 +1,114 @@
+#ifndef _LINUX_KDEV_T_H
+#define _LINUX_KDEV_T_H
+#ifdef __KERNEL__
+/*
+As a preparation for the introduction of larger device numbers,
+we introduce a type kdev_t to hold them. No information about
+this type is known outside of this include file.
+
+Objects of type kdev_t designate a device. Outside of the kernel
+the corresponding things are objects of type dev_t - usually an
+integral type with the device major and minor in the high and low
+bits, respectively. Conversion is done by
+
+extern kdev_t to_kdev_t(int);
+
+It is up to the various file systems to decide how objects of type
+dev_t are stored on disk.
+The only other point of contact between kernel and outside world
+are the system calls stat and mknod, new versions of which will
+eventually have to be used in libc.
+
+[Unfortunately, the floppy control ioctls fail to hide the internal
+kernel structures, and the fd_device field of a struct floppy_drive_struct
+is user-visible. So, it remains a dev_t for the moment, with some ugly
+conversions in floppy.c.]
+
+Inside the kernel, we aim for a kdev_t type that is a pointer
+to a structure with information about the device (like major,
+minor, size, blocksize, sectorsize, name, read-only flag,
+struct file_operations etc.).
+
+However, for the time being we let kdev_t be almost the same as dev_t:
+
+typedef struct { unsigned short major, minor; } kdev_t;
+
+Admissible operations on an object of type kdev_t:
+- passing it along
+- comparing it for equality with another such object
+- storing it in ROOT_DEV, inode->i_dev, inode->i_rdev, sb->s_dev,
+ bh->b_dev, req->rq_dev, de->dc_dev, tty->device
+- using its bit pattern as argument in a hash function
+- finding its major and minor
+- complaining about it
+
+An object of type kdev_t is created only by the function MKDEV(),
+with the single exception of the constant 0 (no device).
+
+Right now the other information mentioned above is usually found
+in static arrays indexed by major or major,minor.
+
+An obstacle to immediately using
+ typedef struct { ... (* lots of information *) } *kdev_t
+is the case of mknod used to create a block device that the
+kernel doesn't know about at present (but first learns about
+when some module is inserted).
+
+aeb - 950811
+*/
+
+/* Since MINOR(dev) is used as index in static arrays,
+ the kernel is not quite ready yet for larger minors.
+ However, everything runs fine with an arbitrary kdev_t type. */
+
+#define MINORBITS 8
+#define MINORMASK ((1<<MINORBITS) - 1)
+
+typedef unsigned short kdev_t;
+
+#define MAJOR(dev) ((dev) >> MINORBITS)
+#define MINOR(dev) ((dev) & MINORMASK)
+#define HASHDEV(dev) (dev)
+#define NODEV 0
+#define MKDEV(ma,mi) (((ma) << MINORBITS) | (mi))
+#define B_FREE 0xffff /* yuk */
+
+extern char * kdevname(kdev_t); /* note: returns pointer to static data! */
+
+/*
+As long as device numbers in the outside world have 16 bits only,
+we use these conversions.
+*/
+
+static inline unsigned int kdev_t_to_nr(kdev_t dev) {
+ return (MAJOR(dev)<<8) | MINOR(dev);
+}
+
+static inline kdev_t to_kdev_t(int dev)
+{
+ int major, minor;
+#if 0
+ major = (dev >> 16);
+ if (!major) {
+ major = (dev >> 8);
+ minor = (dev & 0xff);
+ } else
+ minor = (dev & 0xffff);
+#else
+ major = (dev >> 8);
+ minor = (dev & 0xff);
+#endif
+ return MKDEV(major, minor);
+}
+
+#else /* __KERNEL__ */
+
+/*
+Some programs want their definitions of MAJOR and MINOR and MKDEV
+from the kernel sources. These must be the externally visible ones.
+*/
+#define MAJOR(dev) ((dev)>>8)
+#define MINOR(dev) ((dev) & 0xff)
+#define MKDEV(ma,mi) ((ma)<<8 | (mi))
+#endif /* __KERNEL__ */
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/kernel.h b/i386/i386at/gpl/linux/include/linux/kernel.h
new file mode 100644
index 00000000..d4985576
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/kernel.h
@@ -0,0 +1,94 @@
+#ifndef _LINUX_KERNEL_H
+#define _LINUX_KERNEL_H
+
+/*
+ * 'kernel.h' contains some often-used function prototypes etc
+ */
+
+#ifdef __KERNEL__
+
+#include <stdarg.h>
+#include <linux/linkage.h>
+
+#define INT_MAX ((int)(~0U>>1))
+#define UINT_MAX (~0U)
+#define LONG_MAX ((long)(~0UL>>1))
+#define ULONG_MAX (~0UL)
+
+#define STACK_MAGIC 0xdeadbeef
+
+#define KERN_EMERG "<0>" /* system is unusable */
+#define KERN_ALERT "<1>" /* action must be taken immediately */
+#define KERN_CRIT "<2>" /* critical conditions */
+#define KERN_ERR "<3>" /* error conditions */
+#define KERN_WARNING "<4>" /* warning conditions */
+#define KERN_NOTICE "<5>" /* normal but significant condition */
+#define KERN_INFO "<6>" /* informational */
+#define KERN_DEBUG "<7>" /* debug-level messages */
+
+#if __GNUC__ < 2 || (__GNUC__ == 2 && __GNUC_MINOR__ < 5)
+# define NORET_TYPE __volatile__
+# define ATTRIB_NORET /**/
+# define NORET_AND /**/
+#else
+# define NORET_TYPE /**/
+# define ATTRIB_NORET __attribute__((noreturn))
+# define NORET_AND noreturn,
+#endif
+
+extern void math_error(void);
+NORET_TYPE void panic(const char * fmt, ...)
+ __attribute__ ((NORET_AND format (printf, 1, 2)));
+NORET_TYPE void do_exit(long error_code)
+ ATTRIB_NORET;
+extern unsigned long simple_strtoul(const char *,char **,unsigned int);
+extern int linux_sprintf(char * buf, const char * fmt, ...);
+extern int linux_vsprintf(char *buf, const char *, va_list);
+#ifndef MACH_INCLUDE
+#define sprintf linux_sprintf
+#define vsprintf linux_vsprintf
+#endif
+
+extern int session_of_pgrp(int pgrp);
+
+extern int kill_proc(int pid, int sig, int priv);
+extern int kill_pg(int pgrp, int sig, int priv);
+extern int kill_sl(int sess, int sig, int priv);
+
+asmlinkage int printk(const char * fmt, ...)
+ __attribute__ ((format (printf, 1, 2)));
+
+/*
+ * This is defined as a macro, but at some point this might become a
+ * real subroutine that sets a flag if it returns true (to do
+ * BSD-style accounting where the process is flagged if it uses root
+ * privs). The implication of this is that you should do normal
+ * permissions checks first, and check suser() last.
+ *
+ * "suser()" checks against the effective user id, while "fsuser()"
+ * is used for file permission checking and checks against the fsuid..
+ */
+#ifdef MACH
+#define suser() 1
+#else
+#define suser() (current->euid == 0)
+#endif
+#define fsuser() (current->fsuid == 0)
+
+#endif /* __KERNEL__ */
+
+#define SI_LOAD_SHIFT 16
+struct sysinfo {
+ long uptime; /* Seconds since boot */
+ unsigned long loads[3]; /* 1, 5, and 15 minute load averages */
+ unsigned long totalram; /* Total usable main memory size */
+ unsigned long freeram; /* Available memory size */
+ unsigned long sharedram; /* Amount of shared memory */
+ unsigned long bufferram; /* Memory used by buffers */
+ unsigned long totalswap; /* Total swap space size */
+ unsigned long freeswap; /* swap space still available */
+ unsigned short procs; /* Number of current processes */
+ char _f[22]; /* Pads structure to 64 bytes */
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/kernel_stat.h b/i386/i386at/gpl/linux/include/linux/kernel_stat.h
new file mode 100644
index 00000000..1966490a
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/kernel_stat.h
@@ -0,0 +1,32 @@
+#ifndef _LINUX_KERNEL_STAT_H
+#define _LINUX_KERNEL_STAT_H
+
+#include <asm/irq.h>
+
+/*
+ * 'kernel_stat.h' contains the definitions needed for doing
+ * some kernel statistics (cpu usage, context switches ...),
+ * used by rstatd/perfmeter
+ */
+
+#define DK_NDRIVE 4
+
+struct kernel_stat {
+ unsigned int cpu_user, cpu_nice, cpu_system;
+ unsigned int dk_drive[DK_NDRIVE];
+ unsigned int dk_drive_rio[DK_NDRIVE];
+ unsigned int dk_drive_wio[DK_NDRIVE];
+ unsigned int dk_drive_rblk[DK_NDRIVE];
+ unsigned int dk_drive_wblk[DK_NDRIVE];
+ unsigned int pgpgin, pgpgout;
+ unsigned int pswpin, pswpout;
+ unsigned int interrupts[NR_IRQS];
+ unsigned int ipackets, opackets;
+ unsigned int ierrors, oerrors;
+ unsigned int collisions;
+ unsigned int context_swtch;
+};
+
+extern struct kernel_stat kstat;
+
+#endif /* _LINUX_KERNEL_STAT_H */
diff --git a/i386/i386at/gpl/linux/include/linux/limits.h b/i386/i386at/gpl/linux/include/linux/limits.h
new file mode 100644
index 00000000..d0f300c4
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/limits.h
@@ -0,0 +1,17 @@
+#ifndef _LINUX_LIMITS_H
+#define _LINUX_LIMITS_H
+
+#define NR_OPEN 256
+
+#define NGROUPS_MAX 32 /* supplemental group IDs are available */
+#define ARG_MAX 131072 /* # bytes of args + environ for exec() */
+#define CHILD_MAX 999 /* no limit :-) */
+#define OPEN_MAX 256 /* # open files a process may have */
+#define LINK_MAX 127 /* # links a file may have */
+#define MAX_CANON 255 /* size of the canonical input queue */
+#define MAX_INPUT 255 /* size of the type-ahead buffer */
+#define NAME_MAX 255 /* # chars in a file name */
+#define PATH_MAX 1024 /* # chars in a path name */
+#define PIPE_BUF 4096 /* # bytes in atomic write to a pipe */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/linkage.h b/i386/i386at/gpl/linux/include/linux/linkage.h
new file mode 100644
index 00000000..c8a7a491
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/linkage.h
@@ -0,0 +1,59 @@
+#ifndef _LINUX_LINKAGE_H
+#define _LINUX_LINKAGE_H
+
+#ifdef __cplusplus
+#define asmlinkage extern "C"
+#else
+#define asmlinkage
+#endif
+
+#ifdef __ELF__
+#define SYMBOL_NAME_STR(X) #X
+#define SYMBOL_NAME(X) X
+#ifdef __STDC__
+#define SYMBOL_NAME_LABEL(X) X##:
+#else
+#define SYMBOL_NAME_LABEL(X) X/**/:
+#endif
+#else
+#define SYMBOL_NAME_STR(X) "_"#X
+#ifdef __STDC__
+#define SYMBOL_NAME(X) _##X
+#define SYMBOL_NAME_LABEL(X) _##X##:
+#else
+#define SYMBOL_NAME(X) _/**/X
+#define SYMBOL_NAME_LABEL(X) _/**/X/**/:
+#endif
+#endif
+
+#if !defined(__i486__) && !defined(__i586__)
+#ifdef __ELF__
+#define __ALIGN .align 4,0x90
+#define __ALIGN_STR ".align 4,0x90"
+#else /* __ELF__ */
+#define __ALIGN .align 2,0x90
+#define __ALIGN_STR ".align 2,0x90"
+#endif /* __ELF__ */
+#else /* __i486__/__i586__ */
+#ifdef __ELF__
+#define __ALIGN .align 16,0x90
+#define __ALIGN_STR ".align 16,0x90"
+#else /* __ELF__ */
+#define __ALIGN .align 4,0x90
+#define __ALIGN_STR ".align 4,0x90"
+#endif /* __ELF__ */
+#endif /* __i486__/__i586__ */
+
+#ifdef __ASSEMBLY__
+
+#define ALIGN __ALIGN
+#define ALIGN_STRING __ALIGN_STRING
+
+#define ENTRY(name) \
+ .globl SYMBOL_NAME(name); \
+ ALIGN; \
+ SYMBOL_NAME_LABEL(name)
+
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/locks.h b/i386/i386at/gpl/linux/include/linux/locks.h
new file mode 100644
index 00000000..c3202b08
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/locks.h
@@ -0,0 +1,66 @@
+#ifndef _LINUX_LOCKS_H
+#define _LINUX_LOCKS_H
+
+#ifndef _LINUX_MM_H
+#include <linux/mm.h>
+#endif
+#ifndef _LINUX_PAGEMAP_H
+#include <linux/pagemap.h>
+#endif
+
+/*
+ * Unlocked, temporary IO buffer_heads gets moved to the reuse_list
+ * once their page becomes unlocked.
+ */
+extern struct buffer_head *reuse_list;
+
+/*
+ * Buffer cache locking - note that interrupts may only unlock, not
+ * lock buffers.
+ */
+extern void __wait_on_buffer(struct buffer_head *);
+
+extern inline void wait_on_buffer(struct buffer_head * bh)
+{
+ if (test_bit(BH_Lock, &bh->b_state))
+ __wait_on_buffer(bh);
+}
+
+extern inline void lock_buffer(struct buffer_head * bh)
+{
+ if (set_bit(BH_Lock, &bh->b_state))
+ __wait_on_buffer(bh);
+}
+
+void unlock_buffer(struct buffer_head *);
+
+#ifndef MACH
+/*
+ * super-block locking. Again, interrupts may only unlock
+ * a super-block (although even this isn't done right now.
+ * nfs may need it).
+ */
+extern void __wait_on_super(struct super_block *);
+
+extern inline void wait_on_super(struct super_block * sb)
+{
+ if (sb->s_lock)
+ __wait_on_super(sb);
+}
+
+extern inline void lock_super(struct super_block * sb)
+{
+ if (sb->s_lock)
+ __wait_on_super(sb);
+ sb->s_lock = 1;
+}
+
+extern inline void unlock_super(struct super_block * sb)
+{
+ sb->s_lock = 0;
+ wake_up(&sb->s_wait);
+}
+#endif /* ! MACH */
+
+#endif /* _LINUX_LOCKS_H */
+
diff --git a/i386/i386at/gpl/linux/include/linux/major.h b/i386/i386at/gpl/linux/include/linux/major.h
new file mode 100644
index 00000000..c1b2dcf0
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/major.h
@@ -0,0 +1,119 @@
+#ifndef _LINUX_MAJOR_H
+#define _LINUX_MAJOR_H
+
+/*
+ * This file has definitions for major device numbers
+ */
+
+/* limits */
+
+#define MAX_CHRDEV 64
+#define MAX_BLKDEV 64
+
+/*
+ * assignments
+ *
+ * devices are as follows (same as minix, so we can use the minix fs):
+ *
+ * character block comments
+ * -------------------- -------------------- --------------------
+ * 0 - unnamed unnamed minor 0 = true nodev
+ * 1 - /dev/mem ramdisk
+ * 2 - /dev/ptyp* floppy
+ * 3 - /dev/ttyp* ide0 or hd
+ * 4 - /dev/tty*
+ * 5 - /dev/tty; /dev/cua*
+ * 6 - lp
+ * 7 - /dev/vcs*
+ * 8 - scsi disk
+ * 9 - scsi tape
+ * 10 - mice
+ * 11 - scsi cdrom
+ * 12 - qic02 tape
+ * 13 - xt disk
+ * 14 - sound card
+ * 15 - cdu31a cdrom
+ * 16 - sockets goldstar cdrom
+ * 17 - af_unix optics cdrom
+ * 18 - af_inet sanyo cdrom
+ * 19 - cyclades /dev/ttyC*
+ * 20 - cyclades /dev/cub* mitsumi (mcdx) cdrom
+ * 21 - scsi generic
+ * 22 - ide1
+ * 23 - mitsumi cdrom
+ * 24 - sony535 cdrom
+ * 25 - matsushita cdrom minors 0..3
+ * 26 - matsushita cdrom 2 minors 0..3
+ * 27 - qic117 tape matsushita cdrom 3 minors 0..3
+ * 28 - matsushita cdrom 4 minors 0..3
+ * 29 - aztech/orchid/okano/wearnes cdrom
+ * 32 - philips/lms cm206 cdrom
+ * 33 - ide2
+ * 34 - z8530 driver ide3
+ * 36 - netlink
+ */
+
+#define UNNAMED_MAJOR 0
+#define MEM_MAJOR 1
+#define RAMDISK_MAJOR 1
+#define FLOPPY_MAJOR 2
+#define PTY_MASTER_MAJOR 2
+#define IDE0_MAJOR 3
+#define PTY_SLAVE_MAJOR 3
+#define HD_MAJOR IDE0_MAJOR
+#define TTY_MAJOR 4
+#define TTYAUX_MAJOR 5
+#define LP_MAJOR 6
+#define VCS_MAJOR 7
+#define SCSI_DISK_MAJOR 8
+#define SCSI_TAPE_MAJOR 9
+#define MOUSE_MAJOR 10
+#define SCSI_CDROM_MAJOR 11
+#define QIC02_TAPE_MAJOR 12
+#define XT_DISK_MAJOR 13
+#define SOUND_MAJOR 14
+#define CDU31A_CDROM_MAJOR 15
+#define SOCKET_MAJOR 16
+#define GOLDSTAR_CDROM_MAJOR 16
+#define AF_UNIX_MAJOR 17
+#define OPTICS_CDROM_MAJOR 17
+#define AF_INET_MAJOR 18
+#define SANYO_CDROM_MAJOR 18
+#define CYCLADES_MAJOR 19
+#define CYCLADESAUX_MAJOR 20
+#define MITSUMI_X_CDROM_MAJOR 20
+#define SCSI_GENERIC_MAJOR 21
+#define Z8530_MAJOR 34
+#define IDE1_MAJOR 22
+#define MITSUMI_CDROM_MAJOR 23
+#define CDU535_CDROM_MAJOR 24
+#define STL_SERIALMAJOR 24
+#define MATSUSHITA_CDROM_MAJOR 25
+#define STL_CALLOUTMAJOR 25
+#define MATSUSHITA_CDROM2_MAJOR 26
+#define QIC117_TAPE_MAJOR 27
+#define MATSUSHITA_CDROM3_MAJOR 27
+#define MATSUSHITA_CDROM4_MAJOR 28
+#define STL_SIOMEMMAJOR 28
+#define AZTECH_CDROM_MAJOR 29
+#define CM206_CDROM_MAJOR 32
+#define IDE2_MAJOR 33
+#define IDE3_MAJOR 34
+#define NETLINK_MAJOR 36
+#define IDETAPE_MAJOR 37
+
+/*
+ * Tests for SCSI devices.
+ */
+
+#define SCSI_MAJOR(M) \
+ ((M) == SCSI_DISK_MAJOR \
+ || (M) == SCSI_TAPE_MAJOR \
+ || (M) == SCSI_CDROM_MAJOR \
+ || (M) == SCSI_GENERIC_MAJOR)
+
+static inline int scsi_major(int m) {
+ return SCSI_MAJOR(m);
+}
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/malloc.h b/i386/i386at/gpl/linux/include/linux/malloc.h
new file mode 100644
index 00000000..847383ad
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/malloc.h
@@ -0,0 +1,16 @@
+#ifndef _LINUX_MALLOC_H
+#define _LINUX_MALLOC_H
+
+#include <linux/mm.h>
+
+#ifndef MACH_INCLUDE
+#define kmalloc linux_kmalloc
+#define kfree linux_kfree
+#endif
+
+void *linux_kmalloc(unsigned int size, int priority);
+void linux_kfree(void * obj);
+
+#define kfree_s(a,b) linux_kfree(a)
+
+#endif /* _LINUX_MALLOC_H */
diff --git a/i386/i386at/gpl/linux/include/linux/math_emu.h b/i386/i386at/gpl/linux/include/linux/math_emu.h
new file mode 100644
index 00000000..0d9606d9
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/math_emu.h
@@ -0,0 +1,43 @@
+#ifndef _LINUX_MATH_EMU_H
+#define _LINUX_MATH_EMU_H
+
+struct fpu_reg {
+ char sign;
+ char tag;
+ long exp;
+ unsigned sigl;
+ unsigned sigh;
+};
+
+
+/* This structure matches the layout of the data saved to the stack
+ following a device-not-present interrupt, part of it saved
+ automatically by the 80386/80486.
+ */
+struct info {
+ long ___orig_eip;
+ long ___ret_from_system_call;
+ long ___ebx;
+ long ___ecx;
+ long ___edx;
+ long ___esi;
+ long ___edi;
+ long ___ebp;
+ long ___eax;
+ long ___ds;
+ long ___es;
+ long ___fs;
+ long ___gs;
+ long ___orig_eax;
+ long ___eip;
+ long ___cs;
+ long ___eflags;
+ long ___esp;
+ long ___ss;
+ long ___vm86_es; /* This and the following only in vm86 mode */
+ long ___vm86_ds;
+ long ___vm86_fs;
+ long ___vm86_gs;
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/mc146818rtc.h b/i386/i386at/gpl/linux/include/linux/mc146818rtc.h
new file mode 100644
index 00000000..d2e709a1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/mc146818rtc.h
@@ -0,0 +1,109 @@
+/* mc146818rtc.h - register definitions for the Real-Time-Clock / CMOS RAM
+ * Copyright Torsten Duwe <duwe@informatik.uni-erlangen.de> 1993
+ * derived from Data Sheet, Copyright Motorola 1984 (!).
+ * It was written to be part of the Linux operating system.
+ */
+/* permission is hereby granted to copy, modify and redistribute this code
+ * in terms of the GNU Library General Public License, Version 2 or later,
+ * at your option.
+ */
+
+#ifndef _MC146818RTC_H
+#define _MC146818RTC_H
+#include <asm/io.h>
+
+#ifndef RTC_PORT
+#define RTC_PORT(x) (0x70 + (x))
+#define RTC_ALWAYS_BCD 1
+#endif
+
+#define CMOS_READ(addr) ({ \
+outb_p((addr),RTC_PORT(0)); \
+inb_p(RTC_PORT(1)); \
+})
+#define CMOS_WRITE(val, addr) ({ \
+outb_p((addr),RTC_PORT(0)); \
+outb_p((val),RTC_PORT(1)); \
+})
+
+/**********************************************************************
+ * register summary
+ **********************************************************************/
+#define RTC_SECONDS 0
+#define RTC_SECONDS_ALARM 1
+#define RTC_MINUTES 2
+#define RTC_MINUTES_ALARM 3
+#define RTC_HOURS 4
+#define RTC_HOURS_ALARM 5
+/* RTC_*_alarm is always true if 2 MSBs are set */
+# define RTC_ALARM_DONT_CARE 0xC0
+
+#define RTC_DAY_OF_WEEK 6
+#define RTC_DAY_OF_MONTH 7
+#define RTC_MONTH 8
+#define RTC_YEAR 9
+
+/* control registers - Moto names
+ */
+#define RTC_REG_A 10
+#define RTC_REG_B 11
+#define RTC_REG_C 12
+#define RTC_REG_D 13
+
+/**********************************************************************
+ * register details
+ **********************************************************************/
+#define RTC_FREQ_SELECT RTC_REG_A
+
+/* update-in-progress - set to "1" 244 microsecs before RTC goes off the bus,
+ * reset after update (may take 1.984ms @ 32768Hz RefClock) is complete,
+ * totalling to a max high interval of 2.228 ms.
+ */
+# define RTC_UIP 0x80
+# define RTC_DIV_CTL 0x70
+ /* divider control: refclock values 4.194 / 1.049 MHz / 32.768 kHz */
+# define RTC_REF_CLCK_4MHZ 0x00
+# define RTC_REF_CLCK_1MHZ 0x10
+# define RTC_REF_CLCK_32KHZ 0x20
+ /* 2 values for divider stage reset, others for "testing purposes only" */
+# define RTC_DIV_RESET1 0x60
+# define RTC_DIV_RESET2 0x70
+ /* Periodic intr. / Square wave rate select. 0=none, 1=32.8kHz,... 15=2Hz */
+# define RTC_RATE_SELECT 0x0F
+
+/**********************************************************************/
+#define RTC_CONTROL RTC_REG_B
+# define RTC_SET 0x80 /* disable updates for clock setting */
+# define RTC_PIE 0x40 /* periodic interrupt enable */
+# define RTC_AIE 0x20 /* alarm interrupt enable */
+# define RTC_UIE 0x10 /* update-finished interrupt enable */
+# define RTC_SQWE 0x08 /* enable square-wave output */
+# define RTC_DM_BINARY 0x04 /* all time/date values are BCD if clear */
+# define RTC_24H 0x02 /* 24 hour mode - else hours bit 7 means pm */
+# define RTC_DST_EN 0x01 /* auto switch DST - works f. USA only */
+
+/**********************************************************************/
+#define RTC_INTR_FLAGS RTC_REG_C
+/* caution - cleared by read */
+# define RTC_IRQF 0x80 /* any of the following 3 is active */
+# define RTC_PF 0x40
+# define RTC_AF 0x20
+# define RTC_UF 0x10
+
+/**********************************************************************/
+#define RTC_VALID RTC_REG_D
+# define RTC_VRT 0x80 /* valid RAM and time */
+/**********************************************************************/
+
+/* example: !(CMOS_READ(RTC_CONTROL) & RTC_DM_BINARY)
+ * determines if the following two #defines are needed
+ */
+#ifndef BCD_TO_BIN
+#define BCD_TO_BIN(val) ((val)=((val)&15) + ((val)>>4)*10)
+#endif
+
+#ifndef BIN_TO_BCD
+#define BIN_TO_BCD(val) ((val)=(((val)/10)<<4) + (val)%10)
+#endif
+
+#endif /* _MC146818RTC_H */
diff --git a/i386/i386at/gpl/linux/include/linux/minix_fs.h b/i386/i386at/gpl/linux/include/linux/minix_fs.h
new file mode 100644
index 00000000..f0ecdea0
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/minix_fs.h
@@ -0,0 +1,135 @@
+#ifndef _LINUX_MINIX_FS_H
+#define _LINUX_MINIX_FS_H
+
+/*
+ * The minix filesystem constants/structures
+ */
+
+/*
+ * Thanks to Kees J Bot for sending me the definitions of the new
+ * minix filesystem (aka V2) with bigger inodes and 32-bit block
+ * pointers.
+ */
+
+#define MINIX_ROOT_INO 1
+
+/* Not the same as the bogus LINK_MAX in <linux/limits.h>. Oh well. */
+#define MINIX_LINK_MAX 250
+
+#define MINIX_I_MAP_SLOTS 8
+#define MINIX_Z_MAP_SLOTS 64
+#define MINIX_SUPER_MAGIC 0x137F /* original minix fs */
+#define MINIX_SUPER_MAGIC2 0x138F /* minix fs, 30 char names */
+#define MINIX2_SUPER_MAGIC 0x2468 /* minix V2 fs */
+#define MINIX2_SUPER_MAGIC2 0x2478 /* minix V2 fs, 30 char names */
+#define MINIX_VALID_FS 0x0001 /* Clean fs. */
+#define MINIX_ERROR_FS 0x0002 /* fs has errors. */
+
+#define MINIX_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix_inode)))
+#define MINIX2_INODES_PER_BLOCK ((BLOCK_SIZE)/(sizeof (struct minix2_inode)))
+
+#define MINIX_V1 0x0001 /* original minix fs */
+#define MINIX_V2 0x0002 /* minix V2 fs */
+
+#define INODE_VERSION(inode) inode->i_sb->u.minix_sb.s_version
+
+/*
+ * This is the original minix inode layout on disk.
+ * Note the 8-bit gid and atime and ctime.
+ */
+struct minix_inode {
+ __u16 i_mode;
+ __u16 i_uid;
+ __u32 i_size;
+ __u32 i_time;
+ __u8 i_gid;
+ __u8 i_nlinks;
+ __u16 i_zone[9];
+};
+
+/*
+ * The new minix inode has all the time entries, as well as
+ * long block numbers and a third indirect block (7+1+1+1
+ * instead of 7+1+1). Also, some previously 8-bit values are
+ * now 16-bit. The inode is now 64 bytes instead of 32.
+ */
+struct minix2_inode {
+ __u16 i_mode;
+ __u16 i_nlinks;
+ __u16 i_uid;
+ __u16 i_gid;
+ __u32 i_size;
+ __u32 i_atime;
+ __u32 i_mtime;
+ __u32 i_ctime;
+ __u32 i_zone[10];
+};
+
+/*
+ * minix super-block data on disk
+ */
+struct minix_super_block {
+ __u16 s_ninodes;
+ __u16 s_nzones;
+ __u16 s_imap_blocks;
+ __u16 s_zmap_blocks;
+ __u16 s_firstdatazone;
+ __u16 s_log_zone_size;
+ __u32 s_max_size;
+ __u16 s_magic;
+ __u16 s_state;
+ __u32 s_zones;
+};
+
+struct minix_dir_entry {
+ __u16 inode;
+ char name[0];
+};
+
+#ifdef __KERNEL__
+
+extern int minix_lookup(struct inode * dir,const char * name, int len,
+ struct inode ** result);
+extern int minix_create(struct inode * dir,const char * name, int len, int mode,
+ struct inode ** result);
+extern int minix_mkdir(struct inode * dir, const char * name, int len, int mode);
+extern int minix_rmdir(struct inode * dir, const char * name, int len);
+extern int minix_unlink(struct inode * dir, const char * name, int len);
+extern int minix_symlink(struct inode * inode, const char * name, int len,
+ const char * symname);
+extern int minix_link(struct inode * oldinode, struct inode * dir, const char * name, int len);
+extern int minix_mknod(struct inode * dir, const char * name, int len, int mode, int rdev);
+extern int minix_rename(struct inode * old_dir, const char * old_name, int old_len,
+ struct inode * new_dir, const char * new_name, int new_len);
+extern struct inode * minix_new_inode(const struct inode * dir);
+extern void minix_free_inode(struct inode * inode);
+extern unsigned long minix_count_free_inodes(struct super_block *sb);
+extern int minix_new_block(struct super_block * sb);
+extern void minix_free_block(struct super_block * sb, int block);
+extern unsigned long minix_count_free_blocks(struct super_block *sb);
+
+extern int minix_bmap(struct inode *,int);
+
+extern struct buffer_head * minix_getblk(struct inode *, int, int);
+extern struct buffer_head * minix_bread(struct inode *, int, int);
+
+extern void minix_truncate(struct inode *);
+extern void minix_put_super(struct super_block *);
+extern struct super_block *minix_read_super(struct super_block *,void *,int);
+extern int init_minix_fs(void);
+extern void minix_write_super(struct super_block *);
+extern int minix_remount (struct super_block * sb, int * flags, char * data);
+extern void minix_read_inode(struct inode *);
+extern void minix_write_inode(struct inode *);
+extern void minix_put_inode(struct inode *);
+extern void minix_statfs(struct super_block *, struct statfs *, int);
+extern int minix_sync_inode(struct inode *);
+extern int minix_sync_file(struct inode *, struct file *);
+
+extern struct inode_operations minix_file_inode_operations;
+extern struct inode_operations minix_dir_inode_operations;
+extern struct inode_operations minix_symlink_inode_operations;
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/minix_fs_sb.h b/i386/i386at/gpl/linux/include/linux/minix_fs_sb.h
new file mode 100644
index 00000000..e77b4efc
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/minix_fs_sb.h
@@ -0,0 +1,25 @@
+#ifndef _MINIX_FS_SB
+#define _MINIX_FS_SB
+
+/*
+ * minix super-block data in memory
+ */
+struct minix_sb_info {
+ unsigned long s_ninodes;
+ unsigned long s_nzones;
+ unsigned long s_imap_blocks;
+ unsigned long s_zmap_blocks;
+ unsigned long s_firstdatazone;
+ unsigned long s_log_zone_size;
+ unsigned long s_max_size;
+ struct buffer_head * s_imap[8];
+ struct buffer_head * s_zmap[64];
+ unsigned long s_dirsize;
+ unsigned long s_namelen;
+ struct buffer_head * s_sbh;
+ struct minix_super_block * s_ms;
+ unsigned short s_mount_state;
+ unsigned short s_version;
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/mm.h b/i386/i386at/gpl/linux/include/linux/mm.h
new file mode 100644
index 00000000..f8bbb9ba
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/mm.h
@@ -0,0 +1,297 @@
+#ifndef _LINUX_MM_H
+#define _LINUX_MM_H
+
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+
+extern unsigned long high_memory;
+
+#include <asm/page.h>
+
+#ifdef __KERNEL__
+
+#define VERIFY_READ 0
+#define VERIFY_WRITE 1
+
+extern int verify_area(int, const void *, unsigned long);
+
+/*
+ * Linux kernel virtual memory manager primitives.
+ * The idea being to have a "virtual" mm in the same way
+ * we have a virtual fs - giving a cleaner interface to the
+ * mm details, and allowing different kinds of memory mappings
+ * (from shared memory to executable loading to arbitrary
+ * mmap() functions).
+ */
+
+/*
+ * This struct defines a memory VMM memory area. There is one of these
+ * per VM-area/task. A VM area is any part of the process virtual memory
+ * space that has a special rule for the page-fault handlers (ie a shared
+ * library, the executable area etc).
+ */
+struct vm_area_struct {
+ struct mm_struct * vm_mm; /* VM area parameters */
+ unsigned long vm_start;
+ unsigned long vm_end;
+ pgprot_t vm_page_prot;
+ unsigned short vm_flags;
+/* AVL tree of VM areas per task, sorted by address */
+ short vm_avl_height;
+ struct vm_area_struct * vm_avl_left;
+ struct vm_area_struct * vm_avl_right;
+/* linked list of VM areas per task, sorted by address */
+ struct vm_area_struct * vm_next;
+/* for areas with inode, the circular list inode->i_mmap */
+/* for shm areas, the circular list of attaches */
+/* otherwise unused */
+ struct vm_area_struct * vm_next_share;
+ struct vm_area_struct * vm_prev_share;
+/* more */
+ struct vm_operations_struct * vm_ops;
+ unsigned long vm_offset;
+ struct inode * vm_inode;
+ unsigned long vm_pte; /* shared mem */
+};
+
+/*
+ * vm_flags..
+ */
+#define VM_READ 0x0001 /* currently active flags */
+#define VM_WRITE 0x0002
+#define VM_EXEC 0x0004
+#define VM_SHARED 0x0008
+
+#define VM_MAYREAD 0x0010 /* limits for mprotect() etc */
+#define VM_MAYWRITE 0x0020
+#define VM_MAYEXEC 0x0040
+#define VM_MAYSHARE 0x0080
+
+#define VM_GROWSDOWN 0x0100 /* general info on the segment */
+#define VM_GROWSUP 0x0200
+#define VM_SHM 0x0400 /* shared memory area, don't swap out */
+#define VM_DENYWRITE 0x0800 /* ETXTBSY on write attempts.. */
+
+#define VM_EXECUTABLE 0x1000
+#define VM_LOCKED 0x2000
+
+#define VM_STACK_FLAGS 0x0177
+
+/*
+ * mapping from the currently active vm_flags protection bits (the
+ * low four bits) to a page protection mask..
+ */
+extern pgprot_t protection_map[16];
+
+
+/*
+ * These are the virtual MM functions - opening of an area, closing and
+ * unmapping it (needed to keep files on disk up-to-date etc), pointer
+ * to the functions called when a no-page or a wp-page exception occurs.
+ */
+struct vm_operations_struct {
+ void (*open)(struct vm_area_struct * area);
+ void (*close)(struct vm_area_struct * area);
+ void (*unmap)(struct vm_area_struct *area, unsigned long, size_t);
+ void (*protect)(struct vm_area_struct *area, unsigned long, size_t, unsigned int newprot);
+ int (*sync)(struct vm_area_struct *area, unsigned long, size_t, unsigned int flags);
+ void (*advise)(struct vm_area_struct *area, unsigned long, size_t, unsigned int advise);
+ unsigned long (*nopage)(struct vm_area_struct * area, unsigned long address, int write_access);
+ unsigned long (*wppage)(struct vm_area_struct * area, unsigned long address,
+ unsigned long page);
+ int (*swapout)(struct vm_area_struct *, unsigned long, pte_t *);
+ pte_t (*swapin)(struct vm_area_struct *, unsigned long, unsigned long);
+};
+
+/*
+ * Try to keep the most commonly accessed fields in single cache lines
+ * here (16 bytes or greater). This ordering should be particularly
+ * beneficial on 32-bit processors.
+ *
+ * The first line is data used in linear searches (eg. clock algorithm
+ * scans). The second line is data used in page searches through the
+ * page-cache. -- sct
+ */
+typedef struct page {
+ unsigned int count;
+ unsigned dirty:16,
+ age:8,
+ uptodate:1,
+ error:1,
+ referenced:1,
+ locked:1,
+ free_after:1,
+ unused:2,
+ reserved:1;
+ struct wait_queue *wait;
+ struct page *next;
+
+ struct page *next_hash;
+ unsigned long offset;
+ struct inode *inode;
+ struct page *write_list;
+
+ struct page *prev;
+ struct page *prev_hash;
+} mem_map_t;
+
+extern mem_map_t * mem_map;
+
+/*
+ * Free area management
+ */
+
+#define NR_MEM_LISTS 6
+
+struct mem_list {
+ struct mem_list * next;
+ struct mem_list * prev;
+};
+
+extern struct mem_list free_area_list[NR_MEM_LISTS];
+extern unsigned int * free_area_map[NR_MEM_LISTS];
+
+/*
+ * This is timing-critical - most of the time in getting a new page
+ * goes to clearing the page. If you want a page without the clearing
+ * overhead, just use __get_free_page() directly..
+ */
+#define __get_free_page(priority) __get_free_pages((priority),0,~0UL)
+#define __get_dma_pages(priority, order) __get_free_pages((priority),(order),MAX_DMA_ADDRESS)
+extern unsigned long __get_free_pages(int priority, unsigned long gfporder, unsigned long max_addr);
+
+extern inline unsigned long get_free_page(int priority)
+{
+ unsigned long page;
+
+ page = __get_free_page(priority);
+ if (page)
+ memset((void *) page, 0, PAGE_SIZE);
+ return page;
+}
+
+/* memory.c & swap.c*/
+
+#define free_page(addr) free_pages((addr),0)
+extern void free_pages(unsigned long addr, unsigned long order);
+
+extern void show_free_areas(void);
+extern unsigned long put_dirty_page(struct task_struct * tsk,unsigned long page,
+ unsigned long address);
+
+extern void free_page_tables(struct task_struct * tsk);
+extern void clear_page_tables(struct task_struct * tsk);
+extern int new_page_tables(struct task_struct * tsk);
+extern int copy_page_tables(struct task_struct * to);
+
+extern int zap_page_range(struct mm_struct *mm, unsigned long address, unsigned long size);
+extern int copy_page_range(struct mm_struct *dst, struct mm_struct *src, struct vm_area_struct *vma);
+extern int remap_page_range(unsigned long from, unsigned long to, unsigned long size, pgprot_t prot);
+extern int zeromap_page_range(unsigned long from, unsigned long size, pgprot_t prot);
+
+extern void vmtruncate(struct inode * inode, unsigned long offset);
+extern void handle_mm_fault(struct vm_area_struct *vma, unsigned long address, int write_access);
+extern void do_wp_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, int write_access);
+extern void do_no_page(struct task_struct * tsk, struct vm_area_struct * vma, unsigned long address, int write_access);
+
+extern unsigned long paging_init(unsigned long start_mem, unsigned long end_mem);
+extern void mem_init(unsigned long start_mem, unsigned long end_mem);
+extern void show_mem(void);
+extern void oom(struct task_struct * tsk);
+extern void si_meminfo(struct sysinfo * val);
+
+/* vmalloc.c */
+
+extern void * vmalloc(unsigned long size);
+extern void * vremap(unsigned long offset, unsigned long size);
+extern void vfree(void * addr);
+extern int vread(char *buf, char *addr, int count);
+
+/* mmap.c */
+extern unsigned long do_mmap(struct file * file, unsigned long addr, unsigned long len,
+ unsigned long prot, unsigned long flags, unsigned long off);
+extern void merge_segments(struct task_struct *, unsigned long, unsigned long);
+extern void insert_vm_struct(struct task_struct *, struct vm_area_struct *);
+extern void remove_shared_vm_struct(struct vm_area_struct *);
+extern void build_mmap_avl(struct mm_struct *);
+extern void exit_mmap(struct mm_struct *);
+extern int do_munmap(unsigned long, size_t);
+extern unsigned long get_unmapped_area(unsigned long, unsigned long);
+
+/* filemap.c */
+extern unsigned long page_unuse(unsigned long);
+extern int shrink_mmap(int, unsigned long);
+extern void truncate_inode_pages(struct inode *, unsigned long);
+
+#define GFP_BUFFER 0x00
+#define GFP_ATOMIC 0x01
+#define GFP_USER 0x02
+#define GFP_KERNEL 0x03
+#define GFP_NOBUFFER 0x04
+#define GFP_NFS 0x05
+
+/* Flag - indicates that the buffer will be suitable for DMA. Ignored on some
+ platforms, used as appropriate on others */
+
+#define GFP_DMA 0x80
+
+#define GFP_LEVEL_MASK 0xf
+
+#define avl_empty (struct vm_area_struct *) NULL
+
+#ifndef MACH
+static inline int expand_stack(struct vm_area_struct * vma, unsigned long address)
+{
+ unsigned long grow;
+
+ address &= PAGE_MASK;
+ if (vma->vm_end - address > current->rlim[RLIMIT_STACK].rlim_cur)
+ return -ENOMEM;
+ grow = vma->vm_start - address;
+ vma->vm_start = address;
+ vma->vm_offset -= grow;
+ vma->vm_mm->total_vm += grow >> PAGE_SHIFT;
+ if (vma->vm_flags & VM_LOCKED)
+ vma->vm_mm->locked_vm += grow >> PAGE_SHIFT;
+ return 0;
+}
+
+/* Look up the first VMA which satisfies addr < vm_end, NULL if none. */
+static inline struct vm_area_struct * find_vma (struct task_struct * task, unsigned long addr)
+{
+ struct vm_area_struct * result = NULL;
+ struct vm_area_struct * tree;
+
+ if (!task->mm)
+ return NULL;
+ for (tree = task->mm->mmap_avl ; ; ) {
+ if (tree == avl_empty)
+ return result;
+ if (tree->vm_end > addr) {
+ if (tree->vm_start <= addr)
+ return tree;
+ result = tree;
+ tree = tree->vm_avl_left;
+ } else
+ tree = tree->vm_avl_right;
+ }
+}
+
+/* Look up the first VMA which intersects the interval start_addr..end_addr-1,
+ NULL if none. Assume start_addr < end_addr. */
+static inline struct vm_area_struct * find_vma_intersection (struct task_struct * task, unsigned long start_addr, unsigned long end_addr)
+{
+ struct vm_area_struct * vma;
+
+ vma = find_vma(task,start_addr);
+ if (!vma || end_addr <= vma->vm_start)
+ return NULL;
+ return vma;
+}
+#endif /* ! MACH */
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/module.h b/i386/i386at/gpl/linux/include/linux/module.h
new file mode 100644
index 00000000..a91ad19b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/module.h
@@ -0,0 +1,115 @@
+/*
+ * Dynamic loading of modules into the kernel.
+ *
+ * Modified by Bjorn Ekwall <bj0rn@blox.se>
+ */
+
+#ifndef _LINUX_MODULE_H
+#define _LINUX_MODULE_H
+
+#ifdef __GENKSYMS__
+# define _set_ver(sym,vers) sym
+# undef MODVERSIONS
+# define MODVERSIONS
+#else /* ! __GENKSYMS__ */
+# if defined(MODVERSIONS) && !defined(MODULE) && defined(EXPORT_SYMTAB)
+# define _set_ver(sym,vers) sym
+# include <linux/modversions.h>
+# endif
+#endif /* __GENKSYMS__ */
+
+/* values of module.state */
+#define MOD_UNINITIALIZED 0
+#define MOD_RUNNING 1
+#define MOD_DELETED 2
+
+/* maximum length of module name */
+#define MOD_MAX_NAME 64
+
+/* magic marker for modules inserted from kerneld, to be auto-reaped */
+#define MOD_AUTOCLEAN 0x40000000 /* big enough, but no sign problems... */
+
+/* maximum length of symbol name */
+#define SYM_MAX_NAME 60
+
+struct kernel_sym { /* sent to "insmod" */
+ unsigned long value; /* value of symbol */
+ char name[SYM_MAX_NAME]; /* name of symbol */
+};
+
+struct module_ref {
+ struct module *module;
+ struct module_ref *next;
+};
+
+struct internal_symbol {
+ void *addr;
+ const char *name;
+ };
+
+struct symbol_table { /* received from "insmod" */
+ int size; /* total, including string table!!! */
+ int n_symbols;
+ int n_refs;
+ struct internal_symbol symbol[0]; /* actual size defined by n_symbols */
+ struct module_ref ref[0]; /* actual size defined by n_refs */
+};
+/*
+ * Note: The string table follows immediately after the symbol table in memory!
+ */
+
+struct module {
+ struct module *next;
+ struct module_ref *ref; /* the list of modules that refer to me */
+ struct symbol_table *symtab;
+ const char *name;
+ int size; /* size of module in pages */
+ void* addr; /* address of module */
+ int state;
+ void (*cleanup)(void); /* cleanup routine */
+};
+
+struct mod_routines {
+ int (*init)(void); /* initialization routine */
+ void (*cleanup)(void); /* cleanup routine */
+};
+
+/* rename_module_symbol(old_name, new_name) WOW! */
+extern int rename_module_symbol(char *, char *);
+
+/* insert new symbol table */
+extern int register_symtab(struct symbol_table *);
+
+/*
+ * The first word of the module contains the use count.
+ */
+#define GET_USE_COUNT(module) (* (long *) (module)->addr)
+/*
+ * define the count variable, and usage macros.
+ */
+
+#ifdef MODULE
+
+extern long mod_use_count_;
+#define MOD_INC_USE_COUNT mod_use_count_++
+#define MOD_DEC_USE_COUNT mod_use_count_--
+#define MOD_IN_USE ((mod_use_count_ & ~MOD_AUTOCLEAN) != 0)
+
+#ifndef __NO_VERSION__
+#include <linux/version.h>
+char kernel_version[]=UTS_RELEASE;
+#endif
+
+#if defined(MODVERSIONS) && !defined(__GENKSYMS__)
+int Using_Versions; /* gcc will handle this global (used as a flag) correctly */
+#endif
+
+#else
+
+#define MOD_INC_USE_COUNT do { } while (0)
+#define MOD_DEC_USE_COUNT do { } while (0)
+#define MOD_IN_USE 1
+
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/mount.h b/i386/i386at/gpl/linux/include/linux/mount.h
new file mode 100644
index 00000000..357c7ae6
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/mount.h
@@ -0,0 +1,30 @@
+/*
+ *
+ * Definitions for mount interface. This describes the in the kernel build
+ * linkedlist with mounted filesystems.
+ *
+ * Author: Marco van Wieringen <mvw@mcs.ow.nl> <mvw@tnix.net> <mvw@cistron.nl>
+ *
+ * Version: $Id: mount.h,v 1.1.1.1 1997/02/25 21:27:29 thomas Exp $
+ *
+ */
+#ifndef _LINUX_MOUNT_H
+#define _LINUX_MOUNT_H
+
+struct vfsmount
+{
+ kdev_t mnt_dev; /* Device this applies to */
+ char *mnt_devname; /* Name of device e.g. /dev/dsk/hda1 */
+ char *mnt_dirname; /* Name of directory mounted on */
+ unsigned int mnt_flags; /* Flags of this device */
+ struct semaphore mnt_sem; /* lock device while I/O in progress */
+ struct super_block *mnt_sb; /* pointer to superblock */
+ struct file *mnt_quotas[MAXQUOTAS]; /* fp's to quotafiles */
+ time_t mnt_iexp[MAXQUOTAS]; /* expiretime for inodes */
+ time_t mnt_bexp[MAXQUOTAS]; /* expiretime for blocks */
+ struct vfsmount *mnt_next; /* pointer to next in linkedlist */
+};
+
+struct vfsmount *lookup_vfsmnt(kdev_t dev);
+
+#endif /* _LINUX_MOUNT_H */
diff --git a/i386/i386at/gpl/linux/include/linux/net.h b/i386/i386at/gpl/linux/include/linux/net.h
new file mode 100644
index 00000000..1fbe98a0
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/net.h
@@ -0,0 +1,132 @@
+/*
+ * NET An implementation of the SOCKET network access protocol.
+ * This is the master header file for the Linux NET layer,
+ * or, in plain English: the networking handling part of the
+ * kernel.
+ *
+ * Version: @(#)net.h 1.0.3 05/25/93
+ *
+ * Authors: Orest Zborowski, <obz@Kodak.COM>
+ * Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_NET_H
+#define _LINUX_NET_H
+
+
+#include <linux/wait.h>
+#include <linux/socket.h>
+
+
+#define NSOCKETS 2000 /* Dynamic, this is MAX LIMIT */
+#define NSOCKETS_UNIX 128 /* unix domain static limit */
+#define NPROTO 16 /* should be enough for now.. */
+
+
+#define SYS_SOCKET 1 /* sys_socket(2) */
+#define SYS_BIND 2 /* sys_bind(2) */
+#define SYS_CONNECT 3 /* sys_connect(2) */
+#define SYS_LISTEN 4 /* sys_listen(2) */
+#define SYS_ACCEPT 5 /* sys_accept(2) */
+#define SYS_GETSOCKNAME 6 /* sys_getsockname(2) */
+#define SYS_GETPEERNAME 7 /* sys_getpeername(2) */
+#define SYS_SOCKETPAIR 8 /* sys_socketpair(2) */
+#define SYS_SEND 9 /* sys_send(2) */
+#define SYS_RECV 10 /* sys_recv(2) */
+#define SYS_SENDTO 11 /* sys_sendto(2) */
+#define SYS_RECVFROM 12 /* sys_recvfrom(2) */
+#define SYS_SHUTDOWN 13 /* sys_shutdown(2) */
+#define SYS_SETSOCKOPT 14 /* sys_setsockopt(2) */
+#define SYS_GETSOCKOPT 15 /* sys_getsockopt(2) */
+#define SYS_SENDMSG 16 /* sys_sendmsg(2) */
+#define SYS_RECVMSG 17 /* sys_recvmsg(2) */
+
+
+typedef enum {
+ SS_FREE = 0, /* not allocated */
+ SS_UNCONNECTED, /* unconnected to any socket */
+ SS_CONNECTING, /* in process of connecting */
+ SS_CONNECTED, /* connected to socket */
+ SS_DISCONNECTING /* in process of disconnecting */
+} socket_state;
+
+#define SO_ACCEPTCON (1<<16) /* performed a listen */
+#define SO_WAITDATA (1<<17) /* wait data to read */
+#define SO_NOSPACE (1<<18) /* no space to write */
+
+#ifdef __KERNEL__
+/*
+ * Internal representation of a socket. not all the fields are used by
+ * all configurations:
+ *
+ * server client
+ * conn client connected to server connected to
+ * iconn list of clients -unused-
+ * awaiting connections
+ * wait sleep for clients, sleep for connection,
+ * sleep for i/o sleep for i/o
+ */
+struct socket {
+ short type; /* SOCK_STREAM, ... */
+ socket_state state;
+ long flags;
+ struct proto_ops *ops; /* protocols do most everything */
+ void *data; /* protocol data */
+ struct socket *conn; /* server socket connected to */
+ struct socket *iconn; /* incomplete client conn.s */
+ struct socket *next;
+ struct wait_queue **wait; /* ptr to place to wait on */
+ struct inode *inode;
+ struct fasync_struct *fasync_list; /* Asynchronous wake up list */
+};
+
+#define SOCK_INODE(S) ((S)->inode)
+
+struct proto_ops {
+ int family;
+
+ int (*create) (struct socket *sock, int protocol);
+ int (*dup) (struct socket *newsock, struct socket *oldsock);
+ int (*release) (struct socket *sock, struct socket *peer);
+ int (*bind) (struct socket *sock, struct sockaddr *umyaddr,
+ int sockaddr_len);
+ int (*connect) (struct socket *sock, struct sockaddr *uservaddr,
+ int sockaddr_len, int flags);
+ int (*socketpair) (struct socket *sock1, struct socket *sock2);
+ int (*accept) (struct socket *sock, struct socket *newsock,
+ int flags);
+ int (*getname) (struct socket *sock, struct sockaddr *uaddr,
+ int *usockaddr_len, int peer);
+ int (*select) (struct socket *sock, int sel_type,
+ select_table *wait);
+ int (*ioctl) (struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+ int (*listen) (struct socket *sock, int len);
+ int (*shutdown) (struct socket *sock, int flags);
+ int (*setsockopt) (struct socket *sock, int level, int optname,
+ char *optval, int optlen);
+ int (*getsockopt) (struct socket *sock, int level, int optname,
+ char *optval, int *optlen);
+ int (*fcntl) (struct socket *sock, unsigned int cmd,
+ unsigned long arg);
+ int (*sendmsg) (struct socket *sock, struct msghdr *m, int total_len, int nonblock, int flags);
+ int (*recvmsg) (struct socket *sock, struct msghdr *m, int total_len, int nonblock, int flags, int *addr_len);
+};
+
+struct net_proto {
+ const char *name; /* Protocol name */
+ void (*init_func)(struct net_proto *); /* Bootstrap */
+};
+
+extern int sock_wake_async(struct socket *sock, int how);
+extern int sock_register(int family, struct proto_ops *ops);
+extern int sock_unregister(int family);
+extern struct socket *sock_alloc(void);
+extern void sock_release(struct socket *sock);
+#endif /* __KERNEL__ */
+#endif /* _LINUX_NET_H */
diff --git a/i386/i386at/gpl/linux/include/linux/netdevice.h b/i386/i386at/gpl/linux/include/linux/netdevice.h
new file mode 100644
index 00000000..9e1143be
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/netdevice.h
@@ -0,0 +1,332 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Interfaces handler.
+ *
+ * Version: @(#)dev.h 1.0.10 08/12/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Donald J. Becker, <becker@super.org>
+ * Alan Cox, <A.Cox@swansea.ac.uk>
+ * Bjorn Ekwall. <bj0rn@blox.se>
+ *
+ * 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.
+ *
+ * Moved to /usr/include/linux for NET3
+ */
+#ifndef _LINUX_NETDEVICE_H
+#define _LINUX_NETDEVICE_H
+
+#include <linux/config.h>
+#include <linux/if.h>
+#include <linux/if_ether.h>
+#include <linux/skbuff.h>
+
+/* for future expansion when we will have different priorities. */
+#define DEV_NUMBUFFS 3
+#define MAX_ADDR_LEN 7
+#ifndef CONFIG_AX25
+#ifndef CONFIG_TR
+#ifndef CONFIG_NET_IPIP
+#define MAX_HEADER 32 /* We really need about 18 worst case .. so 32 is aligned */
+#else
+#define MAX_HEADER 48 /* We need to allow for having tunnel headers */
+#endif /* IPIP */
+#else
+#define MAX_HEADER 48 /* Token Ring header needs 40 bytes ... 48 is aligned */
+#endif /* TR */
+#else
+#define MAX_HEADER 96 /* AX.25 + NetROM */
+#endif /* AX25 */
+
+#define IS_MYADDR 1 /* address is (one of) our own */
+#define IS_LOOPBACK 2 /* address is for LOOPBACK */
+#define IS_BROADCAST 3 /* address is a valid broadcast */
+#define IS_INVBCAST 4 /* Wrong netmask bcast not for us (unused)*/
+#define IS_MULTICAST 5 /* Multicast IP address */
+
+/*
+ * We tag multicasts with these structures.
+ */
+
+struct dev_mc_list
+{
+ struct dev_mc_list *next;
+ char dmi_addr[MAX_ADDR_LEN];
+ unsigned short dmi_addrlen;
+ unsigned short dmi_users;
+};
+
+struct hh_cache
+{
+ struct hh_cache *hh_next;
+ void *hh_arp; /* Opaque pointer, used by
+ * any address resolution module,
+ * not only ARP.
+ */
+ unsigned int hh_refcnt; /* number of users */
+ unsigned short hh_type; /* protocol identifier, f.e ETH_P_IP */
+ char hh_uptodate; /* hh_data is valid */
+ char hh_data[16]; /* cached hardware header */
+};
+
+/*
+ * The DEVICE structure.
+ * Actually, this whole structure is a big mistake. It mixes I/O
+ * data with strictly "high-level" data, and it has to know about
+ * almost every data structure used in the INET module.
+ */
+#ifdef MACH
+#ifndef MACH_INCLUDE
+#define device linux_device
+#endif
+struct linux_device
+#else
+struct device
+#endif
+{
+
+ /*
+ * This is the first field of the "visible" part of this structure
+ * (i.e. as seen by users in the "Space.c" file). It is the name
+ * the interface.
+ */
+ char *name;
+
+ /* I/O specific fields - FIXME: Merge these and struct ifmap into one */
+ unsigned long rmem_end; /* shmem "recv" end */
+ unsigned long rmem_start; /* shmem "recv" start */
+ unsigned long mem_end; /* shared mem end */
+ unsigned long mem_start; /* shared mem start */
+ unsigned long base_addr; /* device I/O address */
+ unsigned char irq; /* device IRQ number */
+
+ /* Low-level status flags. */
+ volatile unsigned char start, /* start an operation */
+ interrupt; /* interrupt arrived */
+ unsigned long tbusy; /* transmitter busy must be long for bitops */
+
+ struct linux_device *next;
+
+ /* The device initialization function. Called only once. */
+ int (*init)(struct linux_device *dev);
+
+ /* Some hardware also needs these fields, but they are not part of the
+ usual set specified in Space.c. */
+ unsigned char if_port; /* Selectable AUI, TP,..*/
+ unsigned char dma; /* DMA channel */
+
+ struct enet_statistics* (*get_stats)(struct linux_device *dev);
+
+ /*
+ * This marks the end of the "visible" part of the structure. All
+ * fields hereafter are internal to the system, and may change at
+ * will (read: may be cleaned up at will).
+ */
+
+ /* These may be needed for future network-power-down code. */
+ unsigned long trans_start; /* Time (in jiffies) of last Tx */
+ unsigned long last_rx; /* Time of last Rx */
+
+ unsigned short flags; /* interface flags (a la BSD) */
+ unsigned short family; /* address family ID (AF_INET) */
+ unsigned short metric; /* routing metric (not used) */
+ unsigned short mtu; /* interface MTU value */
+ unsigned short type; /* interface hardware type */
+ unsigned short hard_header_len; /* hardware hdr length */
+ void *priv; /* pointer to private data */
+
+ /* Interface address info. */
+ unsigned char broadcast[MAX_ADDR_LEN]; /* hw bcast add */
+ unsigned char pad; /* make dev_addr aligned to 8 bytes */
+ unsigned char dev_addr[MAX_ADDR_LEN]; /* hw address */
+ unsigned char addr_len; /* hardware address length */
+ unsigned long pa_addr; /* protocol address */
+ unsigned long pa_brdaddr; /* protocol broadcast addr */
+ unsigned long pa_dstaddr; /* protocol P-P other side addr */
+ unsigned long pa_mask; /* protocol netmask */
+ unsigned short pa_alen; /* protocol address length */
+
+ struct dev_mc_list *mc_list; /* Multicast mac addresses */
+ int mc_count; /* Number of installed mcasts */
+
+ struct ip_mc_list *ip_mc_list; /* IP multicast filter chain */
+ __u32 tx_queue_len; /* Max frames per queue allowed */
+
+ /* For load balancing driver pair support */
+
+ unsigned long pkt_queue; /* Packets queued */
+ struct linux_device *slave; /* Slave device */
+ struct net_alias_info *alias_info; /* main dev alias info */
+ struct net_alias *my_alias; /* alias devs */
+
+ /* Pointer to the interface buffers. */
+ struct sk_buff_head buffs[DEV_NUMBUFFS];
+
+ /* Pointers to interface service routines. */
+ int (*open)(struct linux_device *dev);
+ int (*stop)(struct linux_device *dev);
+ int (*hard_start_xmit) (struct sk_buff *skb,
+ struct linux_device *dev);
+ int (*hard_header) (struct sk_buff *skb,
+ struct linux_device *dev,
+ unsigned short type,
+ void *daddr,
+ void *saddr,
+ unsigned len);
+ int (*rebuild_header)(void *eth,
+ struct linux_device *dev,
+ unsigned long raddr, struct sk_buff *skb);
+#define HAVE_MULTICAST
+ void (*set_multicast_list)(struct linux_device *dev);
+#define HAVE_SET_MAC_ADDR
+ int (*set_mac_address)(struct linux_device *dev,
+ void *addr);
+#define HAVE_PRIVATE_IOCTL
+ int (*do_ioctl)(struct linux_device *dev,
+ struct ifreq *ifr, int cmd);
+#define HAVE_SET_CONFIG
+ int (*set_config)(struct linux_device *dev,
+ struct ifmap *map);
+#define HAVE_HEADER_CACHE
+ void (*header_cache_bind)(struct hh_cache **hhp,
+ struct linux_device *dev,
+ unsigned short htype,
+ __u32 daddr);
+ void (*header_cache_update)(struct hh_cache *hh,
+ struct linux_device *dev,
+ unsigned char * haddr);
+#ifdef MACH
+#ifdef MACH_INCLUDE
+ struct net_data *net_data;
+#else
+ void *net_data;
+#endif
+#endif
+};
+
+
+struct packet_type {
+ unsigned short type; /* This is really htons(ether_type). */
+ struct linux_device * dev;
+ int (*func) (struct sk_buff *, struct linux_device *,
+ struct packet_type *);
+ void *data;
+ struct packet_type *next;
+};
+
+
+#ifdef __KERNEL__
+
+#include <linux/notifier.h>
+
+/* Used by dev_rint */
+#define IN_SKBUFF 1
+
+extern volatile unsigned long in_bh;
+
+extern struct linux_device loopback_dev;
+extern struct linux_device *dev_base;
+extern struct packet_type *ptype_base[16];
+
+
+extern int ip_addr_match(unsigned long addr1, unsigned long addr2);
+extern int ip_chk_addr(unsigned long addr);
+extern struct linux_device *ip_dev_check(unsigned long daddr);
+extern unsigned long ip_my_addr(void);
+extern unsigned long ip_get_mask(unsigned long addr);
+extern struct linux_device *ip_dev_find(unsigned long addr);
+extern struct linux_device *dev_getbytype(unsigned short type);
+
+extern void dev_add_pack(struct packet_type *pt);
+extern void dev_remove_pack(struct packet_type *pt);
+extern struct linux_device *dev_get(const char *name);
+extern int dev_open(struct linux_device *dev);
+extern int dev_close(struct linux_device *dev);
+extern void dev_queue_xmit(struct sk_buff *skb,
+ struct linux_device *dev,
+ int pri);
+#define HAVE_NETIF_RX 1
+extern void netif_rx(struct sk_buff *skb);
+extern void dev_transmit(void);
+extern int in_net_bh(void);
+extern void net_bh(void *tmp);
+#ifdef MACH
+#define dev_tint(dev)
+#else
+extern void dev_tint(struct linux_device *dev);
+#endif
+extern int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+extern int dev_ioctl(unsigned int cmd, void *);
+
+extern void dev_init(void);
+
+/* Locking protection for page faults during outputs to devices unloaded during the fault */
+
+extern int dev_lockct;
+
+/*
+ * These two dont currently need to be interrupt safe
+ * but they may do soon. Do it properly anyway.
+ */
+
+extern __inline__ void dev_lock_list(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ dev_lockct++;
+ restore_flags(flags);
+}
+
+extern __inline__ void dev_unlock_list(void)
+{
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ dev_lockct--;
+ restore_flags(flags);
+}
+
+/*
+ * This almost never occurs, isnt in performance critical paths
+ * and we can thus be relaxed about it
+ */
+
+extern __inline__ void dev_lock_wait(void)
+{
+ while(dev_lockct)
+ schedule();
+}
+
+
+/* These functions live elsewhere (drivers/net/net_init.c, but related) */
+
+extern void ether_setup(struct linux_device *dev);
+extern void tr_setup(struct linux_device *dev);
+extern int ether_config(struct linux_device *dev,
+ struct ifmap *map);
+/* Support for loadable net-drivers */
+extern int register_netdev(struct linux_device *dev);
+extern void unregister_netdev(struct linux_device *dev);
+extern int register_netdevice_notifier(struct notifier_block *nb);
+extern int unregister_netdevice_notifier(struct notifier_block *nb);
+/* Functions used for multicast support */
+extern void dev_mc_upload(struct linux_device *dev);
+extern void dev_mc_delete(struct linux_device *dev,
+ void *addr, int alen, int all);
+extern void dev_mc_add(struct linux_device *dev,
+ void *addr, int alen, int newonly);
+extern void dev_mc_discard(struct linux_device *dev);
+/* This is the wrong place but it'll do for the moment */
+extern void ip_mc_allhost(struct linux_device *dev);
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_DEV_H */
diff --git a/i386/i386at/gpl/linux/include/linux/nfs.h b/i386/i386at/gpl/linux/include/linux/nfs.h
new file mode 100644
index 00000000..ceb0cd1b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/nfs.h
@@ -0,0 +1,172 @@
+#ifndef _LINUX_NFS_H
+#define _LINUX_NFS_H
+
+#ifndef MACH_INCLUDE
+#define NFS_PORT 2049
+#define NFS_MAXDATA 8192
+#define NFS_MAXPATHLEN 1024
+#define NFS_MAXNAMLEN 255
+#define NFS_MAXGROUPS 16
+#define NFS_FHSIZE 32
+#define NFS_COOKIESIZE 4
+#define NFS_FIFO_DEV (-1)
+#define NFSMODE_FMT 0170000
+#define NFSMODE_DIR 0040000
+#define NFSMODE_CHR 0020000
+#define NFSMODE_BLK 0060000
+#define NFSMODE_REG 0100000
+#define NFSMODE_LNK 0120000
+#define NFSMODE_SOCK 0140000
+#define NFSMODE_FIFO 0010000
+
+#ifdef __KERNEL__ /* user programs should get these from the rpc header files */
+
+#define RPC_VERSION 2
+
+enum rpc_auth_flavor {
+ RPC_AUTH_NULL = 0,
+ RPC_AUTH_UNIX = 1,
+ RPC_AUTH_SHORT = 2
+};
+
+enum rpc_msg_type {
+ RPC_CALL = 0,
+ RPC_REPLY = 1
+};
+
+enum rpc_reply_stat {
+ RPC_MSG_ACCEPTED = 0,
+ RPC_MSG_DENIED = 1
+};
+
+enum rpc_accept_stat {
+ RPC_SUCCESS = 0,
+ RPC_PROG_UNAVAIL = 1,
+ RPC_PROG_MISMATCH = 2,
+ RPC_PROC_UNAVAIL = 3,
+ RPC_GARBAGE_ARGS = 4
+};
+
+enum rpc_reject_stat {
+ RPC_MISMATCH = 0,
+ RPC_AUTH_ERROR = 1
+};
+
+enum rpc_auth_stat {
+ RPC_AUTH_BADCRED = 1,
+ RPC_AUTH_REJECTEDCRED = 2,
+ RPC_AUTH_BADVERF = 3,
+ RPC_AUTH_REJECTEDVERF = 4,
+ RPC_AUTH_TOOWEAK = 5
+};
+
+#endif /* __KERNEL__ */
+
+enum nfs_stat {
+ NFS_OK = 0,
+ NFSERR_PERM = 1,
+ NFSERR_NOENT = 2,
+ NFSERR_IO = 5,
+ NFSERR_NXIO = 6,
+ NFSERR_EAGAIN = 11,
+ NFSERR_ACCES = 13,
+ NFSERR_EXIST = 17,
+ NFSERR_NODEV = 19,
+ NFSERR_NOTDIR = 20,
+ NFSERR_ISDIR = 21,
+ NFSERR_INVAL = 22, /* that Sun forgot */
+ NFSERR_FBIG = 27,
+ NFSERR_NOSPC = 28,
+ NFSERR_ROFS = 30,
+ NFSERR_NAMETOOLONG = 63,
+ NFSERR_NOTEMPTY = 66,
+ NFSERR_DQUOT = 69,
+ NFSERR_STALE = 70,
+ NFSERR_WFLUSH = 99
+};
+
+enum nfs_ftype {
+ NFNON = 0,
+ NFREG = 1,
+ NFDIR = 2,
+ NFBLK = 3,
+ NFCHR = 4,
+ NFLNK = 5,
+ NFSOCK = 6,
+ NFBAD = 7,
+ NFFIFO = 8
+};
+
+#define NFS_PROGRAM 100003
+#define NFS_VERSION 2
+#define NFSPROC_NULL 0
+#define NFSPROC_GETATTR 1
+#define NFSPROC_SETATTR 2
+#define NFSPROC_ROOT 3
+#define NFSPROC_LOOKUP 4
+#define NFSPROC_READLINK 5
+#define NFSPROC_READ 6
+#define NFSPROC_WRITECACHE 7
+#define NFSPROC_WRITE 8
+#define NFSPROC_CREATE 9
+#define NFSPROC_REMOVE 10
+#define NFSPROC_RENAME 11
+#define NFSPROC_LINK 12
+#define NFSPROC_SYMLINK 13
+#define NFSPROC_MKDIR 14
+#define NFSPROC_RMDIR 15
+#define NFSPROC_READDIR 16
+#define NFSPROC_STATFS 17
+
+struct nfs_fh {
+ char data[NFS_FHSIZE];
+};
+
+struct nfs_time {
+ u_int seconds;
+ u_int useconds;
+};
+
+struct nfs_fattr {
+ enum nfs_ftype type;
+ u_int mode;
+ u_int nlink;
+ u_int uid;
+ u_int gid;
+ u_int size;
+ u_int blocksize;
+ u_int rdev;
+ u_int blocks;
+ u_int fsid;
+ u_int fileid;
+ struct nfs_time atime;
+ struct nfs_time mtime;
+ struct nfs_time ctime;
+};
+
+struct nfs_sattr {
+ u_int mode;
+ u_int uid;
+ u_int gid;
+ u_int size;
+ struct nfs_time atime;
+ struct nfs_time mtime;
+};
+
+struct nfs_entry {
+ u_int fileid;
+ char *name;
+ int cookie;
+ int eof;
+};
+
+struct nfs_fsinfo {
+ u_int tsize;
+ u_int bsize;
+ u_int blocks;
+ u_int bfree;
+ u_int bavail;
+};
+
+#endif /* ! MACH_INCLUDE */
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/notifier.h b/i386/i386at/gpl/linux/include/linux/notifier.h
new file mode 100644
index 00000000..3de4d976
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/notifier.h
@@ -0,0 +1,100 @@
+/*
+ * Routines to manage notifier chains for passing status changes to any
+ * interested routines. We need this instead of hard coded call lists so
+ * that modules can poke their nose into the innards. The network devices
+ * needed them so here they are for the rest of you.
+ *
+ * Alan Cox <Alan.Cox@linux.org>
+ */
+
+#ifndef _LINUX_NOTIFIER_H
+#define _LINUX_NOTIFIER_H
+#include <linux/errno.h>
+
+struct notifier_block
+{
+ int (*notifier_call)(struct notifier_block *this, unsigned long, void *);
+ struct notifier_block *next;
+ int priority;
+};
+
+
+#ifdef __KERNEL__
+
+#define NOTIFY_DONE 0x0000 /* Don't care */
+#define NOTIFY_OK 0x0001 /* Suits me */
+#define NOTIFY_STOP_MASK 0x8000 /* Don't call further */
+#define NOTIFY_BAD (NOTIFY_STOP_MASK|0x0002) /* Bad/Veto action */
+
+extern __inline__ int notifier_chain_register(struct notifier_block **list, struct notifier_block *n)
+{
+ while(*list)
+ {
+ if(n->priority > (*list)->priority)
+ break;
+ list= &((*list)->next);
+ }
+ n->next = *list;
+ *list=n;
+ return 0;
+}
+
+/*
+ * Warning to any non GPL module writers out there.. these functions are
+ * GPL'd
+ */
+
+extern __inline__ int notifier_chain_unregister(struct notifier_block **nl, struct notifier_block *n)
+{
+ while((*nl)!=NULL)
+ {
+ if((*nl)==n)
+ {
+ *nl=n->next;
+ return 0;
+ }
+ nl=&((*nl)->next);
+ }
+#ifdef MACH_INCLUDE
+ return -LINUX_ENOENT;
+#else
+ return -ENOENT;
+#endif
+}
+
+/*
+ * This is one of these things that is generally shorter inline
+ */
+
+extern __inline__ int notifier_call_chain(struct notifier_block **n, unsigned long val, void *v)
+{
+ int ret=NOTIFY_DONE;
+ struct notifier_block *nb = *n;
+ while(nb)
+ {
+ ret=nb->notifier_call(nb,val,v);
+ if(ret&NOTIFY_STOP_MASK)
+ return ret;
+ nb=nb->next;
+ }
+ return ret;
+}
+
+
+/*
+ * Declared notifiers so far. I can imagine quite a few more chains
+ * over time (eg laptop power reset chains, reboot chain (to clean
+ * device units up), device [un]mount chain, module load/unload chain,
+ * low memory chain, screenblank chain (for plug in modular screenblankers)
+ * VC switch chains (for loadable kernel svgalib VC switch helpers) etc...
+ */
+
+/* netdevice notifier chain */
+#define NETDEV_UP 0x0001 /* For now you can't veto a device up/down */
+#define NETDEV_DOWN 0x0002
+#define NETDEV_REBOOT 0x0003 /* Tell a protocol stack a network interface
+ detected a hardware crash and restarted
+ - we can use this eg to kick tcp sessions
+ once done */
+#endif
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/pagemap.h b/i386/i386at/gpl/linux/include/linux/pagemap.h
new file mode 100644
index 00000000..6de993b3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/pagemap.h
@@ -0,0 +1,131 @@
+#ifndef _LINUX_PAGEMAP_H
+#define _LINUX_PAGEMAP_H
+
+#include <asm/system.h>
+
+/*
+ * Page-mapping primitive inline functions
+ *
+ * Copyright 1995 Linus Torvalds
+ */
+
+#ifndef MACH
+static inline unsigned long page_address(struct page * page)
+{
+ return PAGE_OFFSET + PAGE_SIZE*(page - mem_map);
+}
+
+#define PAGE_HASH_BITS 10
+#define PAGE_HASH_SIZE (1 << PAGE_HASH_BITS)
+
+#define PAGE_AGE_VALUE 16
+
+extern unsigned long page_cache_size;
+extern struct page * page_hash_table[PAGE_HASH_SIZE];
+
+/*
+ * We use a power-of-two hash table to avoid a modulus,
+ * and get a reasonable hash by knowing roughly how the
+ * inode pointer and offsets are distributed (ie, we
+ * roughly know which bits are "significant")
+ */
+static inline unsigned long _page_hashfn(struct inode * inode, unsigned long offset)
+{
+#define i (((unsigned long) inode)/sizeof(unsigned long))
+#define o (offset >> PAGE_SHIFT)
+#define s(x) ((x)+((x)>>PAGE_HASH_BITS))
+ return s(i+o) & (PAGE_HASH_SIZE-1);
+#undef i
+#undef o
+#undef s
+}
+
+#define page_hash(inode,offset) page_hash_table[_page_hashfn(inode,offset)]
+
+static inline struct page * find_page(struct inode * inode, unsigned long offset)
+{
+ struct page *page;
+ unsigned long flags;
+
+ for (page = page_hash(inode, offset); page ; page = page->next_hash) {
+ if (page->inode != inode)
+ continue;
+ if (page->offset != offset)
+ continue;
+ save_flags(flags);
+ cli();
+ page->referenced = 1;
+ page->count++;
+ restore_flags(flags);
+ break;
+ }
+ return page;
+}
+
+static inline void remove_page_from_hash_queue(struct page * page)
+{
+ struct page **p = &page_hash(page->inode,page->offset);
+
+ page_cache_size--;
+ if (page->next_hash)
+ page->next_hash->prev_hash = page->prev_hash;
+ if (page->prev_hash)
+ page->prev_hash->next_hash = page->next_hash;
+ if (*p == page)
+ *p = page->next_hash;
+ page->next_hash = page->prev_hash = NULL;
+}
+
+static inline void add_page_to_hash_queue(struct inode * inode, struct page * page)
+{
+ struct page **p = &page_hash(inode,page->offset);
+
+ page_cache_size++;
+ page->referenced = 1;
+ page->age = PAGE_AGE_VALUE;
+ page->prev_hash = NULL;
+ if ((page->next_hash = *p) != NULL)
+ page->next_hash->prev_hash = page;
+ *p = page;
+}
+
+static inline void remove_page_from_inode_queue(struct page * page)
+{
+ struct inode * inode = page->inode;
+
+ page->inode = NULL;
+ inode->i_nrpages--;
+ if (inode->i_pages == page)
+ inode->i_pages = page->next;
+ if (page->next)
+ page->next->prev = page->prev;
+ if (page->prev)
+ page->prev->next = page->next;
+ page->next = NULL;
+ page->prev = NULL;
+}
+
+static inline void add_page_to_inode_queue(struct inode * inode, struct page * page)
+{
+ struct page **p = &inode->i_pages;
+
+ inode->i_nrpages++;
+ page->inode = inode;
+ page->prev = NULL;
+ if ((page->next = *p) != NULL)
+ page->next->prev = page;
+ *p = page;
+}
+
+extern void __wait_on_page(struct page *);
+static inline void wait_on_page(struct page * page)
+{
+ if (page->locked)
+ __wait_on_page(page);
+}
+
+extern void update_vm_cache(struct inode *, unsigned long, const char *, int);
+
+#endif /* ! MACH */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/param.h b/i386/i386at/gpl/linux/include/linux/param.h
new file mode 100644
index 00000000..092e92f6
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/param.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_PARAM_H
+#define _LINUX_PARAM_H
+
+#include <asm/param.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/pci.h b/i386/i386at/gpl/linux/include/linux/pci.h
new file mode 100644
index 00000000..9e059501
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/pci.h
@@ -0,0 +1,618 @@
+/*
+ * PCI defines and function prototypes
+ * Copyright 1994, Drew Eckhardt
+ *
+ * For more information, please consult
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ *
+ * Manuals are $25 each or $50 for all three, plus $7 shipping
+ * within the United States, $35 abroad.
+ */
+
+
+
+/* PROCEDURE TO REPORT NEW PCI DEVICES
+ * We are trying to collect informations on new PCI devices, using
+ * the standart PCI identification procedure. If some warning is
+ * displayed at boot time, please report
+ * - /proc/pci
+ * - your exact hardware description. Try to find out
+ * which device is unknown. It may be you mainboard chipset.
+ * PCI-CPU bridge or PCI-ISA bridge.
+ * - If you can't find the actual information in your hardware
+ * booklet, try to read the references of the chip on the board.
+ * - Send all that, with the word PCIPROBE in the subject,
+ * to frederic@cao-vlsi.ibp.fr, and I'll add your device to
+ * the list as soon as possible
+ * fred.
+ */
+
+
+
+#ifndef PCI_H
+#define PCI_H
+
+/*
+ * Under PCI, each device has 256 bytes of configuration address space,
+ * of which the first 64 bytes are standardized as follows:
+ */
+#define PCI_VENDOR_ID 0x00 /* 16 bits */
+#define PCI_DEVICE_ID 0x02 /* 16 bits */
+#define PCI_COMMAND 0x04 /* 16 bits */
+#define PCI_COMMAND_IO 0x1 /* Enable response in I/O space */
+#define PCI_COMMAND_MEMORY 0x2 /* Enable response in Memory space */
+#define PCI_COMMAND_MASTER 0x4 /* Enable bus mastering */
+#define PCI_COMMAND_SPECIAL 0x8 /* Enable response to special cycles */
+#define PCI_COMMAND_INVALIDATE 0x10 /* Use memory write and invalidate */
+#define PCI_COMMAND_VGA_PALETTE 0x20 /* Enable palette snooping */
+#define PCI_COMMAND_PARITY 0x40 /* Enable parity checking */
+#define PCI_COMMAND_WAIT 0x80 /* Enable address/data stepping */
+#define PCI_COMMAND_SERR 0x100 /* Enable SERR */
+#define PCI_COMMAND_FAST_BACK 0x200 /* Enable back-to-back writes */
+
+#define PCI_STATUS 0x06 /* 16 bits */
+#define PCI_STATUS_66MHZ 0x20 /* Support 66 Mhz PCI 2.1 bus */
+#define PCI_STATUS_UDF 0x40 /* Support User Definable Features */
+
+#define PCI_STATUS_FAST_BACK 0x80 /* Accept fast-back to back */
+#define PCI_STATUS_PARITY 0x100 /* Detected parity error */
+#define PCI_STATUS_DEVSEL_MASK 0x600 /* DEVSEL timing */
+#define PCI_STATUS_DEVSEL_FAST 0x000
+#define PCI_STATUS_DEVSEL_MEDIUM 0x200
+#define PCI_STATUS_DEVSEL_SLOW 0x400
+#define PCI_STATUS_SIG_TARGET_ABORT 0x800 /* Set on target abort */
+#define PCI_STATUS_REC_TARGET_ABORT 0x1000 /* Master ack of " */
+#define PCI_STATUS_REC_MASTER_ABORT 0x2000 /* Set on master abort */
+#define PCI_STATUS_SIG_SYSTEM_ERROR 0x4000 /* Set when we drive SERR */
+#define PCI_STATUS_DETECTED_PARITY 0x8000 /* Set on parity error */
+
+#define PCI_CLASS_REVISION 0x08 /* High 24 bits are class, low 8
+ revision */
+#define PCI_REVISION_ID 0x08 /* Revision ID */
+#define PCI_CLASS_PROG 0x09 /* Reg. Level Programming Interface */
+#define PCI_CLASS_DEVICE 0x0a /* Device class */
+
+#define PCI_CACHE_LINE_SIZE 0x0c /* 8 bits */
+#define PCI_LATENCY_TIMER 0x0d /* 8 bits */
+#define PCI_HEADER_TYPE 0x0e /* 8 bits */
+#define PCI_BIST 0x0f /* 8 bits */
+#define PCI_BIST_CODE_MASK 0x0f /* Return result */
+#define PCI_BIST_START 0x40 /* 1 to start BIST, 2 secs or less */
+#define PCI_BIST_CAPABLE 0x80 /* 1 if BIST capable */
+
+/*
+ * Base addresses specify locations in memory or I/O space.
+ * Decoded size can be determined by writing a value of
+ * 0xffffffff to the register, and reading it back. Only
+ * 1 bits are decoded.
+ */
+#define PCI_BASE_ADDRESS_0 0x10 /* 32 bits */
+#define PCI_BASE_ADDRESS_1 0x14 /* 32 bits */
+#define PCI_BASE_ADDRESS_2 0x18 /* 32 bits */
+#define PCI_BASE_ADDRESS_3 0x1c /* 32 bits */
+#define PCI_BASE_ADDRESS_4 0x20 /* 32 bits */
+#define PCI_BASE_ADDRESS_5 0x24 /* 32 bits */
+#define PCI_BASE_ADDRESS_SPACE 0x01 /* 0 = memory, 1 = I/O */
+#define PCI_BASE_ADDRESS_SPACE_IO 0x01
+#define PCI_BASE_ADDRESS_SPACE_MEMORY 0x00
+#define PCI_BASE_ADDRESS_MEM_TYPE_MASK 0x06
+#define PCI_BASE_ADDRESS_MEM_TYPE_32 0x00 /* 32 bit address */
+#define PCI_BASE_ADDRESS_MEM_TYPE_1M 0x02 /* Below 1M */
+#define PCI_BASE_ADDRESS_MEM_TYPE_64 0x04 /* 64 bit address */
+#define PCI_BASE_ADDRESS_MEM_PREFETCH 0x08 /* prefetchable? */
+#define PCI_BASE_ADDRESS_MEM_MASK (~0x0f)
+#define PCI_BASE_ADDRESS_IO_MASK (~0x03)
+/* bit 1 is reserved if address_space = 1 */
+
+#define PCI_CARDBUS_CIS 0x28
+#define PCI_SUBSYSTEM_ID 0x2c
+#define PCI_SUBSYSTEM_VENDOR_ID 0x2e
+#define PCI_ROM_ADDRESS 0x30 /* 32 bits */
+#define PCI_ROM_ADDRESS_ENABLE 0x01 /* Write 1 to enable ROM,
+ bits 31..11 are address,
+ 10..2 are reserved */
+/* 0x34-0x3b are reserved */
+#define PCI_INTERRUPT_LINE 0x3c /* 8 bits */
+#define PCI_INTERRUPT_PIN 0x3d /* 8 bits */
+#define PCI_MIN_GNT 0x3e /* 8 bits */
+#define PCI_MAX_LAT 0x3f /* 8 bits */
+
+#define PCI_CLASS_NOT_DEFINED 0x0000
+#define PCI_CLASS_NOT_DEFINED_VGA 0x0001
+
+#define PCI_BASE_CLASS_STORAGE 0x01
+#define PCI_CLASS_STORAGE_SCSI 0x0100
+#define PCI_CLASS_STORAGE_IDE 0x0101
+#define PCI_CLASS_STORAGE_FLOPPY 0x0102
+#define PCI_CLASS_STORAGE_IPI 0x0103
+#define PCI_CLASS_STORAGE_RAID 0x0104
+#define PCI_CLASS_STORAGE_OTHER 0x0180
+
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_CLASS_NETWORK_ETHERNET 0x0200
+#define PCI_CLASS_NETWORK_TOKEN_RING 0x0201
+#define PCI_CLASS_NETWORK_FDDI 0x0202
+#define PCI_CLASS_NETWORK_ATM 0x0203
+#define PCI_CLASS_NETWORK_OTHER 0x0280
+
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_CLASS_DISPLAY_VGA 0x0300
+#define PCI_CLASS_DISPLAY_XGA 0x0301
+#define PCI_CLASS_DISPLAY_OTHER 0x0380
+
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_CLASS_MULTIMEDIA_VIDEO 0x0400
+#define PCI_CLASS_MULTIMEDIA_AUDIO 0x0401
+#define PCI_CLASS_MULTIMEDIA_OTHER 0x0480
+
+#define PCI_BASE_CLASS_MEMORY 0x05
+#define PCI_CLASS_MEMORY_RAM 0x0500
+#define PCI_CLASS_MEMORY_FLASH 0x0501
+#define PCI_CLASS_MEMORY_OTHER 0x0580
+
+#define PCI_BASE_CLASS_BRIDGE 0x06
+#define PCI_CLASS_BRIDGE_HOST 0x0600
+#define PCI_CLASS_BRIDGE_ISA 0x0601
+#define PCI_CLASS_BRIDGE_EISA 0x0602
+#define PCI_CLASS_BRIDGE_MC 0x0603
+#define PCI_CLASS_BRIDGE_PCI 0x0604
+#define PCI_CLASS_BRIDGE_PCMCIA 0x0605
+#define PCI_CLASS_BRIDGE_NUBUS 0x0606
+#define PCI_CLASS_BRIDGE_CARDBUS 0x0607
+#define PCI_CLASS_BRIDGE_OTHER 0x0680
+
+
+#define PCI_BASE_CLASS_COMMUNICATION 0x07
+#define PCI_CLASS_COMMUNICATION_SERIAL 0x0700
+#define PCI_CLASS_COMMUNICATION_PARALLEL 0x0701
+#define PCI_CLASS_COMMUNICATION_OTHER 0x0780
+
+#define PCI_BASE_CLASS_SYSTEM 0x08
+#define PCI_CLASS_SYSTEM_PIC 0x0800
+#define PCI_CLASS_SYSTEM_DMA 0x0801
+#define PCI_CLASS_SYSTEM_TIMER 0x0802
+#define PCI_CLASS_SYSTEM_RTC 0x0803
+#define PCI_CLASS_SYSTEM_OTHER 0x0880
+
+#define PCI_BASE_CLASS_INPUT 0x09
+#define PCI_CLASS_INPUT_KEYBOARD 0x0900
+#define PCI_CLASS_INPUT_PEN 0x0901
+#define PCI_CLASS_INPUT_MOUSE 0x0902
+#define PCI_CLASS_INPUT_OTHER 0x0980
+
+#define PCI_BASE_CLASS_DOCKING 0x0a
+#define PCI_CLASS_DOCKING_GENERIC 0x0a00
+#define PCI_CLASS_DOCKING_OTHER 0x0a01
+
+#define PCI_BASE_CLASS_PROCESSOR 0x0b
+#define PCI_CLASS_PROCESSOR_386 0x0b00
+#define PCI_CLASS_PROCESSOR_486 0x0b01
+#define PCI_CLASS_PROCESSOR_PENTIUM 0x0b02
+#define PCI_CLASS_PROCESSOR_ALPHA 0x0b10
+#define PCI_CLASS_PROCESSOR_POWERPC 0x0b20
+#define PCI_CLASS_PROCESSOR_CO 0x0b40
+
+#define PCI_BASE_CLASS_SERIAL 0x0c
+#define PCI_CLASS_SERIAL_FIREWIRE 0x0c00
+#define PCI_CLASS_SERIAL_ACCESS 0x0c01
+#define PCI_CLASS_SERIAL_SSA 0x0c02
+#define PCI_CLASS_SERIAL_USB 0x0c03
+#define PCI_CLASS_SERIAL_FIBER 0x0c04
+
+#define PCI_CLASS_OTHERS 0xff
+
+/*
+ * Vendor and card ID's: sort these numerically according to vendor
+ * (and according to card ID within vendor)
+ */
+#define PCI_VENDOR_ID_COMPAQ 0x0e11
+#define PCI_DEVICE_ID_COMPAQ_1280 0x3033
+#define PCI_DEVICE_ID_COMPAQ_THUNDER 0xf130
+
+#define PCI_VENDOR_ID_NCR 0x1000
+#define PCI_DEVICE_ID_NCR_53C810 0x0001
+#define PCI_DEVICE_ID_NCR_53C820 0x0002
+#define PCI_DEVICE_ID_NCR_53C825 0x0003
+#define PCI_DEVICE_ID_NCR_53C815 0x0004
+
+#define PCI_VENDOR_ID_ATI 0x1002
+#define PCI_DEVICE_ID_ATI_68800 0x4158
+#define PCI_DEVICE_ID_ATI_215CT222 0x4354
+#define PCI_DEVICE_ID_ATI_210888CX 0x4358
+#define PCI_DEVICE_ID_ATI_210888GX 0x4758
+
+#define PCI_VENDOR_ID_VLSI 0x1004
+#define PCI_DEVICE_ID_VLSI_82C592 0x0005
+#define PCI_DEVICE_ID_VLSI_82C593 0x0006
+#define PCI_DEVICE_ID_VLSI_82C594 0x0007
+#define PCI_DEVICE_ID_VLSI_82C597 0x0009
+
+#define PCI_VENDOR_ID_ADL 0x1005
+#define PCI_DEVICE_ID_ADL_2301 0x2301
+
+#define PCI_VENDOR_ID_NS 0x100b
+#define PCI_DEVICE_ID_NS_87410 0xd001
+
+#define PCI_VENDOR_ID_TSENG 0x100c
+#define PCI_DEVICE_ID_TSENG_W32P_2 0x3202
+#define PCI_DEVICE_ID_TSENG_W32P_b 0x3205
+#define PCI_DEVICE_ID_TSENG_W32P_c 0x3206
+#define PCI_DEVICE_ID_TSENG_W32P_d 0x3207
+
+#define PCI_VENDOR_ID_WEITEK 0x100e
+#define PCI_DEVICE_ID_WEITEK_P9000 0x9001
+#define PCI_DEVICE_ID_WEITEK_P9100 0x9100
+
+#define PCI_VENDOR_ID_DEC 0x1011
+#define PCI_DEVICE_ID_DEC_BRD 0x0001
+#define PCI_DEVICE_ID_DEC_TULIP 0x0002
+#define PCI_DEVICE_ID_DEC_TGA 0x0004
+#define PCI_DEVICE_ID_DEC_TULIP_FAST 0x0009
+#define PCI_DEVICE_ID_DEC_FDDI 0x000F
+#define PCI_DEVICE_ID_DEC_TULIP_PLUS 0x0014
+
+#define PCI_VENDOR_ID_CIRRUS 0x1013
+#define PCI_DEVICE_ID_CIRRUS_5430 0x00a0
+#define PCI_DEVICE_ID_CIRRUS_5434_4 0x00a4
+#define PCI_DEVICE_ID_CIRRUS_5434_8 0x00a8
+#define PCI_DEVICE_ID_CIRRUS_5436 0x00ac
+#define PCI_DEVICE_ID_CIRRUS_6205 0x0205
+#define PCI_DEVICE_ID_CIRRUS_6729 0x1100
+#define PCI_DEVICE_ID_CIRRUS_7542 0x1200
+#define PCI_DEVICE_ID_CIRRUS_7543 0x1202
+
+#define PCI_VENDOR_ID_IBM 0x1014
+#define PCI_DEVICE_ID_IBM_82G2675 0x001d
+
+#define PCI_VENDOR_ID_WD 0x101c
+#define PCI_DEVICE_ID_WD_7197 0x3296
+
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_LANCE 0x2000
+#define PCI_DEVICE_ID_AMD_SCSI 0x2020
+
+#define PCI_VENDOR_ID_TRIDENT 0x1023
+#define PCI_DEVICE_ID_TRIDENT_9420 0x9420
+#define PCI_DEVICE_ID_TRIDENT_9440 0x9440
+#define PCI_DEVICE_ID_TRIDENT_9660 0x9660
+
+#define PCI_VENDOR_ID_AI 0x1025
+#define PCI_DEVICE_ID_AI_M1435 0x1435
+
+#define PCI_VENDOR_ID_MATROX 0x102B
+#define PCI_DEVICE_ID_MATROX_MGA_2 0x0518
+#define PCI_DEVICE_ID_MATROX_MIL 0x0519
+#define PCI_DEVICE_ID_MATROX_MGA_IMP 0x0d10
+
+#define PCI_VENDOR_ID_CT 0x102c
+#define PCI_DEVICE_ID_CT_65545 0x00d8
+
+#define PCI_VENDOR_ID_FD 0x1036
+#define PCI_DEVICE_ID_FD_36C70 0x0000
+
+#define PCI_VENDOR_ID_SI 0x1039
+#define PCI_DEVICE_ID_SI_6201 0x0001
+#define PCI_DEVICE_ID_SI_6202 0x0002
+#define PCI_DEVICE_ID_SI_503 0x0008
+#define PCI_DEVICE_ID_SI_501 0x0406
+#define PCI_DEVICE_ID_SI_496 0x0496
+#define PCI_DEVICE_ID_SI_601 0x0601
+#define PCI_DEVICE_ID_SI_5511 0x5511
+#define PCI_DEVICE_ID_SI_5513 0x5513
+
+#define PCI_VENDOR_ID_HP 0x103c
+#define PCI_DEVICE_ID_HP_J2585A 0x1030
+
+#define PCI_VENDOR_ID_PCTECH 0x1042
+#define PCI_DEVICE_ID_PCTECH_RZ1000 0x1000
+
+#define PCI_VENDOR_ID_DPT 0x1044
+#define PCI_DEVICE_ID_DPT 0xa400
+
+#define PCI_VENDOR_ID_OPTI 0x1045
+#define PCI_DEVICE_ID_OPTI_92C178 0xc178
+#define PCI_DEVICE_ID_OPTI_82C557 0xc557
+#define PCI_DEVICE_ID_OPTI_82C558 0xc558
+#define PCI_DEVICE_ID_OPTI_82C621 0xc621
+#define PCI_DEVICE_ID_OPTI_82C822 0xc822
+
+#define PCI_VENDOR_ID_SGS 0x104a
+#define PCI_DEVICE_ID_SGS_2000 0x0008
+#define PCI_DEVICE_ID_SGS_1764 0x0009
+
+#define PCI_VENDOR_ID_BUSLOGIC 0x104B
+#define PCI_DEVICE_ID_BUSLOGIC_946C_2 0x0140
+#define PCI_DEVICE_ID_BUSLOGIC_946C 0x1040
+#define PCI_DEVICE_ID_BUSLOGIC_930 0x8130
+
+#define PCI_VENDOR_ID_OAK 0x104e
+#define PCI_DEVICE_ID_OAK_OTI107 0x0107
+
+#define PCI_VENDOR_ID_PROMISE 0x105a
+#define PCI_DEVICE_ID_PROMISE_5300 0x5300
+
+#define PCI_VENDOR_ID_N9 0x105d
+#define PCI_DEVICE_ID_N9_I128 0x2309
+#define PCI_DEVICE_ID_N9_I128_2 0x2339
+
+#define PCI_VENDOR_ID_UMC 0x1060
+#define PCI_DEVICE_ID_UMC_UM8673F 0x0101
+#define PCI_DEVICE_ID_UMC_UM8891A 0x0891
+#define PCI_DEVICE_ID_UMC_UM8886BF 0x673a
+#define PCI_DEVICE_ID_UMC_UM8886A 0x886a
+#define PCI_DEVICE_ID_UMC_UM8881F 0x8881
+#define PCI_DEVICE_ID_UMC_UM8886F 0x8886
+#define PCI_DEVICE_ID_UMC_UM9017F 0x9017
+#define PCI_DEVICE_ID_UMC_UM8886N 0xe886
+#define PCI_DEVICE_ID_UMC_UM8891N 0xe891
+
+#define PCI_VENDOR_ID_X 0x1061
+#define PCI_DEVICE_ID_X_AGX016 0x0001
+
+#define PCI_VENDOR_ID_NEXGEN 0x1074
+#define PCI_DEVICE_ID_NEXGEN_82C501 0x4e78
+
+#define PCI_VENDOR_ID_QLOGIC 0x1077
+#define PCI_DEVICE_ID_QLOGIC_ISP1020 0x1020
+#define PCI_DEVICE_ID_QLOGIC_ISP1022 0x1022
+
+#define PCI_VENDOR_ID_LEADTEK 0x107d
+#define PCI_DEVICE_ID_LEADTEK_805 0x0000
+
+#define PCI_VENDOR_ID_CONTAQ 0x1080
+#define PCI_DEVICE_ID_CONTAQ_82C599 0x0600
+
+#define PCI_VENDOR_ID_FOREX 0x1083
+
+#define PCI_VENDOR_ID_OLICOM 0x108d
+
+#define PCI_VENDOR_ID_CMD 0x1095
+#define PCI_DEVICE_ID_CMD_640 0x0640
+#define PCI_DEVICE_ID_CMD_646 0x0646
+
+#define PCI_VENDOR_ID_VISION 0x1098
+#define PCI_DEVICE_ID_VISION_QD8500 0x0001
+#define PCI_DEVICE_ID_VISION_QD8580 0x0002
+
+#define PCI_VENDOR_ID_SIERRA 0x10a8
+#define PCI_DEVICE_ID_SIERRA_STB 0x0000
+
+#define PCI_VENDOR_ID_ACC 0x10aa
+#define PCI_DEVICE_ID_ACC_2056 0x0000
+
+#define PCI_VENDOR_ID_WINBOND 0x10ad
+#define PCI_DEVICE_ID_WINBOND_83769 0x0001
+#define PCI_DEVICE_ID_WINBOND_82C105 0x0105
+
+#define PCI_VENDOR_ID_3COM 0x10b7
+#define PCI_DEVICE_ID_3COM_3C590 0x5900
+#define PCI_DEVICE_ID_3COM_3C595TX 0x5950
+#define PCI_DEVICE_ID_3COM_3C595T4 0x5951
+#define PCI_DEVICE_ID_3COM_3C595MII 0x5952
+
+#define PCI_VENDOR_ID_AL 0x10b9
+#define PCI_DEVICE_ID_AL_M1445 0x1445
+#define PCI_DEVICE_ID_AL_M1449 0x1449
+#define PCI_DEVICE_ID_AL_M1451 0x1451
+#define PCI_DEVICE_ID_AL_M1461 0x1461
+#define PCI_DEVICE_ID_AL_M1489 0x1489
+#define PCI_DEVICE_ID_AL_M1511 0x1511
+#define PCI_DEVICE_ID_AL_M1513 0x1513
+#define PCI_DEVICE_ID_AL_M4803 0x5215
+
+#define PCI_VENDOR_ID_ASP 0x10cd
+#define PCI_DEVICE_ID_ASP_ABP940 0x1200
+
+#define PCI_VENDOR_ID_IMS 0x10e0
+#define PCI_DEVICE_ID_IMS_8849 0x8849
+
+#define PCI_VENDOR_ID_TEKRAM2 0x10e1
+#define PCI_DEVICE_ID_TEKRAM2_690c 0x690c
+
+#define PCI_VENDOR_ID_AMCC 0x10e8
+#define PCI_DEVICE_ID_AMCC_MYRINET 0x8043
+
+#define PCI_VENDOR_ID_INTERG 0x10ea
+#define PCI_DEVICE_ID_INTERG_1680 0x1680
+
+#define PCI_VENDOR_ID_REALTEK 0x10ec
+#define PCI_DEVICE_ID_REALTEK_8029 0x8029
+
+#define PCI_VENDOR_ID_INIT 0x1101
+#define PCI_DEVICE_ID_INIT_320P 0x9100
+
+#define PCI_VENDOR_ID_VIA 0x1106
+#define PCI_DEVICE_ID_VIA_82C505 0x0505
+#define PCI_DEVICE_ID_VIA_82C561 0x0561
+#define PCI_DEVICE_ID_VIA_82C576 0x0576
+#define PCI_DEVICE_ID_VIA_82C416 0x1571
+
+#define PCI_VENDOR_ID_VORTEX 0x1119
+#define PCI_DEVICE_ID_VORTEX_GDT 0x0001
+
+#define PCI_VENDOR_ID_EF 0x111a
+#define PCI_DEVICE_ID_EF_ATM_FPGA 0x0000
+#define PCI_DEVICE_ID_EF_ATM_ASIC 0x0002
+
+#define PCI_VENDOR_ID_FORE 0x1127
+#define PCI_DEVICE_ID_FORE_PCA200PC 0x0210
+
+#define PCI_VENDOR_ID_IMAGINGTECH 0x112f
+#define PCI_DEVICE_ID_IMAGINGTECH_ICPCI 0x0000
+
+#define PCI_VENDOR_ID_PLX 0x113c
+#define PCI_DEVICE_ID_PLX_9060 0x0001
+
+#define PCI_VENDOR_ID_ALLIANCE 0x1142
+#define PCI_DEVICE_ID_ALLIANCE_PROMOTIO 0x3210
+#define PCI_DEVICE_ID_ALLIANCE_PROVIDEO 0x6422
+
+#define PCI_VENDOR_ID_MUTECH 0x1159
+#define PCI_DEVICE_ID_MUTECH_MV1000 0x0001
+
+#define PCI_VENDOR_ID_ZEITNET 0x1193
+#define PCI_DEVICE_ID_ZEITNET_1221 0x0001
+#define PCI_DEVICE_ID_ZEITNET_1225 0x0002
+
+#define PCI_VENDOR_ID_SPECIALIX 0x11cb
+#define PCI_DEVICE_ID_SPECIALIX_XIO 0x4000
+#define PCI_DEVICE_ID_SPECIALIX_RIO 0x8000
+
+#define PCI_VENDOR_ID_RP 0x11fe
+#define PCI_DEVICE_ID_RP8OCTA 0x0001
+#define PCI_DEVICE_ID_RP8INTF 0x0002
+#define PCI_DEVICE_ID_RP16INTF 0x0003
+#define PCI_DEVICE_ID_RP32INTF 0x0004
+
+#define PCI_VENDOR_ID_CYCLADES 0x120e
+#define PCI_DEVICE_ID_CYCLADES_Y 0x0100
+
+#define PCI_VENDOR_ID_SYMPHONY 0x1c1c
+#define PCI_DEVICE_ID_SYMPHONY_101 0x0001
+
+#define PCI_VENDOR_ID_TEKRAM 0x1de1
+#define PCI_DEVICE_ID_TEKRAM_DC290 0xdc29
+
+#define PCI_VENDOR_ID_AVANCE 0x4005
+#define PCI_DEVICE_ID_AVANCE_2302 0x2302
+
+#define PCI_VENDOR_ID_S3 0x5333
+#define PCI_DEVICE_ID_S3_811 0x8811
+#define PCI_DEVICE_ID_S3_868 0x8880
+#define PCI_DEVICE_ID_S3_928 0x88b0
+#define PCI_DEVICE_ID_S3_864_1 0x88c0
+#define PCI_DEVICE_ID_S3_864_2 0x88c1
+#define PCI_DEVICE_ID_S3_964_1 0x88d0
+#define PCI_DEVICE_ID_S3_964_2 0x88d1
+#define PCI_DEVICE_ID_S3_968 0x88f0
+
+#define PCI_VENDOR_ID_INTEL 0x8086
+#define PCI_DEVICE_ID_INTEL_82375 0x0482
+#define PCI_DEVICE_ID_INTEL_82424 0x0483
+#define PCI_DEVICE_ID_INTEL_82378 0x0484
+#define PCI_DEVICE_ID_INTEL_82430 0x0486
+#define PCI_DEVICE_ID_INTEL_82434 0x04a3
+#define PCI_DEVICE_ID_INTEL_7116 0x1223
+#define PCI_DEVICE_ID_INTEL_82596 0x1226
+#define PCI_DEVICE_ID_INTEL_82865 0x1227
+#define PCI_DEVICE_ID_INTEL_82557 0x1229
+#define PCI_DEVICE_ID_INTEL_82437 0x122d
+#define PCI_DEVICE_ID_INTEL_82371_0 0x122e
+#define PCI_DEVICE_ID_INTEL_82371_1 0x1230
+#define PCI_DEVICE_ID_INTEL_P6 0x84c4
+
+#define PCI_VENDOR_ID_ADAPTEC 0x9004
+#define PCI_DEVICE_ID_ADAPTEC_7850 0x5078
+#define PCI_DEVICE_ID_ADAPTEC_7870 0x7078
+#define PCI_DEVICE_ID_ADAPTEC_7871 0x7178
+#define PCI_DEVICE_ID_ADAPTEC_7872 0x7278
+#define PCI_DEVICE_ID_ADAPTEC_7873 0x7378
+#define PCI_DEVICE_ID_ADAPTEC_7874 0x7478
+#define PCI_DEVICE_ID_ADAPTEC_7880 0x8078
+#define PCI_DEVICE_ID_ADAPTEC_7881 0x8178
+#define PCI_DEVICE_ID_ADAPTEC_7882 0x8278
+#define PCI_DEVICE_ID_ADAPTEC_7883 0x8378
+#define PCI_DEVICE_ID_ADAPTEC_7884 0x8478
+
+#define PCI_VENDOR_ID_ATRONICS 0x907f
+#define PCI_DEVICE_ID_ATRONICS_2015 0x2015
+
+#define PCI_VENDOR_ID_HER 0xedd8
+#define PCI_DEVICE_ID_HER_STING 0xa091
+#define PCI_DEVICE_ID_HER_STINGARK 0xa099
+
+/*
+ * The PCI interface treats multi-function devices as independent
+ * devices. The slot/function address of each device is encoded
+ * in a single byte as follows:
+ *
+ * 7:4 = slot
+ * 3:0 = function
+ */
+#define PCI_DEVFN(slot,func) ((((slot) & 0x1f) << 3) | ((func) & 0x07))
+#define PCI_SLOT(devfn) (((devfn) >> 3) & 0x1f)
+#define PCI_FUNC(devfn) ((devfn) & 0x07)
+
+/*
+ * There is one pci_dev structure for each slot-number/function-number
+ * combination:
+ */
+struct pci_dev {
+ struct pci_bus *bus; /* bus this device is on */
+ struct pci_dev *sibling; /* next device on this bus */
+ struct pci_dev *next; /* chain of all devices */
+
+ void *sysdata; /* hook for sys-specific extension */
+
+ unsigned int devfn; /* encoded device & function index */
+ unsigned short vendor;
+ unsigned short device;
+ unsigned int class; /* 3 bytes: (base,sub,prog-if) */
+ unsigned int master : 1; /* set if device is master capable */
+ /*
+ * In theory, the irq level can be read from configuration
+ * space and all would be fine. However, old PCI chips don't
+ * support these registers and return 0 instead. For example,
+ * the Vision864-P rev 0 chip can uses INTA, but returns 0 in
+ * the interrupt line and pin registers. pci_init()
+ * initializes this field with the value at PCI_INTERRUPT_LINE
+ * and it is the job of pcibios_fixup() to change it if
+ * necessary. The field must not be 0 unless the device
+ * cannot generate interrupts at all.
+ */
+ unsigned char irq; /* irq generated by this device */
+};
+
+struct pci_bus {
+ struct pci_bus *parent; /* parent bus this bridge is on */
+ struct pci_bus *children; /* chain of P2P bridges on this bus */
+ struct pci_bus *next; /* chain of all PCI buses */
+
+ struct pci_dev *self; /* bridge device as seen by parent */
+ struct pci_dev *devices; /* devices behind this bridge */
+
+ void *sysdata; /* hook for sys-specific extension */
+
+ unsigned char number; /* bus number */
+ unsigned char primary; /* number of primary bridge */
+ unsigned char secondary; /* number of secondary bridge */
+ unsigned char subordinate; /* max number of subordinate buses */
+};
+
+/*
+ * This is used to map a vendor-id/device-id pair into device-specific
+ * information.
+ */
+struct pci_dev_info {
+ unsigned short vendor; /* vendor id */
+ unsigned short device; /* device id */
+
+ const char *name; /* device name */
+ unsigned char bridge_type; /* bridge type or 0xff */
+};
+
+extern struct pci_bus pci_root; /* root bus */
+extern struct pci_dev *pci_devices; /* list of all devices */
+
+
+extern unsigned long pci_init (unsigned long mem_start, unsigned long mem_end);
+
+extern struct pci_dev_info *pci_lookup_dev (unsigned int vendor,
+ unsigned int dev);
+extern const char *pci_strclass (unsigned int class);
+extern const char *pci_strvendor (unsigned int vendor);
+extern const char *pci_strdev (unsigned int vendor, unsigned int device);
+
+extern int get_pci_list (char *buf);
+
+#endif /* PCI_H */
diff --git a/i386/i386at/gpl/linux/include/linux/personality.h b/i386/i386at/gpl/linux/include/linux/personality.h
new file mode 100644
index 00000000..3e465eaa
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/personality.h
@@ -0,0 +1,51 @@
+#ifndef _PERSONALITY_H
+#define _PERSONALITY_H
+
+#include <linux/linkage.h>
+#include <linux/ptrace.h>
+
+
+/* Flags for bug emulation. These occupy the top three bytes. */
+#define STICKY_TIMEOUTS 0x4000000
+#define WHOLE_SECONDS 0x2000000
+
+/* Personality types. These go in the low byte. Avoid using the top bit,
+ * it will conflict with error returns.
+ */
+#define PER_MASK (0x00ff)
+#define PER_LINUX (0x0000)
+#define PER_SVR4 (0x0001 | STICKY_TIMEOUTS)
+#define PER_SVR3 (0x0002 | STICKY_TIMEOUTS)
+#define PER_SCOSVR3 (0x0003 | STICKY_TIMEOUTS | WHOLE_SECONDS)
+#define PER_WYSEV386 (0x0004 | STICKY_TIMEOUTS)
+#define PER_ISCR4 (0x0005 | STICKY_TIMEOUTS)
+#define PER_BSD (0x0006)
+#define PER_XENIX (0x0007 | STICKY_TIMEOUTS)
+
+/* Prototype for an lcall7 syscall handler. */
+typedef asmlinkage void (*lcall7_func)(struct pt_regs *);
+
+
+/* Description of an execution domain - personality range supported,
+ * lcall7 syscall handler, start up / shut down functions etc.
+ * N.B. The name and lcall7 handler must be where they are since the
+ * offset of the handler is hard coded in kernel/sys_call.S.
+ */
+struct exec_domain {
+ const char *name;
+ lcall7_func handler;
+ unsigned char pers_low, pers_high;
+ unsigned long * signal_map;
+ unsigned long * signal_invmap;
+ int *use_count;
+ struct exec_domain *next;
+};
+
+extern struct exec_domain default_exec_domain;
+
+extern struct exec_domain *lookup_exec_domain(unsigned long personality);
+extern int register_exec_domain(struct exec_domain *it);
+extern int unregister_exec_domain(struct exec_domain *it);
+extern asmlinkage int sys_personality(unsigned long personality);
+
+#endif /* _PERSONALITY_H */
diff --git a/i386/i386at/gpl/linux/include/linux/proc_fs.h b/i386/i386at/gpl/linux/include/linux/proc_fs.h
new file mode 100644
index 00000000..cc674c9b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/proc_fs.h
@@ -0,0 +1,269 @@
+#ifndef _LINUX_PROC_FS_H
+#define _LINUX_PROC_FS_H
+
+#include <linux/fs.h>
+#include <linux/malloc.h>
+
+/*
+ * The proc filesystem constants/structures
+ */
+
+/*
+ * We always define these enumerators
+ */
+
+enum root_directory_inos {
+ PROC_ROOT_INO = 1,
+ PROC_LOADAVG,
+ PROC_UPTIME,
+ PROC_MEMINFO,
+ PROC_KMSG,
+ PROC_VERSION,
+ PROC_CPUINFO,
+ PROC_PCI,
+ PROC_SELF, /* will change inode # */
+ PROC_NET,
+ PROC_SCSI,
+ PROC_MALLOC,
+ PROC_KCORE,
+ PROC_MODULES,
+ PROC_STAT,
+ PROC_DEVICES,
+ PROC_INTERRUPTS,
+ PROC_FILESYSTEMS,
+ PROC_KSYMS,
+ PROC_DMA,
+ PROC_IOPORTS,
+ PROC_APM,
+#ifdef __SMP_PROF__
+ PROC_SMP_PROF,
+#endif
+ PROC_PROFILE, /* whether enabled or not */
+ PROC_CMDLINE,
+ PROC_SYS,
+ PROC_MTAB
+};
+
+enum pid_directory_inos {
+ PROC_PID_INO = 2,
+ PROC_PID_STATUS,
+ PROC_PID_MEM,
+ PROC_PID_CWD,
+ PROC_PID_ROOT,
+ PROC_PID_EXE,
+ PROC_PID_FD,
+ PROC_PID_ENVIRON,
+ PROC_PID_CMDLINE,
+ PROC_PID_STAT,
+ PROC_PID_STATM,
+ PROC_PID_MAPS
+};
+
+enum pid_subdirectory_inos {
+ PROC_PID_FD_DIR = 1
+};
+
+enum net_directory_inos {
+ PROC_NET_UNIX = 128,
+ PROC_NET_ARP,
+ PROC_NET_ROUTE,
+ PROC_NET_DEV,
+ PROC_NET_RAW,
+ PROC_NET_TCP,
+ PROC_NET_UDP,
+ PROC_NET_SNMP,
+ PROC_NET_RARP,
+ PROC_NET_IGMP,
+ PROC_NET_IPMR_VIF,
+ PROC_NET_IPMR_MFC,
+ PROC_NET_IPFWFWD,
+ PROC_NET_IPFWIN,
+ PROC_NET_IPFWOUT,
+ PROC_NET_IPACCT,
+ PROC_NET_IPMSQHST,
+ PROC_NET_WAVELAN,
+ PROC_NET_IPX_INTERFACE,
+ PROC_NET_IPX_ROUTE,
+ PROC_NET_IPX,
+ PROC_NET_ATALK,
+ PROC_NET_AT_ROUTE,
+ PROC_NET_ATIF,
+ PROC_NET_AX25_ROUTE,
+ PROC_NET_AX25,
+ PROC_NET_AX25_CALLS,
+ PROC_NET_NR_NODES,
+ PROC_NET_NR_NEIGH,
+ PROC_NET_NR,
+ PROC_NET_SOCKSTAT,
+ PROC_NET_RTCACHE,
+ PROC_NET_AX25_BPQETHER,
+ PROC_NET_ALIAS_TYPES,
+ PROC_NET_ALIASES,
+ PROC_NET_LAST
+};
+
+enum scsi_directory_inos {
+ PROC_SCSI_SCSI = 256,
+ PROC_SCSI_ADVANSYS,
+ PROC_SCSI_EATA,
+ PROC_SCSI_EATA_PIO,
+ PROC_SCSI_AHA152X,
+ PROC_SCSI_AHA1542,
+ PROC_SCSI_AHA1740,
+ PROC_SCSI_AIC7XXX,
+ PROC_SCSI_BUSLOGIC,
+ PROC_SCSI_U14_34F,
+ PROC_SCSI_FDOMAIN,
+ PROC_SCSI_GENERIC_NCR5380,
+ PROC_SCSI_IN2000,
+ PROC_SCSI_PAS16,
+ PROC_SCSI_QLOGIC,
+ PROC_SCSI_SEAGATE,
+ PROC_SCSI_T128,
+ PROC_SCSI_NCR53C7xx,
+ PROC_SCSI_ULTRASTOR,
+ PROC_SCSI_7000FASST,
+ PROC_SCSI_EATA2X,
+ PROC_SCSI_AM53C974,
+ PROC_SCSI_SSC,
+ PROC_SCSI_NCR53C406A,
+ PROC_SCSI_SCSI_DEBUG,
+ PROC_SCSI_NOT_PRESENT,
+ PROC_SCSI_FILE, /* I'm asuming here that we */
+ PROC_SCSI_LAST = (PROC_SCSI_FILE + 16) /* won't ever see more than */
+}; /* 16 HBAs in one machine */
+
+/* Finally, the dynamically allocatable proc entries are reserved: */
+
+#define PROC_DYNAMIC_FIRST 4096
+#define PROC_NDYNAMIC 4096
+
+#define PROC_SUPER_MAGIC 0x9fa0
+
+/*
+ * This is not completely implemented yet. The idea is to
+ * create a in-memory tree (like the actual /proc filesystem
+ * tree) of these proc_dir_entries, so that we can dynamically
+ * add new files to /proc.
+ *
+ * The "next" pointer creates a linked list of one /proc directory,
+ * while parent/subdir create the directory structure (every
+ * /proc file has a parent, but "subdir" is NULL for all
+ * non-directory entries).
+ *
+ * "get_info" is called at "read", while "fill_inode" is used to
+ * fill in file type/protection/owner information specific to the
+ * particular /proc file.
+ */
+struct proc_dir_entry {
+ unsigned short low_ino;
+ unsigned short namelen;
+ const char *name;
+ mode_t mode;
+ nlink_t nlink;
+ uid_t uid;
+ gid_t gid;
+ unsigned long size;
+ struct inode_operations * ops;
+ int (*get_info)(char *, char **, off_t, int, int);
+ void (*fill_inode)(struct inode *);
+ struct proc_dir_entry *next, *parent, *subdir;
+ void *data;
+};
+
+extern int (* dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+
+extern struct proc_dir_entry proc_root;
+extern struct proc_dir_entry proc_net;
+extern struct proc_dir_entry proc_scsi;
+extern struct proc_dir_entry proc_sys;
+extern struct proc_dir_entry proc_pid;
+extern struct proc_dir_entry proc_pid_fd;
+
+extern struct inode_operations proc_scsi_inode_operations;
+
+extern void proc_root_init(void);
+extern void proc_base_init(void);
+extern void proc_net_init(void);
+
+extern int proc_register(struct proc_dir_entry *, struct proc_dir_entry *);
+extern int proc_register_dynamic(struct proc_dir_entry *,
+ struct proc_dir_entry *);
+extern int proc_unregister(struct proc_dir_entry *, int);
+
+static inline int proc_net_register(struct proc_dir_entry * x)
+{
+ return proc_register(&proc_net, x);
+}
+
+static inline int proc_net_unregister(int x)
+{
+ return proc_unregister(&proc_net, x);
+}
+
+static inline int proc_scsi_register(struct proc_dir_entry *driver,
+ struct proc_dir_entry *x)
+{
+ x->ops = &proc_scsi_inode_operations;
+ if(x->low_ino < PROC_SCSI_FILE){
+ return(proc_register(&proc_scsi, x));
+ }else{
+ return(proc_register(driver, x));
+ }
+}
+
+static inline int proc_scsi_unregister(struct proc_dir_entry *driver, int x)
+{
+ extern void scsi_init_free(char *ptr, unsigned int size);
+
+ if(x <= PROC_SCSI_FILE)
+ return(proc_unregister(&proc_scsi, x));
+ else {
+ struct proc_dir_entry **p = &driver->subdir, *dp;
+ int ret;
+
+ while ((dp = *p) != NULL) {
+ if (dp->low_ino == x)
+ break;
+ p = &dp->next;
+ }
+ ret = proc_unregister(driver, x);
+ scsi_init_free((char *) dp, sizeof(struct proc_dir_entry) + 4);
+ return(ret);
+ }
+}
+
+extern struct super_block *proc_read_super(struct super_block *,void *,int);
+extern int init_proc_fs(void);
+extern struct inode * proc_get_inode(struct super_block *, int, struct proc_dir_entry *);
+extern void proc_statfs(struct super_block *, struct statfs *, int);
+extern void proc_read_inode(struct inode *);
+extern void proc_write_inode(struct inode *);
+extern int proc_match(int, const char *, struct proc_dir_entry *);
+
+/*
+ * These are generic /proc routines that use the internal
+ * "struct proc_dir_entry" tree to traverse the filesystem.
+ *
+ * The /proc root directory has extended versions to take care
+ * of the /proc/<pid> subdirectories.
+ */
+extern int proc_readdir(struct inode *, struct file *, void *, filldir_t);
+extern int proc_lookup(struct inode *, const char *, int, struct inode **);
+
+extern struct inode_operations proc_dir_inode_operations;
+extern struct inode_operations proc_net_inode_operations;
+extern struct inode_operations proc_netdir_inode_operations;
+extern struct inode_operations proc_scsi_inode_operations;
+extern struct inode_operations proc_mem_inode_operations;
+extern struct inode_operations proc_sys_inode_operations;
+extern struct inode_operations proc_array_inode_operations;
+extern struct inode_operations proc_arraylong_inode_operations;
+extern struct inode_operations proc_kcore_inode_operations;
+extern struct inode_operations proc_profile_inode_operations;
+extern struct inode_operations proc_kmsg_inode_operations;
+extern struct inode_operations proc_link_inode_operations;
+extern struct inode_operations proc_fd_inode_operations;
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/ptrace.h b/i386/i386at/gpl/linux/include/linux/ptrace.h
new file mode 100644
index 00000000..0a02879d
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/ptrace.h
@@ -0,0 +1,26 @@
+#ifndef _LINUX_PTRACE_H
+#define _LINUX_PTRACE_H
+/* ptrace.h */
+/* structs and defines to help the user use the ptrace system call. */
+
+/* has the defines to get at the registers. */
+
+#define PTRACE_TRACEME 0
+#define PTRACE_PEEKTEXT 1
+#define PTRACE_PEEKDATA 2
+#define PTRACE_PEEKUSR 3
+#define PTRACE_POKETEXT 4
+#define PTRACE_POKEDATA 5
+#define PTRACE_POKEUSR 6
+#define PTRACE_CONT 7
+#define PTRACE_KILL 8
+#define PTRACE_SINGLESTEP 9
+
+#define PTRACE_ATTACH 0x10
+#define PTRACE_DETACH 0x11
+
+#define PTRACE_SYSCALL 24
+
+#include <asm/ptrace.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/quota.h b/i386/i386at/gpl/linux/include/linux/quota.h
new file mode 100644
index 00000000..59b86fe8
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/quota.h
@@ -0,0 +1,219 @@
+/*
+ * Copyright (c) 1982, 1986 Regents of the University of California.
+ * All rights reserved.
+ *
+ * This code is derived from software contributed to Berkeley by
+ * Robert Elz at The University of Melbourne.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * Version: $Id: quota.h,v 1.1.1.1 1997/02/25 21:27:30 thomas Exp $
+ */
+
+#ifndef _LINUX_QUOTA_
+#define _LINUX_QUOTA_
+
+#include <linux/errno.h>
+
+/*
+ * Convert diskblocks to blocks and the other way around.
+ * currently only to fool the BSD source. :-)
+ */
+#define dbtob(num) (num << 10)
+#define btodb(num) (num >> 10)
+
+/*
+ * Convert count of filesystem blocks to diskquota blocks, meant
+ * for filesystems where i_blksize != BLOCK_SIZE
+ */
+#define fs_to_dq_blocks(num, blksize) (((num) * (blksize)) / BLOCK_SIZE)
+
+/*
+ * Definitions for disk quotas imposed on the average user
+ * (big brother finally hits Linux).
+ *
+ * The following constants define the amount of time given a user
+ * before the soft limits are treated as hard limits (usually resulting
+ * in an allocation failure). The timer is started when the user crosses
+ * their soft limit, it is reset when they go below their soft limit.
+ */
+#define MAX_IQ_TIME 604800 /* (7*24*60*60) 1 week */
+#define MAX_DQ_TIME 604800 /* (7*24*60*60) 1 week */
+
+#define MAXQUOTAS 2
+#define USRQUOTA 0 /* element used for user quotas */
+#define GRPQUOTA 1 /* element used for group quotas */
+
+#include <linux/mount.h>
+
+/*
+ * Definitions for the default names of the quotas files.
+ */
+#define INITQFNAMES { \
+ "user", /* USRQUOTA */ \
+ "group", /* GRPQUOTA */ \
+ "undefined", \
+};
+
+#define QUOTAFILENAME "quota"
+#define QUOTAGROUP "staff"
+
+#define NR_DQHASH 43 /* Just an arbitrary number any suggestions ? */
+#define NR_DQUOTS 256 /* Number of quotas active at one time */
+
+/*
+ * Command definitions for the 'quotactl' system call.
+ * The commands are broken into a main command defined below
+ * and a subcommand that is used to convey the type of
+ * quota that is being manipulated (see above).
+ */
+#define SUBCMDMASK 0x00ff
+#define SUBCMDSHIFT 8
+#define QCMD(cmd, type) (((cmd) << SUBCMDSHIFT) | ((type) & SUBCMDMASK))
+
+#define Q_QUOTAON 0x0100 /* enable quotas */
+#define Q_QUOTAOFF 0x0200 /* disable quotas */
+#define Q_GETQUOTA 0x0300 /* get limits and usage */
+#define Q_SETQUOTA 0x0400 /* set limits and usage */
+#define Q_SETUSE 0x0500 /* set usage */
+#define Q_SYNC 0x0600 /* sync disk copy of a filesystems quotas */
+#define Q_SETQLIM 0x0700 /* set limits */
+#define Q_GETSTATS 0x0800 /* get collected stats */
+
+/*
+ * The following structure defines the format of the disk quota file
+ * (as it appears on disk) - the file is an array of these structures
+ * indexed by user or group number.
+ */
+struct dqblk {
+ __u32 dqb_bhardlimit; /* absolute limit on disk blks alloc */
+ __u32 dqb_bsoftlimit; /* preferred limit on disk blks */
+ __u32 dqb_curblocks; /* current block count */
+ __u32 dqb_ihardlimit; /* maximum # allocated inodes */
+ __u32 dqb_isoftlimit; /* preferred inode limit */
+ __u32 dqb_curinodes; /* current # allocated inodes */
+ time_t dqb_btime; /* time limit for excessive disk use */
+ time_t dqb_itime; /* time limit for excessive files */
+};
+
+/*
+ * Shorthand notation.
+ */
+#define dq_bhardlimit dq_dqb.dqb_bhardlimit
+#define dq_bsoftlimit dq_dqb.dqb_bsoftlimit
+#define dq_curblocks dq_dqb.dqb_curblocks
+#define dq_ihardlimit dq_dqb.dqb_ihardlimit
+#define dq_isoftlimit dq_dqb.dqb_isoftlimit
+#define dq_curinodes dq_dqb.dqb_curinodes
+#define dq_btime dq_dqb.dqb_btime
+#define dq_itime dq_dqb.dqb_itime
+
+#define dqoff(UID) ((off_t)((UID) * sizeof (struct dqblk)))
+
+struct dqstats {
+ __u32 lookups;
+ __u32 drops;
+ __u32 reads;
+ __u32 writes;
+ __u32 cache_hits;
+ __u32 pages_allocated;
+ __u32 allocated_dquots;
+ __u32 free_dquots;
+ __u32 syncs;
+};
+
+#ifdef __KERNEL__
+
+/*
+ * Maximum lenght of a message generated in the quota system,
+ * that needs to be kicked onto the tty.
+ */
+#define MAX_QUOTA_MESSAGE 75
+
+#define DQ_LOCKED 0x01 /* locked for update */
+#define DQ_WANT 0x02 /* wanted for update */
+#define DQ_MOD 0x04 /* dquot modified since read */
+#define DQ_BLKS 0x10 /* uid/gid has been warned about blk limit */
+#define DQ_INODES 0x20 /* uid/gid has been warned about inode limit */
+#define DQ_FAKE 0x40 /* no limits only usage */
+
+struct dquot {
+ unsigned int dq_id; /* id this applies to (uid, gid) */
+ short dq_type; /* type of quota */
+ kdev_t dq_dev; /* Device this applies to */
+ short dq_flags; /* see DQ_* */
+ short dq_count; /* reference count */
+ struct vfsmount *dq_mnt; /* vfsmountpoint this applies to */
+ struct dqblk dq_dqb; /* diskquota usage */
+ struct wait_queue *dq_wait; /* pointer to waitqueue */
+ struct dquot *dq_prev; /* pointer to prev dquot */
+ struct dquot *dq_next; /* pointer to next dquot */
+ struct dquot *dq_hash_prev; /* pointer to prev dquot */
+ struct dquot *dq_hash_next; /* pointer to next dquot */
+};
+
+#define NODQUOT (struct dquot *)NULL
+
+/*
+ * Flags used for set_dqblk.
+ */
+#define QUOTA_SYSCALL 0x01
+#define SET_QUOTA 0x02
+#define SET_USE 0x04
+#define SET_QLIMIT 0x08
+
+#define QUOTA_OK 0
+#define NO_QUOTA 1
+
+/*
+ * declaration of quota_function calls in kernel.
+ */
+
+extern void dquot_initialize(struct inode *inode, short type);
+extern void dquot_drop(struct inode *inode);
+extern int dquot_alloc_block(const struct inode *inode, unsigned long number);
+extern int dquot_alloc_inode(const struct inode *inode, unsigned long number);
+extern void dquot_free_block(const struct inode *inode, unsigned long number);
+extern void dquot_free_inode(const struct inode *inode, unsigned long number);
+extern int dquot_transfer(struct inode *inode, struct iattr *iattr, char direction);
+
+extern void invalidate_dquots(kdev_t dev, short type);
+extern int quota_off(kdev_t dev, short type);
+extern int sync_dquots(kdev_t dev, short type);
+
+#else
+
+#include <sys/cdefs.h>
+
+__BEGIN_DECLS
+int quotactl __P ((int, const char *, int, caddr_t));
+__END_DECLS
+
+#endif /* __KERNEL__ */
+#endif /* _QUOTA_ */
diff --git a/i386/i386at/gpl/linux/include/linux/resource.h b/i386/i386at/gpl/linux/include/linux/resource.h
new file mode 100644
index 00000000..f3bffbd7
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/resource.h
@@ -0,0 +1,60 @@
+#ifndef _LINUX_RESOURCE_H
+#define _LINUX_RESOURCE_H
+
+#include <linux/time.h>
+
+/*
+ * Resource control/accounting header file for linux
+ */
+
+/*
+ * Definition of struct rusage taken from BSD 4.3 Reno
+ *
+ * We don't support all of these yet, but we might as well have them....
+ * Otherwise, each time we add new items, programs which depend on this
+ * structure will lose. This reduces the chances of that happening.
+ */
+#define RUSAGE_SELF 0
+#define RUSAGE_CHILDREN (-1)
+#define RUSAGE_BOTH (-2) /* sys_wait4() uses this */
+
+struct rusage {
+ struct timeval ru_utime; /* user time used */
+ struct timeval ru_stime; /* system time used */
+ long ru_maxrss; /* maximum resident set size */
+ long ru_ixrss; /* integral shared memory size */
+ long ru_idrss; /* integral unshared data size */
+ long ru_isrss; /* integral unshared stack size */
+ long ru_minflt; /* page reclaims */
+ long ru_majflt; /* page faults */
+ long ru_nswap; /* swaps */
+ long ru_inblock; /* block input operations */
+ long ru_oublock; /* block output operations */
+ long ru_msgsnd; /* messages sent */
+ long ru_msgrcv; /* messages received */
+ long ru_nsignals; /* signals received */
+ long ru_nvcsw; /* voluntary context switches */
+ long ru_nivcsw; /* involuntary " */
+};
+
+#define RLIM_INFINITY ((long)(~0UL>>1))
+
+struct rlimit {
+ long rlim_cur;
+ long rlim_max;
+};
+
+#define PRIO_MIN (-20)
+#define PRIO_MAX 20
+
+#define PRIO_PROCESS 0
+#define PRIO_PGRP 1
+#define PRIO_USER 2
+
+/*
+ * Due to binary compatibility, the actual resource numbers
+ * may be different for different linux versions..
+ */
+#include <asm/resource.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/route.h b/i386/i386at/gpl/linux/include/linux/route.h
new file mode 100644
index 00000000..5be4853e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/route.h
@@ -0,0 +1,78 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Global definitions for the IP router interface.
+ *
+ * Version: @(#)route.h 1.0.3 05/27/93
+ *
+ * Authors: Original taken from Berkeley UNIX 4.3, (c) UCB 1986-1988
+ * for the purposes of compatibility only.
+ *
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_ROUTE_H
+#define _LINUX_ROUTE_H
+
+#include <linux/if.h>
+
+
+/* This structure gets passed by the SIOCADDRT and SIOCDELRT calls. */
+struct rtentry
+{
+ unsigned long rt_hash; /* hash key for lookups */
+ struct sockaddr rt_dst; /* target address */
+ struct sockaddr rt_gateway; /* gateway addr (RTF_GATEWAY) */
+ struct sockaddr rt_genmask; /* target network mask (IP) */
+ short rt_flags;
+ short rt_refcnt;
+ unsigned long rt_use;
+ struct ifnet *rt_ifp;
+ short rt_metric; /* +1 for binary compatibility! */
+ char *rt_dev; /* forcing the device at add */
+ unsigned long rt_mss; /* per route MTU/Window */
+ unsigned long rt_window; /* Window clamping */
+ unsigned short rt_irtt; /* Initial RTT */
+};
+
+
+#define RTF_UP 0x0001 /* route usable */
+#define RTF_GATEWAY 0x0002 /* destination is a gateway */
+#define RTF_HOST 0x0004 /* host entry (net otherwise) */
+#define RTF_REINSTATE 0x0008 /* reinstate route after tmout */
+#define RTF_DYNAMIC 0x0010 /* created dyn. (by redirect) */
+#define RTF_MODIFIED 0x0020 /* modified dyn. (by redirect) */
+#define RTF_MSS 0x0040 /* specific MSS for this route */
+#define RTF_WINDOW 0x0080 /* per route window clamping */
+#define RTF_IRTT 0x0100 /* Initial round trip time */
+#define RTF_REJECT 0x0200 /* Reject route */
+
+/*
+ * This structure is passed from the kernel to user space by netlink
+ * routing/device announcements
+ */
+
+struct netlink_rtinfo
+{
+ unsigned long rtmsg_type;
+ struct sockaddr rtmsg_dst;
+ struct sockaddr rtmsg_gateway;
+ struct sockaddr rtmsg_genmask;
+ short rtmsg_flags;
+ short rtmsg_metric;
+ char rtmsg_device[16];
+};
+
+#define RTMSG_NEWROUTE 0x01
+#define RTMSG_DELROUTE 0x02
+#define RTMSG_NEWDEVICE 0x11
+#define RTMSG_DELDEVICE 0x12
+
+#endif /* _LINUX_ROUTE_H */
+
diff --git a/i386/i386at/gpl/linux/include/linux/sched.h b/i386/i386at/gpl/linux/include/linux/sched.h
new file mode 100644
index 00000000..28fe7ef0
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/sched.h
@@ -0,0 +1,492 @@
+#ifndef _LINUX_SCHED_H
+#define _LINUX_SCHED_H
+
+/*
+ * define DEBUG if you want the wait-queues to have some extra
+ * debugging code. It's not normally used, but might catch some
+ * wait-queue coding errors.
+ *
+ * #define DEBUG
+ */
+
+#include <asm/param.h> /* for HZ */
+
+extern unsigned long intr_count;
+extern unsigned long event;
+
+#include <linux/binfmts.h>
+#include <linux/personality.h>
+#include <linux/tasks.h>
+#include <linux/kernel.h>
+#include <asm/system.h>
+#include <asm/page.h>
+
+#include <linux/smp.h>
+#include <linux/tty.h>
+#include <linux/sem.h>
+
+/*
+ * cloning flags:
+ */
+#define CSIGNAL 0x000000ff /* signal mask to be sent at exit */
+#define CLONE_VM 0x00000100 /* set if VM shared between processes */
+#define CLONE_FS 0x00000200 /* set if fs info shared between processes */
+#define CLONE_FILES 0x00000400 /* set if open files shared between processes */
+#define CLONE_SIGHAND 0x00000800 /* set if signal handlers shared */
+#define CLONE_PID 0x00001000 /* set if pid shared */
+
+/*
+ * These are the constant used to fake the fixed-point load-average
+ * counting. Some notes:
+ * - 11 bit fractions expand to 22 bits by the multiplies: this gives
+ * a load-average precision of 10 bits integer + 11 bits fractional
+ * - if you want to count load-averages more often, you need more
+ * precision, or rounding will get you. With 2-second counting freq,
+ * the EXP_n values would be 1981, 2034 and 2043 if still using only
+ * 11 bit fractions.
+ */
+extern unsigned long avenrun[]; /* Load averages */
+
+#define FSHIFT 11 /* nr of bits of precision */
+#define FIXED_1 (1<<FSHIFT) /* 1.0 as fixed-point */
+#define LOAD_FREQ (5*HZ) /* 5 sec intervals */
+#define EXP_1 1884 /* 1/exp(5sec/1min) as fixed-point */
+#define EXP_5 2014 /* 1/exp(5sec/5min) */
+#define EXP_15 2037 /* 1/exp(5sec/15min) */
+
+#define CALC_LOAD(load,exp,n) \
+ load *= exp; \
+ load += n*(FIXED_1-exp); \
+ load >>= FSHIFT;
+
+#define CT_TO_SECS(x) ((x) / HZ)
+#define CT_TO_USECS(x) (((x) % HZ) * 1000000/HZ)
+
+extern int nr_running, nr_tasks;
+
+#define FIRST_TASK task[0]
+#define LAST_TASK task[NR_TASKS-1]
+
+#include <linux/head.h>
+#include <linux/fs.h>
+#include <linux/signal.h>
+#include <linux/time.h>
+#include <linux/param.h>
+#include <linux/resource.h>
+#include <linux/vm86.h>
+#include <linux/math_emu.h>
+#include <linux/ptrace.h>
+#include <linux/timer.h>
+
+#include <asm/processor.h>
+
+#define TASK_RUNNING 0
+#define TASK_INTERRUPTIBLE 1
+#define TASK_UNINTERRUPTIBLE 2
+#define TASK_ZOMBIE 3
+#define TASK_STOPPED 4
+#define TASK_SWAPPING 5
+
+/*
+ * Scheduling policies
+ */
+#define SCHED_OTHER 0
+#define SCHED_FIFO 1
+#define SCHED_RR 2
+
+struct sched_param {
+ int sched_priority;
+};
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifdef __KERNEL__
+
+#define barrier() __asm__("": : :"memory")
+
+extern void sched_init(void);
+extern void show_state(void);
+extern void trap_init(void);
+
+asmlinkage void schedule(void);
+
+struct files_struct {
+ int count;
+ fd_set close_on_exec;
+ struct file * fd[NR_OPEN];
+};
+
+#define INIT_FILES { \
+ 1, \
+ { { 0, } }, \
+ { NULL, } \
+}
+
+struct fs_struct {
+ int count;
+ unsigned short umask;
+ struct inode * root, * pwd;
+};
+
+#define INIT_FS { \
+ 1, \
+ 0022, \
+ NULL, NULL \
+}
+
+struct mm_struct {
+ int count;
+ pgd_t * pgd;
+ unsigned long context;
+ unsigned long start_code, end_code, start_data, end_data;
+ unsigned long start_brk, brk, start_stack, start_mmap;
+ unsigned long arg_start, arg_end, env_start, env_end;
+ unsigned long rss, total_vm, locked_vm;
+ unsigned long def_flags;
+ struct vm_area_struct * mmap;
+ struct vm_area_struct * mmap_avl;
+};
+
+#define INIT_MM { \
+ 1, \
+ swapper_pg_dir, \
+ 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, 0, \
+ 0, 0, 0, \
+ 0, \
+ &init_mmap, &init_mmap }
+
+struct signal_struct {
+ int count;
+ struct sigaction action[32];
+};
+
+#define INIT_SIGNALS { \
+ 1, \
+ { {0,}, } }
+
+struct task_struct {
+/* these are hardcoded - don't touch */
+ volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
+ long counter;
+ long priority;
+ unsigned long signal;
+ unsigned long blocked; /* bitmap of masked signals */
+ unsigned long flags; /* per process flags, defined below */
+ int errno;
+ long debugreg[8]; /* Hardware debugging registers */
+ struct exec_domain *exec_domain;
+/* various fields */
+ struct linux_binfmt *binfmt;
+ struct task_struct *next_task, *prev_task;
+ struct task_struct *next_run, *prev_run;
+ unsigned long saved_kernel_stack;
+ unsigned long kernel_stack_page;
+ int exit_code, exit_signal;
+ unsigned long personality;
+ int dumpable:1;
+ int did_exec:1;
+ int pid,pgrp,tty_old_pgrp,session,leader;
+ int groups[NGROUPS];
+ /*
+ * pointers to (original) parent process, youngest child, younger sibling,
+ * older sibling, respectively. (p->father can be replaced with
+ * p->p_pptr->pid)
+ */
+ struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
+ struct wait_queue *wait_chldexit; /* for wait4() */
+ unsigned short uid,euid,suid,fsuid;
+ unsigned short gid,egid,sgid,fsgid;
+ unsigned long timeout, policy, rt_priority;
+ unsigned long it_real_value, it_prof_value, it_virt_value;
+ unsigned long it_real_incr, it_prof_incr, it_virt_incr;
+ struct timer_list real_timer;
+ long utime, stime, cutime, cstime, start_time;
+/* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */
+ unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap;
+ int swappable:1;
+ unsigned long swap_address;
+ unsigned long old_maj_flt; /* old value of maj_flt */
+ unsigned long dec_flt; /* page fault count of the last time */
+ unsigned long swap_cnt; /* number of pages to swap on next pass */
+/* limits */
+ struct rlimit rlim[RLIM_NLIMITS];
+ unsigned short used_math;
+ char comm[16];
+/* file system info */
+ int link_count;
+ struct tty_struct *tty; /* NULL if no tty */
+/* ipc stuff */
+ struct sem_undo *semundo;
+ struct sem_queue *semsleeping;
+/* ldt for this task - used by Wine. If NULL, default_ldt is used */
+ struct desc_struct *ldt;
+/* tss for this task */
+ struct thread_struct tss;
+/* filesystem information */
+ struct fs_struct *fs;
+/* open file information */
+ struct files_struct *files;
+/* memory management info */
+ struct mm_struct *mm;
+/* signal handlers */
+ struct signal_struct *sig;
+#ifdef __SMP__
+ int processor;
+ int last_processor;
+ int lock_depth; /* Lock depth. We can context switch in and out of holding a syscall kernel lock... */
+#endif
+};
+
+/*
+ * Per process flags
+ */
+#define PF_ALIGNWARN 0x00000001 /* Print alignment warning msgs */
+ /* Not implemented yet, only for 486*/
+#define PF_PTRACED 0x00000010 /* set if ptrace (0) has been called. */
+#define PF_TRACESYS 0x00000020 /* tracing system calls */
+
+#define PF_STARTING 0x00000100 /* being created */
+#define PF_EXITING 0x00000200 /* getting shut down */
+
+#define PF_USEDFPU 0x00100000 /* Process used the FPU this quantum (SMP only) */
+
+/*
+ * Limit the stack by to some sane default: root can always
+ * increase this limit if needed.. 8MB seems reasonable.
+ */
+#define _STK_LIM (8*1024*1024)
+
+#define DEF_PRIORITY (20*HZ/100) /* 200 ms time slices */
+
+/*
+ * INIT_TASK is used to set up the first task table, touch at
+ * your own risk!. Base=0, limit=0x1fffff (=2MB)
+ */
+#define INIT_TASK \
+/* state etc */ { 0,DEF_PRIORITY,DEF_PRIORITY,0,0,0,0, \
+/* debugregs */ { 0, }, \
+/* exec domain */&default_exec_domain, \
+/* binfmt */ NULL, \
+/* schedlink */ &init_task,&init_task, &init_task, &init_task, \
+/* stack */ 0,(unsigned long) &init_kernel_stack, \
+/* ec,brk... */ 0,0,0,0,0, \
+/* pid etc.. */ 0,0,0,0,0, \
+/* suppl grps*/ {NOGROUP,}, \
+/* proc links*/ &init_task,&init_task,NULL,NULL,NULL,NULL, \
+/* uid etc */ 0,0,0,0,0,0,0,0, \
+/* timeout */ 0,SCHED_OTHER,0,0,0,0,0,0,0, \
+/* timer */ { NULL, NULL, 0, 0, it_real_fn }, \
+/* utime */ 0,0,0,0,0, \
+/* flt */ 0,0,0,0,0,0, \
+/* swp */ 0,0,0,0,0, \
+/* rlimits */ INIT_RLIMITS, \
+/* math */ 0, \
+/* comm */ "swapper", \
+/* fs info */ 0,NULL, \
+/* ipc */ NULL, NULL, \
+/* ldt */ NULL, \
+/* tss */ INIT_TSS, \
+/* fs */ &init_fs, \
+/* files */ &init_files, \
+/* mm */ &init_mm, \
+/* signals */ &init_signals, \
+}
+
+extern struct mm_struct init_mm;
+extern struct task_struct init_task;
+extern struct task_struct *task[NR_TASKS];
+extern struct task_struct *last_task_used_math;
+extern struct task_struct *current_set[NR_CPUS];
+/*
+ * On a single processor system this comes out as current_set[0] when cpp
+ * has finished with it, which gcc will optimise away.
+ */
+#define current (0+current_set[smp_processor_id()]) /* Current on this processor */
+extern unsigned long volatile jiffies;
+extern unsigned long itimer_ticks;
+extern unsigned long itimer_next;
+extern struct timeval xtime;
+extern int need_resched;
+extern void do_timer(struct pt_regs *);
+
+extern unsigned int * prof_buffer;
+extern unsigned long prof_len;
+extern unsigned long prof_shift;
+
+extern int securelevel; /* system security level */
+
+#define CURRENT_TIME (xtime.tv_sec)
+
+extern void sleep_on(struct wait_queue ** p);
+extern void interruptible_sleep_on(struct wait_queue ** p);
+extern void wake_up(struct wait_queue ** p);
+extern void wake_up_interruptible(struct wait_queue ** p);
+extern void wake_up_process(struct task_struct * tsk);
+
+extern void notify_parent(struct task_struct * tsk);
+extern int send_sig(unsigned long sig,struct task_struct * p,int priv);
+extern int in_group_p(gid_t grp);
+
+extern int request_irq(unsigned int irq,void (*handler)(int, struct pt_regs *),
+ unsigned long flags, const char *device);
+extern void free_irq(unsigned int irq);
+
+extern void copy_thread(int, unsigned long, unsigned long, struct task_struct *, struct pt_regs *);
+extern void flush_thread(void);
+extern void exit_thread(void);
+
+extern void exit_fs(struct task_struct *);
+extern void exit_files(struct task_struct *);
+extern void exit_sighand(struct task_struct *);
+extern void release_thread(struct task_struct *);
+
+extern int do_execve(char *, char **, char **, struct pt_regs *);
+extern int do_fork(unsigned long, unsigned long, struct pt_regs *);
+
+#ifdef MACH
+extern void add_wait_queue(struct wait_queue **, struct wait_queue *);
+extern void remove_wait_queue(struct wait_queue **, struct wait_queue *);
+#else /* ! MACH */
+/*
+ * The wait-queues are circular lists, and you have to be *very* sure
+ * to keep them correct. Use only these two functions to add/remove
+ * entries in the queues.
+ */
+extern inline void add_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ unsigned long flags;
+
+#ifdef DEBUG
+ if (wait->next) {
+ __label__ here;
+ unsigned long pc;
+ pc = (unsigned long) &&here;
+ here:
+ printk("add_wait_queue (%08lx): wait->next = %08lx\n",pc,(unsigned long) wait->next);
+ }
+#endif
+ save_flags(flags);
+ cli();
+ if (!*p) {
+ wait->next = wait;
+ *p = wait;
+ } else {
+ wait->next = (*p)->next;
+ (*p)->next = wait;
+ }
+ restore_flags(flags);
+}
+
+extern inline void remove_wait_queue(struct wait_queue ** p, struct wait_queue * wait)
+{
+ unsigned long flags;
+ struct wait_queue * tmp;
+#ifdef DEBUG
+ unsigned long ok = 0;
+#endif
+
+ save_flags(flags);
+ cli();
+ if ((*p == wait) &&
+#ifdef DEBUG
+ (ok = 1) &&
+#endif
+ ((*p = wait->next) == wait)) {
+ *p = NULL;
+ } else {
+ tmp = wait;
+ while (tmp->next != wait) {
+ tmp = tmp->next;
+#ifdef DEBUG
+ if (tmp == *p)
+ ok = 1;
+#endif
+ }
+ tmp->next = wait->next;
+ }
+ wait->next = NULL;
+ restore_flags(flags);
+#ifdef DEBUG
+ if (!ok) {
+ __label__ here;
+ ok = (unsigned long) &&here;
+ printk("removed wait_queue not on list.\n");
+ printk("list = %08lx, queue = %08lx\n",(unsigned long) p, (unsigned long) wait);
+ here:
+ printk("eip = %08lx\n",ok);
+ }
+#endif
+}
+
+extern inline void select_wait(struct wait_queue ** wait_address, select_table * p)
+{
+ struct select_table_entry * entry;
+
+ if (!p || !wait_address)
+ return;
+ if (p->nr >= __MAX_SELECT_TABLE_ENTRIES)
+ return;
+ entry = p->entry + p->nr;
+ entry->wait_address = wait_address;
+ entry->wait.task = current;
+ entry->wait.next = NULL;
+ add_wait_queue(wait_address,&entry->wait);
+ p->nr++;
+}
+#endif /* ! MACH */
+
+extern void __down(struct semaphore * sem);
+
+/*
+ * These are not yet interrupt-safe
+ */
+extern inline void down(struct semaphore * sem)
+{
+ if (sem->count <= 0)
+ __down(sem);
+ sem->count--;
+}
+
+extern inline void up(struct semaphore * sem)
+{
+ sem->count++;
+ wake_up(&sem->wait);
+}
+
+#define REMOVE_LINKS(p) do { unsigned long flags; \
+ save_flags(flags) ; cli(); \
+ (p)->next_task->prev_task = (p)->prev_task; \
+ (p)->prev_task->next_task = (p)->next_task; \
+ restore_flags(flags); \
+ if ((p)->p_osptr) \
+ (p)->p_osptr->p_ysptr = (p)->p_ysptr; \
+ if ((p)->p_ysptr) \
+ (p)->p_ysptr->p_osptr = (p)->p_osptr; \
+ else \
+ (p)->p_pptr->p_cptr = (p)->p_osptr; \
+ } while (0)
+
+#define SET_LINKS(p) do { unsigned long flags; \
+ save_flags(flags); cli(); \
+ (p)->next_task = &init_task; \
+ (p)->prev_task = init_task.prev_task; \
+ init_task.prev_task->next_task = (p); \
+ init_task.prev_task = (p); \
+ restore_flags(flags); \
+ (p)->p_ysptr = NULL; \
+ if (((p)->p_osptr = (p)->p_pptr->p_cptr) != NULL) \
+ (p)->p_osptr->p_ysptr = p; \
+ (p)->p_pptr->p_cptr = p; \
+ } while (0)
+
+#define for_each_task(p) \
+ for (p = &init_task ; (p = p->next_task) != &init_task ; )
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/scsi.h b/i386/i386at/gpl/linux/include/linux/scsi.h
new file mode 100644
index 00000000..a05072cf
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/scsi.h
@@ -0,0 +1,198 @@
+#ifndef _LINUX_SCSI_H
+#define _LINUX_SCSI_H
+
+/*
+ * This header file contains public constants and structures used by
+ * the scsi code for linux.
+ */
+
+/*
+ $Header: cvs/gnumach/i386/i386at/gpl/linux/include/linux/Attic/scsi.h,v 1.1.1.1 1997/02/25 21:27:31 thomas Exp $
+
+ For documentation on the OPCODES, MESSAGES, and SENSE values,
+ please consult the SCSI standard.
+
+*/
+
+/*
+ * SCSI opcodes
+ */
+
+#define TEST_UNIT_READY 0x00
+#define REZERO_UNIT 0x01
+#define REQUEST_SENSE 0x03
+#define FORMAT_UNIT 0x04
+#define READ_BLOCK_LIMITS 0x05
+#define REASSIGN_BLOCKS 0x07
+#define READ_6 0x08
+#define WRITE_6 0x0a
+#define SEEK_6 0x0b
+#define READ_REVERSE 0x0f
+#define WRITE_FILEMARKS 0x10
+#define SPACE 0x11
+#define INQUIRY 0x12
+#define RECOVER_BUFFERED_DATA 0x14
+#define MODE_SELECT 0x15
+#define RESERVE 0x16
+#define RELEASE 0x17
+#define COPY 0x18
+#define ERASE 0x19
+#define MODE_SENSE 0x1a
+#define START_STOP 0x1b
+#define RECEIVE_DIAGNOSTIC 0x1c
+#define SEND_DIAGNOSTIC 0x1d
+#define ALLOW_MEDIUM_REMOVAL 0x1e
+
+#define SET_WINDOW 0x24
+#define READ_CAPACITY 0x25
+#define READ_10 0x28
+#define WRITE_10 0x2a
+#define SEEK_10 0x2b
+#define WRITE_VERIFY 0x2e
+#define VERIFY 0x2f
+#define SEARCH_HIGH 0x30
+#define SEARCH_EQUAL 0x31
+#define SEARCH_LOW 0x32
+#define SET_LIMITS 0x33
+#define PRE_FETCH 0x34
+#define READ_POSITION 0x34
+#define SYNCHRONIZE_CACHE 0x35
+#define LOCK_UNLOCK_CACHE 0x36
+#define READ_DEFECT_DATA 0x37
+#define MEDIUM_SCAN 0x38
+#define COMPARE 0x39
+#define COPY_VERIFY 0x3a
+#define WRITE_BUFFER 0x3b
+#define READ_BUFFER 0x3c
+#define UPDATE_BLOCK 0x3d
+#define READ_LONG 0x3e
+#define WRITE_LONG 0x3f
+#define CHANGE_DEFINITION 0x40
+#define WRITE_SAME 0x41
+#define LOG_SELECT 0x4c
+#define LOG_SENSE 0x4d
+#define MODE_SELECT_10 0x55
+#define MODE_SENSE_10 0x5a
+#define READ_12 0xa8
+#define WRITE_12 0xaa
+#define WRITE_VERIFY_12 0xae
+#define SEARCH_HIGH_12 0xb0
+#define SEARCH_EQUAL_12 0xb1
+#define SEARCH_LOW_12 0xb2
+#define SEND_VOLUME_TAG 0xb6
+#define WRITE_LONG_2 0xea
+
+/*
+ * Status codes
+ */
+
+#define GOOD 0x00
+#define CHECK_CONDITION 0x01
+#define CONDITION_GOOD 0x02
+#define BUSY 0x04
+#define INTERMEDIATE_GOOD 0x08
+#define INTERMEDIATE_C_GOOD 0x0a
+#define RESERVATION_CONFLICT 0x0c
+#define QUEUE_FULL 0x1a
+
+#define STATUS_MASK 0x1e
+
+/*
+ * SENSE KEYS
+ */
+
+#define NO_SENSE 0x00
+#define RECOVERED_ERROR 0x01
+#define NOT_READY 0x02
+#define MEDIUM_ERROR 0x03
+#define HARDWARE_ERROR 0x04
+#define ILLEGAL_REQUEST 0x05
+#define UNIT_ATTENTION 0x06
+#define DATA_PROTECT 0x07
+#define BLANK_CHECK 0x08
+#define COPY_ABORTED 0x0a
+#define ABORTED_COMMAND 0x0b
+#define VOLUME_OVERFLOW 0x0d
+#define MISCOMPARE 0x0e
+
+
+/*
+ * DEVICE TYPES
+ */
+
+#define TYPE_DISK 0x00
+#define TYPE_TAPE 0x01
+#define TYPE_PROCESSOR 0x03 /* HP scanners use this */
+#define TYPE_WORM 0x04 /* Treated as ROM by our system */
+#define TYPE_ROM 0x05
+#define TYPE_SCANNER 0x06
+#define TYPE_MOD 0x07 /* Magneto-optical disk -
+ * - treated as TYPE_DISK */
+#define TYPE_NO_LUN 0x7f
+
+
+/*
+ * MESSAGE CODES
+ */
+
+#define COMMAND_COMPLETE 0x00
+#define EXTENDED_MESSAGE 0x01
+#define EXTENDED_MODIFY_DATA_POINTER 0x00
+#define EXTENDED_SDTR 0x01
+#define EXTENDED_EXTENDED_IDENTIFY 0x02 /* SCSI-I only */
+#define EXTENDED_WDTR 0x03
+#define SAVE_POINTERS 0x02
+#define RESTORE_POINTERS 0x03
+#define DISCONNECT 0x04
+#define INITIATOR_ERROR 0x05
+#define ABORT 0x06
+#define MESSAGE_REJECT 0x07
+#define NOP 0x08
+#define MSG_PARITY_ERROR 0x09
+#define LINKED_CMD_COMPLETE 0x0a
+#define LINKED_FLG_CMD_COMPLETE 0x0b
+#define BUS_DEVICE_RESET 0x0c
+
+#define INITIATE_RECOVERY 0x0f /* SCSI-II only */
+#define RELEASE_RECOVERY 0x10 /* SCSI-II only */
+
+#define SIMPLE_QUEUE_TAG 0x20
+#define HEAD_OF_QUEUE_TAG 0x21
+#define ORDERED_QUEUE_TAG 0x22
+
+/*
+ * Here are some scsi specific ioctl commands which are sometimes useful.
+ */
+/* These are a few other constants only used by scsi devices */
+
+#define SCSI_IOCTL_GET_IDLUN 0x5382
+
+/* Used to turn on and off tagged queuing for scsi devices */
+
+#define SCSI_IOCTL_TAGGED_ENABLE 0x5383
+#define SCSI_IOCTL_TAGGED_DISABLE 0x5384
+
+/* Used to obtain the host number of a device. */
+#define SCSI_IOCTL_PROBE_HOST 0x5385
+
+
+/*
+ * 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:
+ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/scsicam.h b/i386/i386at/gpl/linux/include/linux/scsicam.h
new file mode 100644
index 00000000..954e1407
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/scsicam.h
@@ -0,0 +1,17 @@
+/*
+ * scsicam.h - SCSI CAM support functions, use for HDIO_GETGEO, etc.
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@Colorado.EDU
+ * +1 (303) 786-7975
+ *
+ * For more information, please consult the SCSI-CAM draft.
+ */
+
+#ifndef SCSICAM_H
+#define SCSICAM_H
+#include <linux/kdev_t.h>
+extern int scsicam_bios_param (Disk *disk, kdev_t dev, int *ip);
+#endif /* def SCSICAM_H */
diff --git a/i386/i386at/gpl/linux/include/linux/sem.h b/i386/i386at/gpl/linux/include/linux/sem.h
new file mode 100644
index 00000000..0eb1d024
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/sem.h
@@ -0,0 +1,112 @@
+#ifndef _LINUX_SEM_H
+#define _LINUX_SEM_H
+#include <linux/ipc.h>
+
+/* semop flags */
+#define SEM_UNDO 0x1000 /* undo the operation on exit */
+
+/* semctl Command Definitions. */
+#define GETPID 11 /* get sempid */
+#define GETVAL 12 /* get semval */
+#define GETALL 13 /* get all semval's */
+#define GETNCNT 14 /* get semncnt */
+#define GETZCNT 15 /* get semzcnt */
+#define SETVAL 16 /* set semval */
+#define SETALL 17 /* set all semval's */
+
+/* One semid data structure for each set of semaphores in the system. */
+struct semid_ds {
+ struct ipc_perm sem_perm; /* permissions .. see ipc.h */
+ time_t sem_otime; /* last semop time */
+ time_t sem_ctime; /* last change time */
+ struct sem *sem_base; /* ptr to first semaphore in array */
+ struct sem_queue *sem_pending; /* pending operations to be processed */
+ struct sem_queue **sem_pending_last; /* last pending operation */
+ struct sem_undo *undo; /* undo requests on this array */
+ ushort sem_nsems; /* no. of semaphores in array */
+};
+
+/* semop system calls takes an array of these. */
+struct sembuf {
+ ushort sem_num; /* semaphore index in array */
+ short sem_op; /* semaphore operation */
+ short sem_flg; /* operation flags */
+};
+
+/* arg for semctl system calls. */
+union semun {
+ int val; /* value for SETVAL */
+ struct semid_ds *buf; /* buffer for IPC_STAT & IPC_SET */
+ ushort *array; /* array for GETALL & SETALL */
+ struct seminfo *__buf; /* buffer for IPC_INFO */
+ void *__pad;
+};
+
+struct seminfo {
+ int semmap;
+ int semmni;
+ int semmns;
+ int semmnu;
+ int semmsl;
+ int semopm;
+ int semume;
+ int semusz;
+ int semvmx;
+ int semaem;
+};
+
+#define SEMMNI 128 /* ? max # of semaphore identifiers */
+#define SEMMSL 32 /* <= 512 max num of semaphores per id */
+#define SEMMNS (SEMMNI*SEMMSL) /* ? max # of semaphores in system */
+#define SEMOPM 32 /* ~ 100 max num of ops per semop call */
+#define SEMVMX 32767 /* semaphore maximum value */
+
+/* unused */
+#define SEMUME SEMOPM /* max num of undo entries per process */
+#define SEMMNU SEMMNS /* num of undo structures system wide */
+#define SEMAEM (SEMVMX >> 1) /* adjust on exit max value */
+#define SEMMAP SEMMNS /* # of entries in semaphore map */
+#define SEMUSZ 20 /* sizeof struct sem_undo */
+
+#ifdef __KERNEL__
+
+/* One semaphore structure for each semaphore in the system. */
+struct sem {
+ short semval; /* current value */
+ short sempid; /* pid of last operation */
+};
+
+/* ipcs ctl cmds */
+#define SEM_STAT 18
+#define SEM_INFO 19
+
+/* One queue for each semaphore set in the system. */
+struct sem_queue {
+ struct sem_queue * next; /* next entry in the queue */
+ struct sem_queue ** prev; /* previous entry in the queue, *(q->prev) == q */
+ struct wait_queue * sleeper; /* sleeping process */
+ struct sem_undo * undo; /* undo structure */
+ int pid; /* process id of requesting process */
+ int status; /* completion status of operation */
+ struct semid_ds * sma; /* semaphore array for operations */
+ struct sembuf * sops; /* array of pending operations */
+ int nsops; /* number of operations */
+};
+
+/* Each task has a list of undo requests. They are executed automatically
+ * when the process exits.
+ */
+struct sem_undo {
+ struct sem_undo * proc_next; /* next entry on this process */
+ struct sem_undo * id_next; /* next entry on this semaphore set */
+ int semid; /* semaphore set identifier */
+ short * semadj; /* array of adjustments, one per semaphore */
+};
+
+asmlinkage int sys_semget (key_t key, int nsems, int semflg);
+asmlinkage int sys_semop (int semid, struct sembuf *sops, unsigned nsops);
+asmlinkage int sys_semctl (int semid, int semnum, int cmd, union semun arg);
+
+#endif /* __KERNEL__ */
+
+#endif /* _LINUX_SEM_H */
diff --git a/i386/i386at/gpl/linux/include/linux/signal.h b/i386/i386at/gpl/linux/include/linux/signal.h
new file mode 100644
index 00000000..9d1afa91
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/signal.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_SIGNAL_H
+#define _LINUX_SIGNAL_H
+
+#include <asm/signal.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/skbuff.h b/i386/i386at/gpl/linux/include/linux/skbuff.h
new file mode 100644
index 00000000..65418168
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/skbuff.h
@@ -0,0 +1,474 @@
+/*
+ * Definitions for the 'struct sk_buff' memory handlers.
+ *
+ * Authors:
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#ifndef _LINUX_SKBUFF_H
+#define _LINUX_SKBUFF_H
+#include <linux/malloc.h>
+#include <linux/wait.h>
+#include <linux/time.h>
+#include <linux/config.h>
+
+#define CONFIG_SKB_CHECK 0
+
+#define HAVE_ALLOC_SKB /* For the drivers to know */
+#define HAVE_ALIGNABLE_SKB /* Ditto 8) */
+
+
+#define FREE_READ 1
+#define FREE_WRITE 0
+
+#define CHECKSUM_NONE 0
+#define CHECKSUM_HW 1
+#define CHECKSUM_UNNECESSARY 2
+
+struct sk_buff_head
+{
+ struct sk_buff * volatile next;
+ struct sk_buff * volatile prev;
+ __u32 qlen; /* Must be same length as a pointer
+ for using debugging */
+#if CONFIG_SKB_CHECK
+ int magic_debug_cookie;
+#endif
+};
+
+
+struct sk_buff
+{
+ struct sk_buff * volatile next; /* Next buffer in list */
+ struct sk_buff * volatile prev; /* Previous buffer in list */
+ struct sk_buff_head * list; /* List we are on */
+#if CONFIG_SKB_CHECK
+ int magic_debug_cookie;
+#endif
+ struct sk_buff * volatile link3; /* Link for IP protocol level buffer chains */
+ struct sock *sk; /* Socket we are owned by */
+ unsigned long when; /* used to compute rtt's */
+ struct timeval stamp; /* Time we arrived */
+ struct linux_device *dev; /* Device we arrived on/are leaving by */
+ union
+ {
+ struct tcphdr *th;
+ struct ethhdr *eth;
+ struct iphdr *iph;
+ struct udphdr *uh;
+ unsigned char *raw;
+ /* for passing an fd in a unix domain socket */
+ struct file *filp;
+ } h;
+
+ union
+ {
+ /* As yet incomplete physical layer views */
+ unsigned char *raw;
+ struct ethhdr *ethernet;
+ } mac;
+
+ struct iphdr *ip_hdr; /* For IPPROTO_RAW */
+ unsigned long len; /* Length of actual data */
+ unsigned long csum; /* Checksum */
+ __u32 saddr; /* IP source address */
+ __u32 daddr; /* IP target address */
+ __u32 raddr; /* IP next hop address */
+ __u32 seq; /* TCP sequence number */
+ __u32 end_seq; /* seq [+ fin] [+ syn] + datalen */
+ __u32 ack_seq; /* TCP ack sequence number */
+ unsigned char proto_priv[16]; /* Protocol private data */
+ volatile char acked, /* Are we acked ? */
+ used, /* Are we in use ? */
+ free, /* How to free this buffer */
+ arp; /* Has IP/ARP resolution finished */
+ unsigned char tries, /* Times tried */
+ lock, /* Are we locked ? */
+ localroute, /* Local routing asserted for this frame */
+ pkt_type, /* Packet class */
+ ip_summed; /* Driver fed us an IP checksum */
+#define PACKET_HOST 0 /* To us */
+#define PACKET_BROADCAST 1 /* To all */
+#define PACKET_MULTICAST 2 /* To group */
+#define PACKET_OTHERHOST 3 /* To someone else */
+ unsigned short users; /* User count - see datagram.c,tcp.c */
+ unsigned short protocol; /* Packet protocol from driver. */
+ unsigned short truesize; /* Buffer size */
+
+ int count; /* reference count */
+ struct sk_buff *data_skb; /* Link to the actual data skb */
+ unsigned char *head; /* Head of buffer */
+ unsigned char *data; /* Data head pointer */
+ unsigned char *tail; /* Tail pointer */
+ unsigned char *end; /* End pointer */
+ void (*destructor)(struct sk_buff *this); /* Destruct function */
+#ifdef MACH
+#ifdef MACH_INCLUDE
+ ipc_port_t reply;
+ mach_msg_type_name_t reply_type;
+ vm_map_copy_t copy;
+#else
+ void *reply;
+ unsigned reply_type;
+ void *copy;
+#endif
+#endif
+};
+
+#ifdef CONFIG_SKB_LARGE
+#define SK_WMEM_MAX 65535
+#define SK_RMEM_MAX 65535
+#else
+#define SK_WMEM_MAX 32767
+#define SK_RMEM_MAX 32767
+#endif
+
+#if CONFIG_SKB_CHECK
+#define SK_FREED_SKB 0x0DE2C0DE
+#define SK_GOOD_SKB 0xDEC0DED1
+#define SK_HEAD_SKB 0x12231298
+#endif
+
+#ifdef __KERNEL__
+/*
+ * Handling routines are only of interest to the kernel
+ */
+
+#include <asm/system.h>
+
+#if 0
+extern void print_skb(struct sk_buff *);
+#endif
+extern void kfree_skb(struct sk_buff *skb, int rw);
+extern void skb_queue_head_init(struct sk_buff_head *list);
+extern void skb_queue_head(struct sk_buff_head *list,struct sk_buff *buf);
+extern void skb_queue_tail(struct sk_buff_head *list,struct sk_buff *buf);
+extern struct sk_buff * skb_dequeue(struct sk_buff_head *list);
+extern void skb_insert(struct sk_buff *old,struct sk_buff *newsk);
+extern void skb_append(struct sk_buff *old,struct sk_buff *newsk);
+extern void skb_unlink(struct sk_buff *buf);
+extern __u32 skb_queue_len(struct sk_buff_head *list);
+extern struct sk_buff * skb_peek_copy(struct sk_buff_head *list);
+extern struct sk_buff * alloc_skb(unsigned int size, int priority);
+extern struct sk_buff * dev_alloc_skb(unsigned int size);
+extern void kfree_skbmem(struct sk_buff *skb);
+extern struct sk_buff * skb_clone(struct sk_buff *skb, int priority);
+extern struct sk_buff * skb_copy(struct sk_buff *skb, int priority);
+extern void skb_device_lock(struct sk_buff *skb);
+extern void skb_device_unlock(struct sk_buff *skb);
+extern void dev_kfree_skb(struct sk_buff *skb, int mode);
+extern int skb_device_locked(struct sk_buff *skb);
+#ifdef MACH
+#define skb_put(skb, len) ((skb)->data)
+#else
+extern unsigned char * skb_put(struct sk_buff *skb, int len);
+#endif
+extern unsigned char * skb_push(struct sk_buff *skb, int len);
+extern unsigned char * skb_pull(struct sk_buff *skb, int len);
+extern int skb_headroom(struct sk_buff *skb);
+extern int skb_tailroom(struct sk_buff *skb);
+#ifdef MACH
+#define skb_reserve(skb, len)
+#else
+extern void skb_reserve(struct sk_buff *skb, int len);
+#endif
+extern void skb_trim(struct sk_buff *skb, int len);
+
+/*
+ * Peek an sk_buff. Unlike most other operations you _MUST_
+ * be careful with this one. A peek leaves the buffer on the
+ * list and someone else may run off with it. For an interrupt
+ * type system cli() peek the buffer copy the data and sti();
+ */
+extern __inline__ struct sk_buff *skb_peek(struct sk_buff_head *list_)
+{
+ struct sk_buff *list = ((struct sk_buff *)list_)->next;
+ if (list == (struct sk_buff *)list_)
+ list = NULL;
+ return list;
+}
+
+/*
+ * Return the length of an sk_buff queue
+ */
+
+extern __inline__ __u32 skb_queue_len(struct sk_buff_head *list_)
+{
+ return(list_->qlen);
+}
+
+#if CONFIG_SKB_CHECK
+extern int skb_check(struct sk_buff *skb,int,int, char *);
+#define IS_SKB(skb) skb_check((skb), 0, __LINE__,__FILE__)
+#define IS_SKB_HEAD(skb) skb_check((skb), 1, __LINE__,__FILE__)
+#else
+#define IS_SKB(skb)
+#define IS_SKB_HEAD(skb)
+
+extern __inline__ void skb_queue_head_init(struct sk_buff_head *list)
+{
+ list->prev = (struct sk_buff *)list;
+ list->next = (struct sk_buff *)list;
+ list->qlen = 0;
+}
+
+/*
+ * Insert an sk_buff at the start of a list.
+ *
+ * The "__skb_xxxx()" functions are the non-atomic ones that
+ * can only be called with interrupts disabled.
+ */
+
+extern __inline__ void __skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ struct sk_buff *prev, *next;
+
+ newsk->list = list;
+ list->qlen++;
+ prev = (struct sk_buff *)list;
+ next = prev->next;
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+}
+
+extern __inline__ void skb_queue_head(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ __skb_queue_head(list, newsk);
+ restore_flags(flags);
+}
+
+/*
+ * Insert an sk_buff at the end of a list.
+ */
+
+extern __inline__ void __skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ struct sk_buff *prev, *next;
+
+ newsk->list = list;
+ list->qlen++;
+ next = (struct sk_buff *)list;
+ prev = next->prev;
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+}
+
+extern __inline__ void skb_queue_tail(struct sk_buff_head *list, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ __skb_queue_tail(list, newsk);
+ restore_flags(flags);
+}
+
+/*
+ * Remove an sk_buff from a list.
+ */
+
+extern __inline__ struct sk_buff *__skb_dequeue(struct sk_buff_head *list)
+{
+ struct sk_buff *next, *prev, *result;
+
+ prev = (struct sk_buff *) list;
+ next = prev->next;
+ result = NULL;
+ if (next != prev) {
+ result = next;
+ next = next->next;
+ list->qlen--;
+ next->prev = prev;
+ prev->next = next;
+ result->next = NULL;
+ result->prev = NULL;
+ result->list = NULL;
+ }
+ return result;
+}
+
+extern __inline__ struct sk_buff *skb_dequeue(struct sk_buff_head *list)
+{
+ long flags;
+ struct sk_buff *result;
+
+ save_flags(flags);
+ cli();
+ result = __skb_dequeue(list);
+ restore_flags(flags);
+ return result;
+}
+
+/*
+ * Insert a packet before another one in a list.
+ */
+
+extern __inline__ void __skb_insert(struct sk_buff *next, struct sk_buff *newsk)
+{
+ struct sk_buff * prev = next->prev;
+
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+ newsk->list = next->list;
+ newsk->list->qlen++;
+}
+
+extern __inline__ void skb_insert(struct sk_buff *old, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ __skb_insert(old, newsk);
+ restore_flags(flags);
+}
+
+/*
+ * Place a packet after a given packet in a list.
+ */
+
+extern __inline__ void __skb_append(struct sk_buff *prev, struct sk_buff *newsk)
+{
+ struct sk_buff * next = prev->next;
+
+ newsk->next = next;
+ newsk->prev = prev;
+ next->prev = newsk;
+ prev->next = newsk;
+ newsk->list = prev->list;
+ newsk->list->qlen++;
+}
+
+extern __inline__ void skb_append(struct sk_buff *old, struct sk_buff *newsk)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ __skb_append(old, newsk);
+ restore_flags(flags);
+}
+
+/*
+ * remove sk_buff from list. _Must_ be called atomically, and with
+ * the list known..
+ */
+extern __inline__ void __skb_unlink(struct sk_buff *skb, struct sk_buff_head *list)
+{
+ struct sk_buff * next, * prev;
+
+ list->qlen--;
+ next = skb->next;
+ prev = skb->prev;
+ skb->next = NULL;
+ skb->prev = NULL;
+ skb->list = NULL;
+ next->prev = prev;
+ prev->next = next;
+}
+
+/*
+ * Remove an sk_buff from its list. Works even without knowing the list it
+ * is sitting on, which can be handy at times. It also means that THE LIST
+ * MUST EXIST when you unlink. Thus a list must have its contents unlinked
+ * _FIRST_.
+ */
+
+extern __inline__ void skb_unlink(struct sk_buff *skb)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if(skb->list)
+ __skb_unlink(skb, skb->list);
+ restore_flags(flags);
+}
+
+#ifndef MACH
+/*
+ * Add data to an sk_buff
+ */
+
+extern __inline__ unsigned char *skb_put(struct sk_buff *skb, int len)
+{
+ unsigned char *tmp=skb->tail;
+ skb->tail+=len;
+ skb->len+=len;
+ if(skb->tail>skb->end)
+ panic("skput:over: %p:%d", __builtin_return_address(0),len);
+ return tmp;
+}
+#endif
+
+extern __inline__ unsigned char *skb_push(struct sk_buff *skb, int len)
+{
+ skb->data-=len;
+ skb->len+=len;
+ if(skb->data<skb->head)
+ panic("skpush:under: %p:%d", __builtin_return_address(0),len);
+ return skb->data;
+}
+
+extern __inline__ unsigned char * skb_pull(struct sk_buff *skb, int len)
+{
+ if(len > skb->len)
+ return NULL;
+ skb->data+=len;
+ skb->len-=len;
+ return skb->data;
+}
+
+extern __inline__ int skb_headroom(struct sk_buff *skb)
+{
+ return skb->data-skb->head;
+}
+
+extern __inline__ int skb_tailroom(struct sk_buff *skb)
+{
+ return skb->end-skb->tail;
+}
+
+#ifndef MACH
+extern __inline__ void skb_reserve(struct sk_buff *skb, int len)
+{
+ skb->data+=len;
+ skb->tail+=len;
+}
+#endif
+
+extern __inline__ void skb_trim(struct sk_buff *skb, int len)
+{
+ if(skb->len>len)
+ {
+ skb->len=len;
+ skb->tail=skb->data+len;
+ }
+}
+
+#endif
+
+extern struct sk_buff * skb_recv_datagram(struct sock *sk,unsigned flags,int noblock, int *err);
+extern int datagram_select(struct sock *sk, int sel_type, select_table *wait);
+extern void skb_copy_datagram(struct sk_buff *from, int offset, char *to,int size);
+extern void skb_copy_datagram_iovec(struct sk_buff *from, int offset, struct iovec *to,int size);
+extern void skb_free_datagram(struct sock * sk, struct sk_buff *skb);
+
+#endif /* __KERNEL__ */
+#endif /* _LINUX_SKBUFF_H */
diff --git a/i386/i386at/gpl/linux/include/linux/smp.h b/i386/i386at/gpl/linux/include/linux/smp.h
new file mode 100644
index 00000000..72984f15
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/smp.h
@@ -0,0 +1,54 @@
+#ifndef __LINUX_SMP_H
+#define __LINUX_SMP_H
+
+/*
+ * Generic SMP support
+ * Alan Cox. <alan@cymru.net>
+ */
+
+#ifdef __SMP__
+#include <asm/smp.h>
+
+extern void smp_message_pass(int target, int msg, unsigned long data, int wait);
+extern void smp_boot_cpus(void); /* Boot processor call to load the other CPU's */
+extern void smp_callin(void); /* Processor call in. Must hold processors until .. */
+extern void smp_commence(void); /* Multiprocessors may now schedule */
+extern int smp_num_cpus;
+extern int smp_threads_ready; /* True once the per process idle is forked */
+#ifdef __SMP_PROF__
+extern volatile unsigned long smp_spins[NR_CPUS]; /* count of interrupt spins */
+extern volatile unsigned long smp_spins_sys_idle[]; /* count of idle spins */
+extern volatile unsigned long smp_spins_syscall[]; /* count of syscall spins */
+extern volatile unsigned long smp_spins_syscall_cur[]; /* count of syscall spins for the current
+ call */
+extern volatile unsigned long smp_idle_count[1+NR_CPUS];/* count idle ticks */
+extern volatile unsigned long smp_idle_map; /* map with idle cpus */
+#else
+extern volatile unsigned long smp_spins;
+#endif
+
+
+extern volatile unsigned long smp_msg_data;
+extern volatile int smp_src_cpu;
+extern volatile int smp_msg_id;
+
+#define MSG_ALL_BUT_SELF 0x8000 /* Assume <32768 CPU's */
+#define MSG_ALL 0x8001
+
+#define MSG_INVALIDATE_TLB 0x0001 /* Remote processor TLB invalidate */
+#define MSG_STOP_CPU 0x0002 /* Sent to shut down slave CPU's when rebooting */
+#define MSG_RESCHEDULE 0x0003 /* Reschedule request from master CPU */
+
+#else
+
+/*
+ * These macros fold the SMP functionality into a single CPU system
+ */
+
+#define smp_num_cpus 1
+#define smp_processor_id() 0
+#define smp_message_pass(t,m,d,w)
+#define smp_threads_ready 1
+#define kernel_lock()
+#endif
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/socket.h b/i386/i386at/gpl/linux/include/linux/socket.h
new file mode 100644
index 00000000..bf2991ad
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/socket.h
@@ -0,0 +1,126 @@
+#ifndef _LINUX_SOCKET_H
+#define _LINUX_SOCKET_H
+
+#include <asm/socket.h> /* arch-dependent defines */
+#include <linux/sockios.h> /* the SIOCxxx I/O controls */
+#include <linux/uio.h> /* iovec support */
+
+struct sockaddr {
+ unsigned short sa_family; /* address family, AF_xxx */
+ char sa_data[14]; /* 14 bytes of protocol address */
+};
+
+struct linger {
+ int l_onoff; /* Linger active */
+ int l_linger; /* How long to linger for */
+};
+
+struct msghdr
+{
+ void * msg_name; /* Socket name */
+ int msg_namelen; /* Length of name */
+ struct iovec * msg_iov; /* Data blocks */
+ int msg_iovlen; /* Number of blocks */
+ void * msg_accrights; /* Per protocol magic (eg BSD file descriptor passing) */
+ int msg_accrightslen; /* Length of rights list */
+};
+
+/* Socket types. */
+#define SOCK_STREAM 1 /* stream (connection) socket */
+#define SOCK_DGRAM 2 /* datagram (conn.less) socket */
+#define SOCK_RAW 3 /* raw socket */
+#define SOCK_RDM 4 /* reliably-delivered message */
+#define SOCK_SEQPACKET 5 /* sequential packet socket */
+#define SOCK_PACKET 10 /* linux specific way of */
+ /* getting packets at the dev */
+ /* level. For writing rarp and */
+ /* other similar things on the */
+ /* user level. */
+
+/* Supported address families. */
+#define AF_UNSPEC 0
+#define AF_UNIX 1 /* Unix domain sockets */
+#define AF_INET 2 /* Internet IP Protocol */
+#define AF_AX25 3 /* Amateur Radio AX.25 */
+#define AF_IPX 4 /* Novell IPX */
+#define AF_APPLETALK 5 /* Appletalk DDP */
+#define AF_NETROM 6 /* Amateur radio NetROM */
+#define AF_BRIDGE 7 /* Multiprotocol bridge */
+#define AF_AAL5 8 /* Reserved for Werner's ATM */
+#define AF_X25 9 /* Reserved for X.25 project */
+#define AF_INET6 10 /* IP version 6 */
+#define AF_MAX 12 /* For now.. */
+
+/* Protocol families, same as address families. */
+#define PF_UNSPEC AF_UNSPEC
+#define PF_UNIX AF_UNIX
+#define PF_INET AF_INET
+#define PF_AX25 AF_AX25
+#define PF_IPX AF_IPX
+#define PF_APPLETALK AF_APPLETALK
+#define PF_NETROM AF_NETROM
+#define PF_BRIDGE AF_BRIDGE
+#define PF_AAL5 AF_AAL5
+#define PF_X25 AF_X25
+#define PF_INET6 AF_INET6
+
+#define PF_MAX AF_MAX
+
+/* Maximum queue length specificable by listen. */
+#define SOMAXCONN 128
+
+/* Flags we can use with send/ and recv. */
+#define MSG_OOB 1
+#define MSG_PEEK 2
+#define MSG_DONTROUTE 4
+
+/* Setsockoptions(2) level. Thanks to BSD these must match IPPROTO_xxx */
+#define SOL_IP 0
+#define SOL_IPX 256
+#define SOL_AX25 257
+#define SOL_ATALK 258
+#define SOL_NETROM 259
+#define SOL_TCP 6
+#define SOL_UDP 17
+
+/* IP options */
+#define IP_TOS 1
+#define IPTOS_LOWDELAY 0x10
+#define IPTOS_THROUGHPUT 0x08
+#define IPTOS_RELIABILITY 0x04
+#define IP_TTL 2
+#define IP_HDRINCL 3
+#define IP_OPTIONS 4
+
+#define IP_MULTICAST_IF 32
+#define IP_MULTICAST_TTL 33
+#define IP_MULTICAST_LOOP 34
+#define IP_ADD_MEMBERSHIP 35
+#define IP_DROP_MEMBERSHIP 36
+
+
+/* These need to appear somewhere around here */
+#define IP_DEFAULT_MULTICAST_TTL 1
+#define IP_DEFAULT_MULTICAST_LOOP 1
+#define IP_MAX_MEMBERSHIPS 20
+
+/* IPX options */
+#define IPX_TYPE 1
+
+/* TCP options - this way around because someone left a set in the c library includes */
+#define TCP_NODELAY 1
+#define TCP_MAXSEG 2
+
+/* The various priorities. */
+#define SOPRI_INTERACTIVE 0
+#define SOPRI_NORMAL 1
+#define SOPRI_BACKGROUND 2
+
+#ifdef __KERNEL__
+extern void memcpy_fromiovec(unsigned char *kdata, struct iovec *iov, int len);
+extern int verify_iovec(struct msghdr *m, struct iovec *iov, char *address, int mode);
+extern void memcpy_toiovec(struct iovec *v, unsigned char *kdata, int len);
+extern int move_addr_to_user(void *kaddr, int klen, void *uaddr, int *ulen);
+extern int move_addr_to_kernel(void *uaddr, int ulen, void *kaddr);
+#endif
+#endif /* _LINUX_SOCKET_H */
diff --git a/i386/i386at/gpl/linux/include/linux/sockios.h b/i386/i386at/gpl/linux/include/linux/sockios.h
new file mode 100644
index 00000000..ee20a0b1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/sockios.h
@@ -0,0 +1,91 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions of the socket-level I/O control calls.
+ *
+ * Version: @(#)sockios.h 1.0.2 03/09/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_SOCKIOS_H
+#define _LINUX_SOCKIOS_H
+
+/* Routing table calls. */
+#define SIOCADDRT 0x890B /* add routing table entry */
+#define SIOCDELRT 0x890C /* delete routing table entry */
+
+/* Socket configuration controls. */
+#define SIOCGIFNAME 0x8910 /* get iface name */
+#define SIOCSIFLINK 0x8911 /* set iface channel */
+#define SIOCGIFCONF 0x8912 /* get iface list */
+#define SIOCGIFFLAGS 0x8913 /* get flags */
+#define SIOCSIFFLAGS 0x8914 /* set flags */
+#define SIOCGIFADDR 0x8915 /* get PA address */
+#define SIOCSIFADDR 0x8916 /* set PA address */
+#define SIOCGIFDSTADDR 0x8917 /* get remote PA address */
+#define SIOCSIFDSTADDR 0x8918 /* set remote PA address */
+#define SIOCGIFBRDADDR 0x8919 /* get broadcast PA address */
+#define SIOCSIFBRDADDR 0x891a /* set broadcast PA address */
+#define SIOCGIFNETMASK 0x891b /* get network PA mask */
+#define SIOCSIFNETMASK 0x891c /* set network PA mask */
+#define SIOCGIFMETRIC 0x891d /* get metric */
+#define SIOCSIFMETRIC 0x891e /* set metric */
+#define SIOCGIFMEM 0x891f /* get memory address (BSD) */
+#define SIOCSIFMEM 0x8920 /* set memory address (BSD) */
+#define SIOCGIFMTU 0x8921 /* get MTU size */
+#define SIOCSIFMTU 0x8922 /* set MTU size */
+#define SIOCSIFHWADDR 0x8924 /* set hardware address (NI) */
+#define SIOCGIFENCAP 0x8925 /* get/set slip encapsulation */
+#define SIOCSIFENCAP 0x8926
+#define SIOCGIFHWADDR 0x8927 /* Get hardware address */
+#define SIOCGIFSLAVE 0x8929 /* Driver slaving support */
+#define SIOCSIFSLAVE 0x8930
+/* begin multicast support change */
+#define SIOCADDMULTI 0x8931
+#define SIOCDELMULTI 0x8932
+/* end multicast support change */
+
+/* ARP cache control calls. */
+#define OLD_SIOCDARP 0x8950 /* old delete ARP table entry */
+#define OLD_SIOCGARP 0x8951 /* old get ARP table entry */
+#define OLD_SIOCSARP 0x8952 /* old set ARP table entry */
+#define SIOCDARP 0x8953 /* delete ARP table entry */
+#define SIOCGARP 0x8954 /* get ARP table entry */
+#define SIOCSARP 0x8955 /* set ARP table entry */
+
+/* RARP cache control calls. */
+#define SIOCDRARP 0x8960 /* delete RARP table entry */
+#define SIOCGRARP 0x8961 /* get RARP table entry */
+#define SIOCSRARP 0x8962 /* set RARP table entry */
+
+/* Driver configuration calls */
+
+#define SIOCGIFMAP 0x8970 /* Get device parameters */
+#define SIOCSIFMAP 0x8971 /* Set device parameters */
+
+
+/* Device private ioctl calls */
+
+/*
+ * These 16 ioctls are available to devices via the do_ioctl() device
+ * vector. Each device should include this file and redefine these names
+ * as their own. Because these are device dependent it is a good idea
+ * _NOT_ to issue them to random objects and hope.
+ */
+
+#define SIOCDEVPRIVATE 0x89F0 /* to 89FF */
+
+/*
+ * These 16 ioctl calls are protocol private
+ */
+
+#define SIOCPROTOPRIVATE 0x89E0 /* to 89EF */
+#endif /* _LINUX_SOCKIOS_H */
diff --git a/i386/i386at/gpl/linux/include/linux/stat.h b/i386/i386at/gpl/linux/include/linux/stat.h
new file mode 100644
index 00000000..d86b1646
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/stat.h
@@ -0,0 +1,53 @@
+#ifndef _LINUX_STAT_H
+#define _LINUX_STAT_H
+
+#ifdef __KERNEL__
+
+#include <asm/stat.h>
+
+#endif
+
+#define S_IFMT 00170000
+#define S_IFSOCK 0140000
+#define S_IFLNK 0120000
+#define S_IFREG 0100000
+#define S_IFBLK 0060000
+#define S_IFDIR 0040000
+#define S_IFCHR 0020000
+#define S_IFIFO 0010000
+#define S_ISUID 0004000
+#define S_ISGID 0002000
+#define S_ISVTX 0001000
+
+#define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK)
+#define S_ISREG(m) (((m) & S_IFMT) == S_IFREG)
+#define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR)
+#define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR)
+#define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK)
+#define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO)
+#define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK)
+
+#define S_IRWXU 00700
+#define S_IRUSR 00400
+#define S_IWUSR 00200
+#define S_IXUSR 00100
+
+#define S_IRWXG 00070
+#define S_IRGRP 00040
+#define S_IWGRP 00020
+#define S_IXGRP 00010
+
+#define S_IRWXO 00007
+#define S_IROTH 00004
+#define S_IWOTH 00002
+#define S_IXOTH 00001
+
+#ifdef __KERNEL__
+#define S_IRWXUGO (S_IRWXU|S_IRWXG|S_IRWXO)
+#define S_IALLUGO (S_ISUID|S_ISGID|S_ISVTX|S_IRWXUGO)
+#define S_IRUGO (S_IRUSR|S_IRGRP|S_IROTH)
+#define S_IWUGO (S_IWUSR|S_IWGRP|S_IWOTH)
+#define S_IXUGO (S_IXUSR|S_IXGRP|S_IXOTH)
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/stddef.h b/i386/i386at/gpl/linux/include/linux/stddef.h
new file mode 100644
index 00000000..c6221e71
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/stddef.h
@@ -0,0 +1,15 @@
+#ifndef _LINUX_STDDEF_H
+#define _LINUX_STDDEF_H
+
+#ifndef _SIZE_T
+#define _SIZE_T
+typedef unsigned int size_t;
+#endif
+
+#undef NULL
+#define NULL ((void *)0)
+
+#undef offsetof
+#define offsetof(TYPE, MEMBER) ((size_t) &((TYPE *)0)->MEMBER)
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/string.h b/i386/i386at/gpl/linux/include/linux/string.h
new file mode 100644
index 00000000..e9162b38
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/string.h
@@ -0,0 +1,44 @@
+#ifndef _LINUX_STRING_H_
+#define _LINUX_STRING_H_
+
+#include <linux/types.h> /* for size_t */
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern char * ___strtok;
+extern char * strcpy(char *,const char *);
+extern char * strncpy(char *,const char *,size_t);
+extern char * strcat(char *, const char *);
+extern char * strncat(char *, const char *, size_t);
+extern char * strchr(const char *,int);
+extern char * strpbrk(const char *,const char *);
+extern char * strtok(char *,const char *);
+extern char * strstr(const char *,const char *);
+extern size_t strlen(const char *);
+extern size_t strnlen(const char *,size_t);
+extern size_t strspn(const char *,const char *);
+extern int strcmp(const char *,const char *);
+extern int strncmp(const char *,const char *,size_t);
+
+extern void * memset(void *,int,size_t);
+extern void * memcpy(void *,const void *,size_t);
+extern void * memmove(void *,const void *,size_t);
+extern void * memscan(void *,int,size_t);
+extern int memcmp(const void *,const void *,size_t);
+
+/*
+ * Include machine specific inline routines
+ */
+#include <asm/string.h>
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _LINUX_STRING_H_ */
diff --git a/i386/i386at/gpl/linux/include/linux/tasks.h b/i386/i386at/gpl/linux/include/linux/tasks.h
new file mode 100644
index 00000000..4540e34f
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/tasks.h
@@ -0,0 +1,19 @@
+#ifndef _LINUX_TASKS_H
+#define _LINUX_TASKS_H
+
+/*
+ * This is the maximum nr of tasks - change it if you need to
+ */
+
+#ifdef __SMP__
+#define NR_CPUS 32 /* Max processors that can be running in SMP */
+#else
+#define NR_CPUS 1
+#endif
+
+#define NR_TASKS 512
+
+#define MAX_TASKS_PER_USER (NR_TASKS/2)
+#define MIN_TASKS_LEFT_FOR_ROOT 4
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/tcp.h b/i386/i386at/gpl/linux/include/linux/tcp.h
new file mode 100644
index 00000000..ae6a063e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/tcp.h
@@ -0,0 +1,71 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP protocol.
+ *
+ * Version: @(#)tcp.h 1.0.2 04/28/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _LINUX_TCP_H
+#define _LINUX_TCP_H
+
+#include <linux/types.h>
+#include <asm/byteorder.h>
+
+struct tcphdr {
+ __u16 source;
+ __u16 dest;
+ __u32 seq;
+ __u32 ack_seq;
+#if defined(__LITTLE_ENDIAN_BITFIELD)
+ __u16 res1:4,
+ doff:4,
+ fin:1,
+ syn:1,
+ rst:1,
+ psh:1,
+ ack:1,
+ urg:1,
+ res2:2;
+#elif defined(__BIG_ENDIAN_BITFIELD)
+ __u16 doff:4,
+ res1:4,
+ res2:2,
+ urg:1,
+ ack:1,
+ psh:1,
+ rst:1,
+ syn:1,
+ fin:1;
+#else
+#error "Adjust your <asm/byteorder.h> defines"
+#endif
+ __u16 window;
+ __u16 check;
+ __u16 urg_ptr;
+};
+
+
+enum {
+ TCP_ESTABLISHED = 1,
+ TCP_SYN_SENT,
+ TCP_SYN_RECV,
+ TCP_FIN_WAIT1,
+ TCP_FIN_WAIT2,
+ TCP_TIME_WAIT,
+ TCP_CLOSE,
+ TCP_CLOSE_WAIT,
+ TCP_LAST_ACK,
+ TCP_LISTEN,
+ TCP_CLOSING /* now a valid state */
+};
+
+#endif /* _LINUX_TCP_H */
diff --git a/i386/i386at/gpl/linux/include/linux/termios.h b/i386/i386at/gpl/linux/include/linux/termios.h
new file mode 100644
index 00000000..47866288
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/termios.h
@@ -0,0 +1,7 @@
+#ifndef _LINUX_TERMIOS_H
+#define _LINUX_TERMIOS_H
+
+#include <linux/types.h>
+#include <asm/termios.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/time.h b/i386/i386at/gpl/linux/include/linux/time.h
new file mode 100644
index 00000000..269e9dc6
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/time.h
@@ -0,0 +1,50 @@
+#ifndef _LINUX_TIME_H
+#define _LINUX_TIME_H
+
+struct timespec {
+ long tv_sec; /* seconds */
+ long tv_nsec; /* nanoseconds */
+};
+
+struct timeval {
+ int tv_sec; /* seconds */
+ int tv_usec; /* microseconds */
+};
+
+struct timezone {
+ int tz_minuteswest; /* minutes west of Greenwich */
+ int tz_dsttime; /* type of dst correction */
+};
+
+#define NFDBITS __NFDBITS
+
+#ifdef __KERNEL__
+void do_gettimeofday(struct timeval *tv);
+void do_settimeofday(struct timeval *tv);
+#endif
+
+#define FD_SETSIZE __FD_SETSIZE
+#define FD_SET(fd,fdsetp) __FD_SET(fd,fdsetp)
+#define FD_CLR(fd,fdsetp) __FD_CLR(fd,fdsetp)
+#define FD_ISSET(fd,fdsetp) __FD_ISSET(fd,fdsetp)
+#define FD_ZERO(fdsetp) __FD_ZERO(fdsetp)
+
+/*
+ * Names of the interval timers, and structure
+ * defining a timer setting.
+ */
+#define ITIMER_REAL 0
+#define ITIMER_VIRTUAL 1
+#define ITIMER_PROF 2
+
+struct itimerspec {
+ struct timespec it_interval; /* timer period */
+ struct timespec it_value; /* timer expiration */
+};
+
+struct itimerval {
+ struct timeval it_interval; /* timer interval */
+ struct timeval it_value; /* current value */
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/timer.h b/i386/i386at/gpl/linux/include/linux/timer.h
new file mode 100644
index 00000000..c54e8c5e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/timer.h
@@ -0,0 +1,101 @@
+#ifndef _LINUX_TIMER_H
+#define _LINUX_TIMER_H
+
+/*
+ * DON'T CHANGE THESE!! Most of them are hardcoded into some assembly language
+ * as well as being defined here.
+ */
+
+/*
+ * The timers are:
+ *
+ * BLANK_TIMER console screen-saver timer
+ *
+ * BEEP_TIMER console beep timer
+ *
+ * RS_TIMER timer for the RS-232 ports
+ *
+ * SWAP_TIMER timer for the background pageout daemon
+ *
+ * HD_TIMER harddisk timer
+ *
+ * HD_TIMER2 (atdisk2 patches)
+ *
+ * FLOPPY_TIMER floppy disk timer (not used right now)
+ *
+ * SCSI_TIMER scsi.c timeout timer
+ *
+ * NET_TIMER tcp/ip timeout timer
+ *
+ * COPRO_TIMER 387 timeout for buggy hardware..
+ *
+ * QIC02_TAPE_TIMER timer for QIC-02 tape driver (it's not hardcoded)
+ *
+ * MCD_TIMER Mitsumi CD-ROM Timer
+ *
+ * GSCD_TIMER Goldstar CD-ROM Timer
+ *
+ * OPTCD_TIMER Optics Storage CD-ROM Timer
+ *
+ */
+
+#define BLANK_TIMER 0
+#define BEEP_TIMER 1
+#define RS_TIMER 2
+#define SWAP_TIMER 3
+
+#define HD_TIMER 16
+#define FLOPPY_TIMER 17
+#define SCSI_TIMER 18
+#define NET_TIMER 19
+#define SOUND_TIMER 20
+#define COPRO_TIMER 21
+
+#define QIC02_TAPE_TIMER 22 /* hhb */
+#define MCD_TIMER 23
+
+#define HD_TIMER2 24
+#define GSCD_TIMER 25
+#define OPTCD_TIMER 26
+
+struct timer_struct {
+ unsigned long expires;
+ void (*fn)(void);
+};
+
+extern unsigned long timer_active;
+extern struct timer_struct timer_table[32];
+
+/*
+ * This is completely separate from the above, and is the
+ * "new and improved" way of handling timers more dynamically.
+ * Hopefully efficient and general enough for most things.
+ *
+ * The "hardcoded" timers above are still useful for well-
+ * defined problems, but the timer-list is probably better
+ * when you need multiple outstanding timers or similar.
+ *
+ * The "data" field is in case you want to use the same
+ * timeout function for several timeouts. You can use this
+ * to distinguish between the different invocations.
+ */
+struct timer_list {
+ struct timer_list *next;
+ struct timer_list *prev;
+ unsigned long expires;
+ unsigned long data;
+ void (*function)(unsigned long);
+};
+
+extern void add_timer(struct timer_list * timer);
+extern int del_timer(struct timer_list * timer);
+
+extern void it_real_fn(unsigned long);
+
+extern inline void init_timer(struct timer_list * timer)
+{
+ timer->next = NULL;
+ timer->prev = NULL;
+}
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/tqueue.h b/i386/i386at/gpl/linux/include/linux/tqueue.h
new file mode 100644
index 00000000..d483a155
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/tqueue.h
@@ -0,0 +1,163 @@
+/*
+ * tqueue.h --- task queue handling for Linux.
+ *
+ * Mostly based on a proposed bottom-half replacement code written by
+ * Kai Petzke, wpp@marie.physik.tu-berlin.de.
+ *
+ * Modified for use in the Linux kernel by Theodore Ts'o,
+ * tytso@mit.edu. Any bugs are my fault, not Kai's.
+ *
+ * The original comment follows below.
+ */
+
+#ifndef _LINUX_TQUEUE_H
+#define _LINUX_TQUEUE_H
+
+#include <asm/bitops.h>
+#include <asm/system.h>
+
+#ifdef INCLUDE_INLINE_FUNCS
+#define _INLINE_ extern
+#else
+#define _INLINE_ extern __inline__
+#endif
+
+/*
+ * New proposed "bottom half" handlers:
+ * (C) 1994 Kai Petzke, wpp@marie.physik.tu-berlin.de
+ *
+ * Advantages:
+ * - Bottom halfs are implemented as a linked list. You can have as many
+ * of them, as you want.
+ * - No more scanning of a bit field is required upon call of a bottom half.
+ * - Support for chained bottom half lists. The run_task_queue() function can be
+ * used as a bottom half handler. This is for example useful for bottom
+ * halfs, which want to be delayed until the next clock tick.
+ *
+ * Problems:
+ * - The queue_task_irq() inline function is only atomic with respect to itself.
+ * Problems can occur, when queue_task_irq() is called from a normal system
+ * call, and an interrupt comes in. No problems occur, when queue_task_irq()
+ * is called from an interrupt or bottom half, and interrupted, as run_task_queue()
+ * will not be executed/continued before the last interrupt returns. If in
+ * doubt, use queue_task(), not queue_task_irq().
+ * - Bottom halfs are called in the reverse order that they were linked into
+ * the list.
+ */
+
+struct tq_struct {
+ struct tq_struct *next; /* linked list of active bh's */
+ int sync; /* must be initialized to zero */
+ void (*routine)(void *); /* function to call */
+ void *data; /* argument to function */
+};
+
+typedef struct tq_struct * task_queue;
+
+#define DECLARE_TASK_QUEUE(q) task_queue q = &tq_last
+
+extern struct tq_struct tq_last;
+extern task_queue tq_timer, tq_immediate, tq_scheduler;
+
+#ifdef INCLUDE_INLINE_FUNCS
+struct tq_struct tq_last = {
+ &tq_last, 0, 0, 0
+};
+#endif
+
+/*
+ * To implement your own list of active bottom halfs, use the following
+ * two definitions:
+ *
+ * struct tq_struct *my_bh = &tq_last;
+ * struct tq_struct run_my_bh = {
+ * 0, 0, (void *)(void *) run_task_queue, &my_bh
+ * };
+ *
+ * To activate a bottom half on your list, use:
+ *
+ * queue_task(tq_pointer, &my_bh);
+ *
+ * To run the bottom halfs on your list put them on the immediate list by:
+ *
+ * queue_task(&run_my_bh, &tq_immediate);
+ *
+ * This allows you to do deferred procession. For example, you could
+ * have a bottom half list tq_timer, which is marked active by the timer
+ * interrupt.
+ */
+
+/*
+ * queue_task_irq: put the bottom half handler "bh_pointer" on the list
+ * "bh_list". You may call this function only from an interrupt
+ * handler or a bottom half handler.
+ */
+_INLINE_ void queue_task_irq(struct tq_struct *bh_pointer,
+ task_queue *bh_list)
+{
+ if (!set_bit(0,&bh_pointer->sync)) {
+ bh_pointer->next = *bh_list;
+ *bh_list = bh_pointer;
+ }
+}
+
+/*
+ * queue_task_irq_off: put the bottom half handler "bh_pointer" on the list
+ * "bh_list". You may call this function only when interrupts are off.
+ */
+_INLINE_ void queue_task_irq_off(struct tq_struct *bh_pointer,
+ task_queue *bh_list)
+{
+ if (!(bh_pointer->sync & 1)) {
+ bh_pointer->sync = 1;
+ bh_pointer->next = *bh_list;
+ *bh_list = bh_pointer;
+ }
+}
+
+
+/*
+ * queue_task: as queue_task_irq, but can be called from anywhere.
+ */
+_INLINE_ void queue_task(struct tq_struct *bh_pointer,
+ task_queue *bh_list)
+{
+ if (!set_bit(0,&bh_pointer->sync)) {
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ bh_pointer->next = *bh_list;
+ *bh_list = bh_pointer;
+ restore_flags(flags);
+ }
+}
+
+/*
+ * Call all "bottom halfs" on a given list.
+ */
+_INLINE_ void run_task_queue(task_queue *list)
+{
+ register struct tq_struct *save_p;
+ register struct tq_struct *p;
+ void *arg;
+ void (*f) (void *);
+
+ while(1) {
+ p = xchg(list,&tq_last);
+ if(p == &tq_last)
+ break;
+
+ do {
+ arg = p -> data;
+ f = p -> routine;
+ save_p = p -> next;
+ p -> sync = 0;
+ (*f)(arg);
+ p = save_p;
+ } while(p != &tq_last);
+ }
+}
+
+#undef _INLINE_
+
+#endif /* _LINUX_TQUEUE_H */
diff --git a/i386/i386at/gpl/linux/include/linux/trdevice.h b/i386/i386at/gpl/linux/include/linux/trdevice.h
new file mode 100644
index 00000000..96801763
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/trdevice.h
@@ -0,0 +1,40 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. NET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the Ethernet handlers.
+ *
+ * Version: @(#)eth.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Relocated to include/linux where it belongs by Alan Cox
+ * <gw4pts@gw4pts.ampr.org>
+ *
+ * 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.
+ *
+ * WARNING: This move may well be temporary. This file will get merged with others RSN.
+ *
+ */
+#ifndef _LINUX_TRDEVICE_H
+#define _LINUX_TRDEVICE_H
+
+
+#include <linux/if_tr.h>
+
+#ifdef __KERNEL__
+extern int tr_header(struct sk_buff *skb, struct device *dev,
+ unsigned short type, void *daddr,
+ void *saddr, unsigned len);
+extern int tr_rebuild_header(void *buff, struct device *dev,
+ unsigned long raddr, struct sk_buff *skb);
+extern unsigned short tr_type_trans(struct sk_buff *skb, struct device *dev);
+
+#endif
+
+#endif /* _LINUX_TRDEVICE_H */
diff --git a/i386/i386at/gpl/linux/include/linux/tty.h b/i386/i386at/gpl/linux/include/linux/tty.h
new file mode 100644
index 00000000..fe139511
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/tty.h
@@ -0,0 +1,340 @@
+#ifndef _LINUX_TTY_H
+#define _LINUX_TTY_H
+
+/*
+ * 'tty.h' defines some structures used by tty_io.c and some defines.
+ */
+
+#ifdef __KERNEL__
+#include <linux/fs.h>
+#include <linux/termios.h>
+#include <linux/tqueue.h>
+#include <linux/tty_driver.h>
+#include <linux/tty_ldisc.h>
+
+#include <asm/system.h>
+
+
+/*
+ * Note: don't mess with NR_PTYS until you understand the tty minor
+ * number allocation game...
+ * (Note: the *_driver.minor_start values 1, 64, 128, 192 are
+ * hardcoded at present.)
+ */
+#define MIN_NR_CONSOLES 1 /* must be at least 1 */
+#define MAX_NR_CONSOLES 63 /* serial lines start at 64 */
+#define MAX_NR_USER_CONSOLES 63 /* must be root to allocate above this */
+ /* Note: the ioctl VT_GETSTATE does not work for
+ consoles 16 and higher (since it returns a short) */
+#define NR_PTYS 256
+#define NR_LDISCS 16
+
+/*
+ * These are set up by the setup-routine at boot-time:
+ */
+
+struct screen_info {
+ unsigned char orig_x;
+ unsigned char orig_y;
+ unsigned char unused1[2];
+ unsigned short orig_video_page;
+ unsigned char orig_video_mode;
+ unsigned char orig_video_cols;
+ unsigned short unused2;
+ unsigned short orig_video_ega_bx;
+ unsigned short unused3;
+ unsigned char orig_video_lines;
+ unsigned char orig_video_isVGA;
+ unsigned short orig_video_points;
+};
+
+extern struct screen_info screen_info;
+
+#define ORIG_X (screen_info.orig_x)
+#define ORIG_Y (screen_info.orig_y)
+#define ORIG_VIDEO_PAGE (screen_info.orig_video_page)
+#define ORIG_VIDEO_MODE (screen_info.orig_video_mode)
+#define ORIG_VIDEO_COLS (screen_info.orig_video_cols)
+#define ORIG_VIDEO_EGA_BX (screen_info.orig_video_ega_bx)
+#define ORIG_VIDEO_LINES (screen_info.orig_video_lines)
+#define ORIG_VIDEO_ISVGA (screen_info.orig_video_isVGA)
+#define ORIG_VIDEO_POINTS (screen_info.orig_video_points)
+
+#define VIDEO_TYPE_MDA 0x10 /* Monochrome Text Display */
+#define VIDEO_TYPE_CGA 0x11 /* CGA Display */
+#define VIDEO_TYPE_EGAM 0x20 /* EGA/VGA in Monochrome Mode */
+#define VIDEO_TYPE_EGAC 0x21 /* EGA in Color Mode */
+#define VIDEO_TYPE_VGAC 0x22 /* VGA+ in Color Mode */
+
+#define VIDEO_TYPE_TGAC 0x40 /* DEC TGA */
+
+/*
+ * This character is the same as _POSIX_VDISABLE: it cannot be used as
+ * a c_cc[] character, but indicates that a particular special character
+ * isn't in use (eg VINTR has no character etc)
+ */
+#define __DISABLED_CHAR '\0'
+
+/*
+ * This is the flip buffer used for the tty driver. The buffer is
+ * located in the tty structure, and is used as a high speed interface
+ * between the tty driver and the tty line discipline.
+ */
+#define TTY_FLIPBUF_SIZE 512
+
+struct tty_flip_buffer {
+ struct tq_struct tqueue;
+ unsigned char char_buf[2*TTY_FLIPBUF_SIZE];
+ char flag_buf[2*TTY_FLIPBUF_SIZE];
+ char *char_buf_ptr;
+ unsigned char *flag_buf_ptr;
+ int count;
+ int buf_num;
+};
+
+/*
+ * When a break, frame error, or parity error happens, these codes are
+ * stuffed into the flags buffer.
+ */
+#define TTY_NORMAL 0
+#define TTY_BREAK 1
+#define TTY_FRAME 2
+#define TTY_PARITY 3
+#define TTY_OVERRUN 4
+
+#define INTR_CHAR(tty) ((tty)->termios->c_cc[VINTR])
+#define QUIT_CHAR(tty) ((tty)->termios->c_cc[VQUIT])
+#define ERASE_CHAR(tty) ((tty)->termios->c_cc[VERASE])
+#define KILL_CHAR(tty) ((tty)->termios->c_cc[VKILL])
+#define EOF_CHAR(tty) ((tty)->termios->c_cc[VEOF])
+#define TIME_CHAR(tty) ((tty)->termios->c_cc[VTIME])
+#define MIN_CHAR(tty) ((tty)->termios->c_cc[VMIN])
+#define SWTC_CHAR(tty) ((tty)->termios->c_cc[VSWTC])
+#define START_CHAR(tty) ((tty)->termios->c_cc[VSTART])
+#define STOP_CHAR(tty) ((tty)->termios->c_cc[VSTOP])
+#define SUSP_CHAR(tty) ((tty)->termios->c_cc[VSUSP])
+#define EOL_CHAR(tty) ((tty)->termios->c_cc[VEOL])
+#define REPRINT_CHAR(tty) ((tty)->termios->c_cc[VREPRINT])
+#define DISCARD_CHAR(tty) ((tty)->termios->c_cc[VDISCARD])
+#define WERASE_CHAR(tty) ((tty)->termios->c_cc[VWERASE])
+#define LNEXT_CHAR(tty) ((tty)->termios->c_cc[VLNEXT])
+#define EOL2_CHAR(tty) ((tty)->termios->c_cc[VEOL2])
+
+#define _I_FLAG(tty,f) ((tty)->termios->c_iflag & (f))
+#define _O_FLAG(tty,f) ((tty)->termios->c_oflag & (f))
+#define _C_FLAG(tty,f) ((tty)->termios->c_cflag & (f))
+#define _L_FLAG(tty,f) ((tty)->termios->c_lflag & (f))
+
+#define I_IGNBRK(tty) _I_FLAG((tty),IGNBRK)
+#define I_BRKINT(tty) _I_FLAG((tty),BRKINT)
+#define I_IGNPAR(tty) _I_FLAG((tty),IGNPAR)
+#define I_PARMRK(tty) _I_FLAG((tty),PARMRK)
+#define I_INPCK(tty) _I_FLAG((tty),INPCK)
+#define I_ISTRIP(tty) _I_FLAG((tty),ISTRIP)
+#define I_INLCR(tty) _I_FLAG((tty),INLCR)
+#define I_IGNCR(tty) _I_FLAG((tty),IGNCR)
+#define I_ICRNL(tty) _I_FLAG((tty),ICRNL)
+#define I_IUCLC(tty) _I_FLAG((tty),IUCLC)
+#define I_IXON(tty) _I_FLAG((tty),IXON)
+#define I_IXANY(tty) _I_FLAG((tty),IXANY)
+#define I_IXOFF(tty) _I_FLAG((tty),IXOFF)
+#define I_IMAXBEL(tty) _I_FLAG((tty),IMAXBEL)
+
+#define O_OPOST(tty) _O_FLAG((tty),OPOST)
+#define O_OLCUC(tty) _O_FLAG((tty),OLCUC)
+#define O_ONLCR(tty) _O_FLAG((tty),ONLCR)
+#define O_OCRNL(tty) _O_FLAG((tty),OCRNL)
+#define O_ONOCR(tty) _O_FLAG((tty),ONOCR)
+#define O_ONLRET(tty) _O_FLAG((tty),ONLRET)
+#define O_OFILL(tty) _O_FLAG((tty),OFILL)
+#define O_OFDEL(tty) _O_FLAG((tty),OFDEL)
+#define O_NLDLY(tty) _O_FLAG((tty),NLDLY)
+#define O_CRDLY(tty) _O_FLAG((tty),CRDLY)
+#define O_TABDLY(tty) _O_FLAG((tty),TABDLY)
+#define O_BSDLY(tty) _O_FLAG((tty),BSDLY)
+#define O_VTDLY(tty) _O_FLAG((tty),VTDLY)
+#define O_FFDLY(tty) _O_FLAG((tty),FFDLY)
+
+#define C_BAUD(tty) _C_FLAG((tty),CBAUD)
+#define C_CSIZE(tty) _C_FLAG((tty),CSIZE)
+#define C_CSTOPB(tty) _C_FLAG((tty),CSTOPB)
+#define C_CREAD(tty) _C_FLAG((tty),CREAD)
+#define C_PARENB(tty) _C_FLAG((tty),PARENB)
+#define C_PARODD(tty) _C_FLAG((tty),PARODD)
+#define C_HUPCL(tty) _C_FLAG((tty),HUPCL)
+#define C_CLOCAL(tty) _C_FLAG((tty),CLOCAL)
+#define C_CIBAUD(tty) _C_FLAG((tty),CIBAUD)
+#define C_CRTSCTS(tty) _C_FLAG((tty),CRTSCTS)
+
+#define L_ISIG(tty) _L_FLAG((tty),ISIG)
+#define L_ICANON(tty) _L_FLAG((tty),ICANON)
+#define L_XCASE(tty) _L_FLAG((tty),XCASE)
+#define L_ECHO(tty) _L_FLAG((tty),ECHO)
+#define L_ECHOE(tty) _L_FLAG((tty),ECHOE)
+#define L_ECHOK(tty) _L_FLAG((tty),ECHOK)
+#define L_ECHONL(tty) _L_FLAG((tty),ECHONL)
+#define L_NOFLSH(tty) _L_FLAG((tty),NOFLSH)
+#define L_TOSTOP(tty) _L_FLAG((tty),TOSTOP)
+#define L_ECHOCTL(tty) _L_FLAG((tty),ECHOCTL)
+#define L_ECHOPRT(tty) _L_FLAG((tty),ECHOPRT)
+#define L_ECHOKE(tty) _L_FLAG((tty),ECHOKE)
+#define L_FLUSHO(tty) _L_FLAG((tty),FLUSHO)
+#define L_PENDIN(tty) _L_FLAG((tty),PENDIN)
+#define L_IEXTEN(tty) _L_FLAG((tty),IEXTEN)
+
+/*
+ * Where all of the state associated with a tty is kept while the tty
+ * is open. Since the termios state should be kept even if the tty
+ * has been closed --- for things like the baud rate, etc --- it is
+ * not stored here, but rather a pointer to the real state is stored
+ * here. Possible the winsize structure should have the same
+ * treatment, but (1) the default 80x24 is usually right and (2) it's
+ * most often used by a windowing system, which will set the correct
+ * size each time the window is created or resized anyway.
+ * IMPORTANT: since this structure is dynamically allocated, it must
+ * be no larger than 4096 bytes. Changing TTY_BUF_SIZE will change
+ * the size of this structure, and it needs to be done with care.
+ * - TYT, 9/14/92
+ */
+struct tty_struct {
+ int magic;
+ struct tty_driver driver;
+ struct tty_ldisc ldisc;
+ struct termios *termios, *termios_locked;
+ int pgrp;
+ int session;
+ kdev_t device;
+ unsigned long flags;
+ int count;
+ struct winsize winsize;
+ unsigned char stopped:1, hw_stopped:1, packet:1;
+ unsigned char ctrl_status;
+
+ struct tty_struct *link;
+ struct fasync_struct *fasync;
+ struct tty_flip_buffer flip;
+ int max_flip_cnt;
+ struct wait_queue *write_wait;
+ struct wait_queue *read_wait;
+ void *disc_data;
+ void *driver_data;
+
+#define N_TTY_BUF_SIZE 4096
+
+ /*
+ * The following is data for the N_TTY line discipline. For
+ * historical reasons, this is included in the tty structure.
+ */
+ unsigned int column;
+ unsigned char lnext:1, erasing:1, raw:1, real_raw:1, icanon:1;
+ unsigned char closing:1;
+ unsigned short minimum_to_wake;
+ unsigned overrun_time;
+ int num_overrun;
+ unsigned long process_char_map[256/(8*sizeof(unsigned long))];
+ char *read_buf;
+ int read_head;
+ int read_tail;
+ int read_cnt;
+ unsigned long read_flags[N_TTY_BUF_SIZE/(8*sizeof(unsigned long))];
+ int canon_data;
+ unsigned long canon_head;
+ unsigned int canon_column;
+};
+
+/* tty magic number */
+#define TTY_MAGIC 0x5401
+
+/*
+ * These bits are used in the flags field of the tty structure.
+ *
+ * So that interrupts won't be able to mess up the queues,
+ * copy_to_cooked must be atomic with respect to itself, as must
+ * tty->write. Thus, you must use the inline functions set_bit() and
+ * clear_bit() to make things atomic.
+ */
+#define TTY_THROTTLED 0
+#define TTY_IO_ERROR 1
+#define TTY_SLAVE_CLOSED 2
+#define TTY_EXCLUSIVE 3
+#define TTY_DEBUG 4
+#define TTY_DO_WRITE_WAKEUP 5
+#define TTY_PUSH 6
+#define TTY_CLOSING 7
+
+#define TTY_WRITE_FLUSH(tty) tty_write_flush((tty))
+
+extern void tty_write_flush(struct tty_struct *);
+
+extern struct termios tty_std_termios;
+extern struct tty_struct * redirect;
+extern struct tty_ldisc ldiscs[];
+extern int fg_console, last_console, want_console;
+
+extern int kmsg_redirect;
+extern struct wait_queue * keypress_wait;
+
+extern unsigned long con_init(unsigned long);
+
+extern int rs_init(void);
+extern int lp_init(void);
+extern int pty_init(void);
+extern int tty_init(void);
+extern int vcs_init(void);
+extern int cy_init(void);
+extern int stl_init(void);
+extern int stli_init(void);
+
+extern int tty_paranoia_check(struct tty_struct *tty, kdev_t device,
+ const char *routine);
+extern char *_tty_name(struct tty_struct *tty, char *buf);
+extern char *tty_name(struct tty_struct *tty);
+extern void tty_wait_until_sent(struct tty_struct * tty, int timeout);
+extern int tty_check_change(struct tty_struct * tty);
+extern void stop_tty(struct tty_struct * tty);
+extern void start_tty(struct tty_struct * tty);
+extern int tty_register_ldisc(int disc, struct tty_ldisc *new_ldisc);
+extern int tty_register_driver(struct tty_driver *driver);
+extern int tty_unregister_driver(struct tty_driver *driver);
+extern int tty_read_raw_data(struct tty_struct *tty, unsigned char *bufp,
+ int buflen);
+extern void tty_write_message(struct tty_struct *tty, char *msg);
+
+extern int is_orphaned_pgrp(int pgrp);
+extern int is_ignored(int sig);
+extern int tty_signal(int sig, struct tty_struct *tty);
+extern void tty_hangup(struct tty_struct * tty);
+extern void tty_vhangup(struct tty_struct * tty);
+extern void tty_unhangup(struct file *filp);
+extern int tty_hung_up_p(struct file * filp);
+extern void do_SAK(struct tty_struct *tty);
+extern void disassociate_ctty(int priv);
+
+/* n_tty.c */
+extern struct tty_ldisc tty_ldisc_N_TTY;
+
+/* tty_ioctl.c */
+extern int n_tty_ioctl(struct tty_struct * tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+
+/* serial.c */
+
+extern int rs_open(struct tty_struct * tty, struct file * filp);
+
+/* pty.c */
+
+extern int pty_open(struct tty_struct * tty, struct file * filp);
+
+/* console.c */
+
+extern int con_open(struct tty_struct * tty, struct file * filp);
+extern void update_screen(int new_console);
+extern void console_print(const char *);
+
+/* vt.c */
+
+extern int vt_ioctl(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+
+#endif /* __KERNEL__ */
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/tty_driver.h b/i386/i386at/gpl/linux/include/linux/tty_driver.h
new file mode 100644
index 00000000..3468fa2d
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/tty_driver.h
@@ -0,0 +1,189 @@
+#ifndef _LINUX_TTY_DRIVER_H
+#define _LINUX_TTY_DRIVER_H
+
+/*
+ * This structure defines the interface between the low-level tty
+ * driver and the tty routines. The following routines can be
+ * defined; unless noted otherwise, they are optional, and can be
+ * filled in with a null pointer.
+ *
+ * int (*open)(struct tty_struct * tty, struct file * filp);
+ *
+ * This routine is called when a particular tty device is opened.
+ * This routine is mandatory; if this routine is not filled in,
+ * the attempted open will fail with ENODEV.
+ *
+ * void (*close)(struct tty_struct * tty, struct file * filp);
+ *
+ * This routine is called when a particular tty device is closed.
+ *
+ * int (*write)(struct tty_struct * tty, int from_user,
+ * const unsigned char *buf, int count);
+ *
+ * This routine is called by the kernel to write a series of
+ * characters to the tty device. The characters may come from
+ * user space or kernel space. This routine will return the
+ * number of characters actually accepted for writing. This
+ * routine is mandatory.
+ *
+ * void (*put_char)(struct tty_struct *tty, unsigned char ch);
+ *
+ * This routine is called by the kernel to write a single
+ * character to the tty device. If the kernel uses this routine,
+ * it must call the flush_chars() routine (if defined) when it is
+ * done stuffing characters into the driver. If there is no room
+ * in the queue, the character is ignored.
+ *
+ * void (*flush_chars)(struct tty_struct *tty);
+ *
+ * This routine is called by the kernel after it has written a
+ * series of characters to the tty device using put_char().
+ *
+ * int (*write_room)(struct tty_struct *tty);
+ *
+ * This routine returns the numbers of characters the tty driver
+ * will accept for queuing to be written. This number is subject
+ * to change as output buffers get emptied, or if the output flow
+ * control is acted.
+ *
+ * int (*ioctl)(struct tty_struct *tty, struct file * file,
+ * unsigned int cmd, unsigned long arg);
+ *
+ * This routine allows the tty driver to implement
+ * device-specific ioctl's. If the ioctl number passed in cmd
+ * is not recognized by the driver, it should return ENOIOCTLCMD.
+ *
+ * void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ *
+ * This routine allows the tty driver to be notified when
+ * device's termios settings have changed. Note that a
+ * well-designed tty driver should be prepared to accept the case
+ * where old == NULL, and try to do something rational.
+ *
+ * void (*set_ldisc)(struct tty_struct *tty);
+ *
+ * This routine allows the tty driver to be notified when the
+ * device's termios settings have changed.
+ *
+ * void (*throttle)(struct tty_struct * tty);
+ *
+ * This routine notifies the tty driver that input buffers for
+ * the line discipline are close to full, and it should somehow
+ * signal that no more characters should be sent to the tty.
+ *
+ * void (*unthrottle)(struct tty_struct * tty);
+ *
+ * This routine notifies the tty drivers that it should signals
+ * that characters can now be sent to the tty without fear of
+ * overrunning the input buffers of the line disciplines.
+ *
+ * void (*stop)(struct tty_struct *tty);
+ *
+ * This routine notifies the tty driver that it should stop
+ * outputting characters to the tty device.
+ *
+ * void (*start)(struct tty_struct *tty);
+ *
+ * This routine notifies the tty driver that it resume sending
+ * characters to the tty device.
+ *
+ * void (*hangup)(struct tty_struct *tty);
+ *
+ * This routine notifies the tty driver that it should hangup the
+ * tty device.
+ *
+ */
+
+#include <linux/fs.h>
+
+struct tty_driver {
+ int magic; /* magic number for this structure */
+ const char *name;
+ int name_base; /* offset of printed name */
+ short major; /* major device number */
+ short minor_start; /* start of minor device number*/
+ short num; /* number of devices */
+ short type; /* type of tty driver */
+ short subtype; /* subtype of tty driver */
+ struct termios init_termios; /* Initial termios */
+ int flags; /* tty driver flags */
+ int *refcount; /* for loadable tty drivers */
+ struct tty_driver *other; /* only used for the PTY driver */
+
+ /*
+ * Pointer to the tty data structures
+ */
+ struct tty_struct **table;
+ struct termios **termios;
+ struct termios **termios_locked;
+
+ /*
+ * Interface routines from the upper tty layer to the tty
+ * driver.
+ */
+ int (*open)(struct tty_struct * tty, struct file * filp);
+ void (*close)(struct tty_struct * tty, struct file * filp);
+ int (*write)(struct tty_struct * tty, int from_user,
+ const unsigned char *buf, int count);
+ void (*put_char)(struct tty_struct *tty, unsigned char ch);
+ void (*flush_chars)(struct tty_struct *tty);
+ int (*write_room)(struct tty_struct *tty);
+ int (*chars_in_buffer)(struct tty_struct *tty);
+ int (*ioctl)(struct tty_struct *tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+ void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ void (*throttle)(struct tty_struct * tty);
+ void (*unthrottle)(struct tty_struct * tty);
+ void (*stop)(struct tty_struct *tty);
+ void (*start)(struct tty_struct *tty);
+ void (*hangup)(struct tty_struct *tty);
+ void (*flush_buffer)(struct tty_struct *tty);
+ void (*set_ldisc)(struct tty_struct *tty);
+
+ /*
+ * linked list pointers
+ */
+ struct tty_driver *next;
+ struct tty_driver *prev;
+};
+
+/* tty driver magic number */
+#define TTY_DRIVER_MAGIC 0x5402
+
+/*
+ * tty driver flags
+ *
+ * TTY_DRIVER_RESET_TERMIOS --- requests the tty layer to reset the
+ * termios setting when the last process has closed the device.
+ * Used for PTY's, in particular.
+ *
+ * TTY_DRIVER_REAL_RAW --- if set, indicates that the driver will
+ * guarantee never not to set any special character handling
+ * flags if ((IGNBRK || (!BRKINT && !PARMRK)) && (IGNPAR ||
+ * !INPCK)). That is, if there is no reason for the driver to
+ * send notifications of parity and break characters up to the
+ * line driver, it won't do so. This allows the line driver to
+ * optimize for this case if this flag is set. (Note that there
+ * is also a promise, if the above case is true, not to signal
+ * overruns, either.)
+ */
+#define TTY_DRIVER_INSTALLED 0x0001
+#define TTY_DRIVER_RESET_TERMIOS 0x0002
+#define TTY_DRIVER_REAL_RAW 0x0004
+
+/* tty driver types */
+#define TTY_DRIVER_TYPE_SYSTEM 0x0001
+#define TTY_DRIVER_TYPE_CONSOLE 0x0002
+#define TTY_DRIVER_TYPE_SERIAL 0x0003
+#define TTY_DRIVER_TYPE_PTY 0x0004
+#define TTY_DRIVER_TYPE_SCC 0x0005 /* scc driver */
+
+/* system subtypes (magic, used by tty_io.c) */
+#define SYSTEM_TYPE_TTY 0x0001
+#define SYSTEM_TYPE_CONSOLE 0x0002
+
+/* pty subtypes (magic, used by tty_io.c) */
+#define PTY_TYPE_MASTER 0x0001
+#define PTY_TYPE_SLAVE 0x0002
+
+#endif /* #ifdef _LINUX_TTY_DRIVER_H */
diff --git a/i386/i386at/gpl/linux/include/linux/tty_ldisc.h b/i386/i386at/gpl/linux/include/linux/tty_ldisc.h
new file mode 100644
index 00000000..87b54ca3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/tty_ldisc.h
@@ -0,0 +1,46 @@
+#ifndef _LINUX_TTY_LDISC_H
+#define _LINUX_TTY_LDISC_H
+
+/*
+ * Definitions for the tty line discipline
+ */
+
+#include <linux/fs.h>
+#include <linux/wait.h>
+
+struct tty_ldisc {
+ int magic;
+ int num;
+ int flags;
+ /*
+ * The following routines are called from above.
+ */
+ int (*open)(struct tty_struct *);
+ void (*close)(struct tty_struct *);
+ void (*flush_buffer)(struct tty_struct *tty);
+ int (*chars_in_buffer)(struct tty_struct *tty);
+ int (*read)(struct tty_struct * tty, struct file * file,
+ unsigned char * buf, unsigned int nr);
+ int (*write)(struct tty_struct * tty, struct file * file,
+ const unsigned char * buf, unsigned int nr);
+ int (*ioctl)(struct tty_struct * tty, struct file * file,
+ unsigned int cmd, unsigned long arg);
+ void (*set_termios)(struct tty_struct *tty, struct termios * old);
+ int (*select)(struct tty_struct * tty, struct inode * inode,
+ struct file * file, int sel_type,
+ struct select_table_struct *wait);
+
+ /*
+ * The following routines are called from below.
+ */
+ void (*receive_buf)(struct tty_struct *, const unsigned char *cp,
+ char *fp, int count);
+ int (*receive_room)(struct tty_struct *);
+ void (*write_wakeup)(struct tty_struct *);
+};
+
+#define TTY_LDISC_MAGIC 0x5403
+
+#define LDISC_FLAG_DEFINED 0x00000001
+
+#endif /* _LINUX_TTY_LDISC_H */
diff --git a/i386/i386at/gpl/linux/include/linux/types.h b/i386/i386at/gpl/linux/include/linux/types.h
new file mode 100644
index 00000000..376d3ac3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/types.h
@@ -0,0 +1,72 @@
+#ifndef _LINUX_TYPES_H
+#define _LINUX_TYPES_H
+
+/*
+ * This allows for 256 file descriptors: if NR_OPEN is ever grown beyond that
+ * you'll have to change this too. But 256 fd's seem to be enough even for such
+ * "real" unices like SunOS, so hopefully this is one limit that doesn't have
+ * to be changed.
+ *
+ * Note that POSIX wants the FD_CLEAR(fd,fdsetp) defines to be in <sys/time.h>
+ * (and thus <linux/time.h>) - but this is a more logical place for them. Solved
+ * by having dummy defines in <sys/time.h>.
+ */
+
+/*
+ * Those macros may have been defined in <gnu/types.h>. But we always
+ * use the ones here.
+ */
+#undef __NFDBITS
+#define __NFDBITS (8 * sizeof(unsigned int))
+
+#undef __FD_SETSIZE
+#define __FD_SETSIZE 256
+
+#undef __FDSET_INTS
+#define __FDSET_INTS (__FD_SETSIZE/__NFDBITS)
+
+typedef struct fd_set {
+ unsigned int fds_bits [__FDSET_INTS];
+} fd_set;
+
+#include <asm/types.h>
+
+#ifndef NULL
+#define NULL ((void *) 0)
+#endif
+
+#if defined(__GNUC__) && !defined(__STRICT_ANSI__)
+#define _LOFF_T
+typedef long long loff_t;
+#endif
+
+#ifndef MACH_INCLUDE
+/* bsd */
+typedef unsigned char u_char;
+typedef unsigned short u_short;
+typedef unsigned int u_int;
+typedef unsigned long u_long;
+#endif
+
+/* sysv */
+typedef unsigned char unchar;
+typedef unsigned short ushort;
+typedef unsigned int uint;
+typedef unsigned long ulong;
+
+#ifndef MACH_INCLUDE
+typedef char *caddr_t;
+#endif
+
+typedef unsigned char cc_t;
+typedef unsigned int speed_t;
+typedef unsigned int tcflag_t;
+
+struct ustat {
+ daddr_t f_tfree;
+ ino_t f_tinode;
+ char f_fname[6];
+ char f_fpack[6];
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/uio.h b/i386/i386at/gpl/linux/include/linux/uio.h
new file mode 100644
index 00000000..8051b3d0
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/uio.h
@@ -0,0 +1,25 @@
+#ifndef __LINUX_UIO_H
+#define __LINUX_UIO_H
+
+/*
+ * Berkeley style UIO structures - Alan Cox 1994.
+ *
+ * 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.
+ */
+
+
+/* A word of warning: Our uio structure will clash with the C library one (which is now obsolete). Remove the C
+ library one from sys/uio.h */
+
+struct iovec
+{
+ void *iov_base; /* BSD uses caddr_t (same thing in effect) */
+ int iov_len;
+};
+
+#define MAX_IOVEC 8 /* Maximum iovec's in one operation */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/unistd.h b/i386/i386at/gpl/linux/include/linux/unistd.h
new file mode 100644
index 00000000..10ed9834
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/unistd.h
@@ -0,0 +1,11 @@
+#ifndef _LINUX_UNISTD_H_
+#define _LINUX_UNISTD_H_
+
+extern int errno;
+
+/*
+ * Include machine specific syscallX macros
+ */
+#include <asm/unistd.h>
+
+#endif /* _LINUX_UNISTD_H_ */
diff --git a/i386/i386at/gpl/linux/include/linux/utsname.h b/i386/i386at/gpl/linux/include/linux/utsname.h
new file mode 100644
index 00000000..7aef28fc
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/utsname.h
@@ -0,0 +1,35 @@
+#ifndef _LINUX_UTSNAME_H
+#define _LINUX_UTSNAME_H
+
+#define __OLD_UTS_LEN 8
+
+struct oldold_utsname {
+ char sysname[9];
+ char nodename[9];
+ char release[9];
+ char version[9];
+ char machine[9];
+};
+
+#define __NEW_UTS_LEN 64
+
+struct old_utsname {
+ char sysname[65];
+ char nodename[65];
+ char release[65];
+ char version[65];
+ char machine[65];
+};
+
+struct new_utsname {
+ char sysname[65];
+ char nodename[65];
+ char release[65];
+ char version[65];
+ char machine[65];
+ char domainname[65];
+};
+
+extern struct new_utsname system_utsname;
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/version.h b/i386/i386at/gpl/linux/include/linux/version.h
new file mode 100644
index 00000000..39c1b599
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/version.h
@@ -0,0 +1,8 @@
+#define UTS_RELEASE "1.3.68"
+#define UTS_VERSION "#1 Thu Feb 29 16:37:10 MST 1996"
+#define LINUX_COMPILE_TIME "09:03:52"
+#define LINUX_COMPILE_BY "goel"
+#define LINUX_COMPILE_HOST "stamp.cs.utah.edu"
+#define LINUX_COMPILE_DOMAIN "cs.utah.edu"
+#define LINUX_COMPILER "gcc version 2.7.2"
+#define LINUX_VERSION_CODE (65536 + 4 * 256)
diff --git a/i386/i386at/gpl/linux/include/linux/vfs.h b/i386/i386at/gpl/linux/include/linux/vfs.h
new file mode 100644
index 00000000..b3a58657
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/vfs.h
@@ -0,0 +1,6 @@
+#ifndef _LINUX_VFS_H
+#define _LINUX_VFS_H
+
+#include <asm/statfs.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/vm86.h b/i386/i386at/gpl/linux/include/linux/vm86.h
new file mode 100644
index 00000000..ceb10358
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/vm86.h
@@ -0,0 +1,109 @@
+#ifndef _LINUX_VM86_H
+#define _LINUX_VM86_H
+
+/*
+ * I'm guessing at the VIF/VIP flag usage, but hope that this is how
+ * the Pentium uses them. Linux will return from vm86 mode when both
+ * VIF and VIP is set.
+ *
+ * On a Pentium, we could probably optimize the virtual flags directly
+ * in the eflags register instead of doing it "by hand" in vflags...
+ *
+ * Linus
+ */
+
+#define TF_MASK 0x00000100
+#define IF_MASK 0x00000200
+#define IOPL_MASK 0x00003000
+#define NT_MASK 0x00004000
+#define VM_MASK 0x00020000
+#define AC_MASK 0x00040000
+#define VIF_MASK 0x00080000 /* virtual interrupt flag */
+#define VIP_MASK 0x00100000 /* virtual interrupt pending */
+#define ID_MASK 0x00200000
+
+#define BIOSSEG 0x0f000
+
+#define CPU_086 0
+#define CPU_186 1
+#define CPU_286 2
+#define CPU_386 3
+#define CPU_486 4
+#define CPU_586 5
+
+/*
+ * Return values for the 'vm86()' system call
+ */
+#define VM86_TYPE(retval) ((retval) & 0xff)
+#define VM86_ARG(retval) ((retval) >> 8)
+
+#define VM86_SIGNAL 0 /* return due to signal */
+#define VM86_UNKNOWN 1 /* unhandled GP fault - IO-instruction or similar */
+#define VM86_INTx 2 /* int3/int x instruction (ARG = x) */
+#define VM86_STI 3 /* sti/popf/iret instruction enabled virtual interrupts */
+
+/*
+ * This is the stack-layout when we have done a "SAVE_ALL" from vm86
+ * mode - the main change is that the old segment descriptors aren't
+ * useful any more and are forced to be zero by the kernel (and the
+ * hardware when a trap occurs), and the real segment descriptors are
+ * at the end of the structure. Look at ptrace.h to see the "normal"
+ * setup.
+ */
+
+struct vm86_regs {
+/*
+ * normal regs, with special meaning for the segment descriptors..
+ */
+ long ebx;
+ long ecx;
+ long edx;
+ long esi;
+ long edi;
+ long ebp;
+ long eax;
+ long __null_ds;
+ long __null_es;
+ long __null_fs;
+ long __null_gs;
+ long orig_eax;
+ long eip;
+ unsigned short cs, __csh;
+ long eflags;
+ long esp;
+ unsigned short ss, __ssh;
+/*
+ * these are specific to v86 mode:
+ */
+ unsigned short es, __esh;
+ unsigned short ds, __dsh;
+ unsigned short fs, __fsh;
+ unsigned short gs, __gsh;
+};
+
+struct revectored_struct {
+ unsigned long __map[8]; /* 256 bits */
+};
+
+struct vm86_struct {
+ struct vm86_regs regs;
+ unsigned long flags;
+ unsigned long screen_bitmap;
+ unsigned long cpu_type;
+ struct revectored_struct int_revectored;
+ struct revectored_struct int21_revectored;
+};
+
+/*
+ * flags masks
+ */
+#define VM86_SCREEN_BITMAP 0x0001
+
+#ifdef __KERNEL__
+
+void handle_vm86_fault(struct vm86_regs *, long);
+void handle_vm86_debug(struct vm86_regs *, long);
+
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/linux/wait.h b/i386/i386at/gpl/linux/include/linux/wait.h
new file mode 100644
index 00000000..90ffe7b3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/linux/wait.h
@@ -0,0 +1,38 @@
+#ifndef _LINUX_WAIT_H
+#define _LINUX_WAIT_H
+
+#define WNOHANG 0x00000001
+#define WUNTRACED 0x00000002
+
+#define __WCLONE 0x80000000
+
+#ifdef __KERNEL__
+
+struct wait_queue {
+ struct task_struct * task;
+ struct wait_queue * next;
+};
+
+struct semaphore {
+ int count;
+ struct wait_queue * wait;
+};
+
+#define MUTEX ((struct semaphore) { 1, NULL })
+#define MUTEX_LOCKED ((struct semaphore) { 0, NULL })
+
+struct select_table_entry {
+ struct wait_queue wait;
+ struct wait_queue ** wait_address;
+};
+
+typedef struct select_table_struct {
+ int nr;
+ struct select_table_entry * entry;
+} select_table;
+
+#define __MAX_SELECT_TABLE_ENTRIES (4096 / sizeof (struct select_table_entry))
+
+#endif /* __KERNEL__ */
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/af_unix.h b/i386/i386at/gpl/linux/include/net/af_unix.h
new file mode 100644
index 00000000..dc4a48d6
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/af_unix.h
@@ -0,0 +1,4 @@
+extern void unix_proto_init(struct net_proto *pro);
+
+typedef struct sock unix_socket;
+
diff --git a/i386/i386at/gpl/linux/include/net/arp.h b/i386/i386at/gpl/linux/include/net/arp.h
new file mode 100644
index 00000000..db7a29c3
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/arp.h
@@ -0,0 +1,17 @@
+/* linux/net/inet/arp.h */
+#ifndef _ARP_H
+#define _ARP_H
+
+extern void arp_init(void);
+extern int arp_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern int arp_query(unsigned char *haddr, u32 paddr, struct device *dev);
+extern int arp_find(unsigned char *haddr, u32 paddr,
+ struct device *dev, u32 saddr, struct sk_buff *skb);
+extern int arp_ioctl(unsigned int cmd, void *arg);
+extern void arp_send(int type, int ptype, u32 dest_ip,
+ struct device *dev, u32 src_ip,
+ unsigned char *dest_hw, unsigned char *src_hw, unsigned char *th);
+extern int arp_bind_cache(struct hh_cache ** hhp, struct device *dev, unsigned short type, __u32 daddr);
+extern int arp_update_cache(struct hh_cache * hh);
+#endif /* _ARP_H */
diff --git a/i386/i386at/gpl/linux/include/net/atalkcall.h b/i386/i386at/gpl/linux/include/net/atalkcall.h
new file mode 100644
index 00000000..726e33cd
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/atalkcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void atalk_proto_init(struct net_proto *pro);
diff --git a/i386/i386at/gpl/linux/include/net/ax25.h b/i386/i386at/gpl/linux/include/net/ax25.h
new file mode 100644
index 00000000..45967cb1
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ax25.h
@@ -0,0 +1,246 @@
+/*
+ * Declarations of AX.25 type objects.
+ *
+ * Alan Cox (GW4PTS) 10/11/93
+ */
+
+#ifndef _AX25_H
+#define _AX25_H
+#include <linux/ax25.h>
+
+#define PR_SLOWHZ 10 /* Run timing at 1/10 second - gives us better resolution for 56kbit links */
+
+#define AX25_T1CLAMPLO (1 * PR_SLOWHZ) /* If defined, clamp at 1 second **/
+#define AX25_T1CLAMPHI (30 * PR_SLOWHZ) /* If defined, clamp at 30 seconds **/
+
+#define AX25_BROKEN_NETMAC
+
+#define AX25_BPQ_HEADER_LEN 16
+#define AX25_KISS_HEADER_LEN 1
+
+#define AX25_HEADER_LEN 17
+#define AX25_ADDR_LEN 7
+#define AX25_DIGI_HEADER_LEN (AX25_MAX_DIGIS * AX25_ADDR_LEN)
+#define AX25_MAX_HEADER_LEN (AX25_HEADER_LEN + AX25_DIGI_HEADER_LEN)
+
+#define AX25_P_IP 0xCC
+#define AX25_P_ARP 0xCD
+#define AX25_P_TEXT 0xF0
+#define AX25_P_NETROM 0xCF
+#define AX25_P_SEGMENT 0x08
+
+#define SEG_REM 0x7F
+#define SEG_FIRST 0x80
+
+#define LAPB_UI 0x03
+#define LAPB_C 0x80
+#define LAPB_E 0x01
+
+#define SSSID_SPARE 0x60 /* Unused bits in SSID for standard AX.25 */
+#define ESSID_SPARE 0x20 /* Unused bits in SSID for extended AX.25 */
+#define DAMA_FLAG 0x40 /* Well, it is *NOT* unused! (dl1bke 951121 */
+
+#define AX25_REPEATED 0x80
+
+#define ACK_PENDING_CONDITION 0x01
+#define REJECT_CONDITION 0x02
+#define PEER_RX_BUSY_CONDITION 0x04
+#define OWN_RX_BUSY_CONDITION 0x08
+
+#ifndef _LINUX_NETDEVICE_H
+#include <linux/netdevice.h>
+#endif
+
+/*
+ * These headers are taken from the KA9Q package by Phil Karn. These specific
+ * files have been placed under the GPL (not the whole package) by Phil.
+ *
+ *
+ * Copyright 1991 Phil Karn, KA9Q
+ *
+ * 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; version 2 dated June, 1991.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave., Cambridge, MA 02139, USA.
+ */
+
+/* Upper sub-layer (LAPB) definitions */
+
+/* Control field templates */
+#define I 0x00 /* Information frames */
+#define S 0x01 /* Supervisory frames */
+#define RR 0x01 /* Receiver ready */
+#define RNR 0x05 /* Receiver not ready */
+#define REJ 0x09 /* Reject */
+#define U 0x03 /* Unnumbered frames */
+#define SABM 0x2f /* Set Asynchronous Balanced Mode */
+#define SABME 0x6f /* Set Asynchronous Balanced Mode Extended */
+#define DISC 0x43 /* Disconnect */
+#define DM 0x0f /* Disconnected mode */
+#define UA 0x63 /* Unnumbered acknowledge */
+#define FRMR 0x87 /* Frame reject */
+#define UI 0x03 /* Unnumbered information */
+#define PF 0x10 /* Poll/final bit for standard AX.25 */
+#define EPF 0x01 /* Poll/final bit for extended AX.25 */
+
+#define ILLEGAL 0x100 /* Impossible to be a real frame type */
+
+#define POLLOFF 0
+#define POLLON 1
+
+/* AX25 L2 C-bit */
+
+#define C_COMMAND 1 /* C_ otherwise it clashes with the de600 defines (sigh)) */
+#define C_RESPONSE 2
+
+/* Define Link State constants. */
+
+#define AX25_STATE_0 0
+#define AX25_STATE_1 1
+#define AX25_STATE_2 2
+#define AX25_STATE_3 3
+#define AX25_STATE_4 4
+
+#define MODULUS 8 /* Standard AX.25 modulus */
+#define EMODULUS 128 /* Extended AX.25 modulus */
+
+#define AX25_DEF_IPDEFMODE 'D'
+#define AX25_DEF_AXDEFMODE 8
+#define AX25_DEF_NETROM 1
+#define AX25_DEF_TEXT 1
+#define AX25_DEF_BACKOFF 'E'
+#define AX25_DEF_CONMODE 1
+#define AX25_DEF_WINDOW 2
+#define AX25_DEF_EWINDOW 32
+#define AX25_DEF_T1 10
+#define AX25_DEF_T2 3
+#define AX25_DEF_T3 300
+#define AX25_DEF_N2 10
+#define AX25_DEF_DIGI (AX25_DIGI_INBAND|AX25_DIGI_XBAND)
+
+typedef struct ax25_uid_assoc {
+ struct ax25_uid_assoc *next;
+ uid_t uid;
+ ax25_address call;
+} ax25_uid_assoc;
+
+typedef struct {
+ ax25_address calls[AX25_MAX_DIGIS];
+ unsigned char repeated[AX25_MAX_DIGIS];
+ unsigned char ndigi;
+ char lastrepeat;
+} ax25_digi;
+
+typedef struct ax25_cb {
+ struct ax25_cb *next;
+ ax25_address source_addr, dest_addr;
+ struct device *device;
+ unsigned char dama_slave; /* dl1bke 951121 */
+ unsigned char state, modulus, hdrincl;
+ unsigned short vs, vr, va;
+ unsigned char condition, backoff;
+ unsigned char n2, n2count;
+ unsigned short t1, t2, t3, rtt;
+ unsigned short t1timer, t2timer, t3timer;
+ unsigned short fragno, fraglen;
+ ax25_digi *digipeat;
+ struct sk_buff_head write_queue;
+ struct sk_buff_head reseq_queue;
+ struct sk_buff_head ack_queue;
+ struct sk_buff_head frag_queue;
+ unsigned char window;
+ struct timer_list timer;
+ struct sock *sk; /* Backlink to socket */
+} ax25_cb;
+
+/* af_ax25.c */
+extern ax25_address null_ax25_address;
+extern char *ax2asc(ax25_address *);
+extern int ax25cmp(ax25_address *, ax25_address *);
+extern int ax25_send_frame(struct sk_buff *, ax25_address *, ax25_address *, ax25_digi *, struct device *);
+extern void ax25_destroy_socket(ax25_cb *);
+extern struct device *ax25rtr_get_dev(ax25_address *);
+extern int ax25_encapsulate(struct sk_buff *, struct device *, unsigned short,
+ void *, void *, unsigned int);
+extern int ax25_rebuild_header(unsigned char *, struct device *, unsigned long, struct sk_buff *);
+extern ax25_uid_assoc *ax25_uid_list;
+extern int ax25_uid_policy;
+extern ax25_address *ax25_findbyuid(uid_t);
+extern void ax25_queue_xmit(struct sk_buff *, struct device *, int);
+extern int ax25_dev_is_dama_slave(struct device *); /* dl1bke 951121 */
+
+#include <net/ax25call.h>
+
+/* ax25_in.c */
+extern int ax25_process_rx_frame(ax25_cb *, struct sk_buff *, int, int);
+
+/* ax25_out.c */
+extern void ax25_output(ax25_cb *, struct sk_buff *);
+extern void ax25_kick(ax25_cb *);
+extern void ax25_transmit_buffer(ax25_cb *, struct sk_buff *, int);
+extern void ax25_nr_error_recovery(ax25_cb *);
+extern void ax25_establish_data_link(ax25_cb *);
+extern void ax25_transmit_enquiry(ax25_cb *);
+extern void ax25_enquiry_response(ax25_cb *);
+extern void ax25_timeout_response(ax25_cb *);
+extern void ax25_check_iframes_acked(ax25_cb *, unsigned short);
+extern void ax25_check_need_response(ax25_cb *, int, int);
+extern void dama_enquiry_response(ax25_cb *); /* dl1bke 960114 */
+extern void dama_check_need_response(ax25_cb *, int, int); /* dl1bke 960114 */
+extern void dama_establish_data_link(ax25_cb *);
+
+/* ax25_route.c */
+extern void ax25_rt_rx_frame(ax25_address *, struct device *, ax25_digi *);
+extern int ax25_rt_get_info(char *, char **, off_t, int, int);
+extern int ax25_cs_get_info(char *, char **, off_t, int, int);
+extern int ax25_rt_autobind(ax25_cb *, ax25_address *);
+extern void ax25_rt_build_path(ax25_cb *, ax25_address *);
+extern void ax25_dg_build_path(struct sk_buff *, ax25_address *, struct device *);
+extern void ax25_rt_device_down(struct device *);
+extern int ax25_rt_ioctl(unsigned int, void *);
+extern void ax25_ip_mode_set(ax25_address *, struct device *, char);
+extern char ax25_ip_mode_get(ax25_address *, struct device *);
+extern unsigned short ax25_dev_get_value(struct device *, int);
+extern void ax25_dev_device_up(struct device *);
+extern void ax25_dev_device_down(struct device *);
+extern int ax25_dev_ioctl(unsigned int, void *);
+extern int ax25_bpq_get_info(char *, char **, off_t, int, int);
+extern ax25_address *ax25_bpq_get_addr(struct device *);
+extern int ax25_bpq_ioctl(unsigned int, void *);
+
+/* ax25_subr.c */
+extern void ax25_clear_queues(ax25_cb *);
+extern void ax25_frames_acked(ax25_cb *, unsigned short);
+extern void ax25_requeue_frames(ax25_cb *);
+extern int ax25_validate_nr(ax25_cb *, unsigned short);
+extern int ax25_decode(ax25_cb *, struct sk_buff *, int *, int *, int *);
+extern void ax25_send_control(ax25_cb *, int, int, int);
+extern unsigned short ax25_calculate_t1(ax25_cb *);
+extern void ax25_calculate_rtt(ax25_cb *);
+extern unsigned char *ax25_parse_addr(unsigned char *, int, ax25_address *,
+ ax25_address *, ax25_digi *, int *, int *); /* dl1bke 951121 */
+extern int build_ax25_addr(unsigned char *, ax25_address *, ax25_address *,
+ ax25_digi *, int, int);
+extern int size_ax25_addr(ax25_digi *);
+extern void ax25_digi_invert(ax25_digi *, ax25_digi *);
+extern void ax25_return_dm(struct device *, ax25_address *, ax25_address *, ax25_digi *);
+extern void ax25_dama_on(ax25_cb *); /* dl1bke 951121 */
+extern void ax25_dama_off(ax25_cb *); /* dl1bke 951121 */
+
+/* ax25_timer */
+extern void ax25_set_timer(ax25_cb *);
+extern void ax25_t1_timeout(ax25_cb *);
+
+/* ... */
+
+extern ax25_cb * volatile ax25_list;
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/ax25call.h b/i386/i386at/gpl/linux/include/net/ax25call.h
new file mode 100644
index 00000000..89569656
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ax25call.h
@@ -0,0 +1,2 @@
+/* Seperate to keep compilation of protocols.c simpler */
+extern void ax25_proto_init(struct net_proto *pro);
diff --git a/i386/i386at/gpl/linux/include/net/checksum.h b/i386/i386at/gpl/linux/include/net/checksum.h
new file mode 100644
index 00000000..aee4fd47
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/checksum.h
@@ -0,0 +1,25 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Checksumming functions for IP, TCP, UDP and so on
+ *
+ * Authors: Jorge Cwik, <jorge@laser.satlink.net>
+ * Arnt Gulbrandsen, <agulbra@nvg.unit.no>
+ * Borrows very liberally from tcp.c and ip.c, see those
+ * files for more names.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _CHECKSUM_H
+#define _CHECKSUM_H
+
+#include <asm/byteorder.h>
+#include <net/ip.h>
+#include <asm/checksum.h>
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/datalink.h b/i386/i386at/gpl/linux/include/net/datalink.h
new file mode 100644
index 00000000..44e56990
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/datalink.h
@@ -0,0 +1,16 @@
+#ifndef _NET_INET_DATALINK_H_
+#define _NET_INET_DATALINK_H_
+
+struct datalink_proto {
+ unsigned short type_len;
+ unsigned char type[8];
+ const char *string_name;
+ unsigned short header_length;
+ int (*rcvfunc)(struct sk_buff *, struct device *,
+ struct packet_type *);
+ void (*datalink_header)(struct datalink_proto *, struct sk_buff *,
+ unsigned char *);
+ struct datalink_proto *next;
+};
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/icmp.h b/i386/i386at/gpl/linux/include/net/icmp.h
new file mode 100644
index 00000000..e4ae8213
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/icmp.h
@@ -0,0 +1,40 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the ICMP module.
+ *
+ * Version: @(#)icmp.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ICMP_H
+#define _ICMP_H
+
+#include <linux/icmp.h>
+#include <linux/skbuff.h>
+
+#include <net/sock.h>
+#include <net/protocol.h>
+
+extern struct icmp_err icmp_err_convert[];
+extern struct icmp_mib icmp_statistics;
+
+extern void icmp_send(struct sk_buff *skb_in, int type, int code,
+ unsigned long info, struct device *dev);
+extern int icmp_rcv(struct sk_buff *skb1, struct device *dev,
+ struct options *opt, __u32 daddr,
+ unsigned short len, __u32 saddr,
+ int redo, struct inet_protocol *protocol);
+extern int icmp_ioctl(struct sock *sk, int cmd,
+ unsigned long arg);
+extern void icmp_init(struct proto_ops *ops);
+
+#endif /* _ICMP_H */
diff --git a/i386/i386at/gpl/linux/include/net/ip.h b/i386/i386at/gpl/linux/include/net/ip.h
new file mode 100644
index 00000000..c7bd9987
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ip.h
@@ -0,0 +1,154 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP module.
+ *
+ * Version: @(#)ip.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _IP_H
+#define _IP_H
+
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/socket.h>
+#include <linux/ip.h>
+#include <linux/netdevice.h>
+#include <net/route.h>
+
+#ifndef _SNMP_H
+#include <net/snmp.h>
+#endif
+
+#include <net/sock.h> /* struct sock */
+
+/* IP flags. */
+#define IP_CE 0x8000 /* Flag: "Congestion" */
+#define IP_DF 0x4000 /* Flag: "Don't Fragment" */
+#define IP_MF 0x2000 /* Flag: "More Fragments" */
+#define IP_OFFSET 0x1FFF /* "Fragment Offset" part */
+
+#define IP_FRAG_TIME (30 * HZ) /* fragment lifetime */
+
+#ifdef CONFIG_IP_MULTICAST
+extern void ip_mc_dropsocket(struct sock *);
+extern void ip_mc_dropdevice(struct device *dev);
+extern int ip_mc_procinfo(char *, char **, off_t, int, int);
+#endif
+
+#include <net/ip_forward.h>
+
+/* Describe an IP fragment. */
+struct ipfrag
+{
+ int offset; /* offset of fragment in IP datagram */
+ int end; /* last byte of data in datagram */
+ int len; /* length of this fragment */
+ struct sk_buff *skb; /* complete received fragment */
+ unsigned char *ptr; /* pointer into real fragment data */
+ struct ipfrag *next; /* linked list pointers */
+ struct ipfrag *prev;
+};
+
+/*
+ * Describe an entry in the "incomplete datagrams" queue.
+ */
+
+struct ipq
+{
+ unsigned char *mac; /* pointer to MAC header */
+ struct iphdr *iph; /* pointer to IP header */
+ int len; /* total length of original datagram */
+ short ihlen; /* length of the IP header */
+ short maclen; /* length of the MAC header */
+ struct timer_list timer; /* when will this queue expire? */
+ struct ipfrag *fragments; /* linked list of received fragments */
+ struct ipq *next; /* linked list pointers */
+ struct ipq *prev;
+ struct device *dev; /* Device - for icmp replies */
+};
+
+/*
+ * Functions provided by ip.c
+ */
+
+extern void ip_print(const struct iphdr *ip);
+extern int ip_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void ip_route_check(__u32 daddr);
+extern int ip_send(struct rtable *rt, struct sk_buff *skb, __u32 daddr, int len, struct device *dev, __u32 saddr);
+extern int ip_build_header(struct sk_buff *skb,
+ __u32 saddr,
+ __u32 daddr,
+ struct device **dev, int type,
+ struct options *opt, int len,
+ int tos,int ttl,struct rtable **rp);
+extern int ip_rcv(struct sk_buff *skb, struct device *dev,
+ struct packet_type *pt);
+extern int ip_options_echo(struct options * dopt, struct options * sopt,
+ __u32 daddr, __u32 saddr,
+ struct sk_buff * skb);
+extern int ip_options_compile(struct options * opt, struct sk_buff * skb);
+extern void ip_send_check(struct iphdr *ip);
+extern int ip_id_count;
+extern void ip_queue_xmit(struct sock *sk,
+ struct device *dev, struct sk_buff *skb,
+ int free);
+extern void ip_init(void);
+extern int ip_build_xmit(struct sock *sk,
+ void getfrag (const void *,
+ __u32,
+ char *,
+ unsigned int,
+ unsigned int),
+ const void *frag,
+ unsigned short int length,
+ __u32 daddr,
+ __u32 saddr,
+ struct options * opt,
+ int flags,
+ int type,
+ int noblock);
+
+extern struct ip_mib ip_statistics;
+
+/*
+ * Functions provided by ip_fragment.o
+ */
+
+struct sk_buff *ip_defrag(struct iphdr *iph, struct sk_buff *skb, struct device *dev);
+void ip_fragment(struct sock *sk, struct sk_buff *skb, struct device *dev, int is_frag);
+
+/*
+ * Functions provided by ip_forward.c
+ */
+
+extern int ip_forward(struct sk_buff *skb, struct device *dev, int is_frag, __u32 target_addr);
+
+/*
+ * Functions provided by ip_options.c
+ */
+
+extern void ip_options_build(struct sk_buff *skb, struct options *opt, __u32 daddr, __u32 saddr, int is_frag);
+extern int ip_options_echo(struct options *dopt, struct options *sopt, __u32 daddr, __u32 saddr, struct sk_buff *skb);
+extern void ip_options_fragment(struct sk_buff *skb);
+extern int ip_options_compile(struct options *opt, struct sk_buff *skb);
+
+/*
+ * Functions provided by ip_sockglue.c
+ */
+
+extern int ip_setsockopt(struct sock *sk, int level, int optname, char *optval, int optlen);
+extern int ip_getsockopt(struct sock *sk, int level, int optname, char *optval, int *optlen);
+
+#endif /* _IP_H */
diff --git a/i386/i386at/gpl/linux/include/net/ip_alias.h b/i386/i386at/gpl/linux/include/net/ip_alias.h
new file mode 100644
index 00000000..683a0427
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ip_alias.h
@@ -0,0 +1,23 @@
+/*
+ * IP_ALIAS (AF_INET) aliasing definitions.
+ *
+ *
+ * Version: @(#)ip_alias.h 0.43 12/20/95
+ *
+ * Author: Juan Jose Ciarlante, <jjciarla@raiz.uncu.edu.ar>
+ *
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _IP_ALIAS_H
+#define _IP_ALIAS_H
+
+extern int ip_alias_init(void);
+extern int ip_alias_done(void);
+
+#endif /* _IP_ALIAS_H */
diff --git a/i386/i386at/gpl/linux/include/net/ip_forward.h b/i386/i386at/gpl/linux/include/net/ip_forward.h
new file mode 100644
index 00000000..b8596500
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ip_forward.h
@@ -0,0 +1,10 @@
+#ifndef __NET_IP_FORWARD_H
+#define __NET_IP_FORWARD_H
+
+#define IPFWD_FRAGMENT 1
+#define IPFWD_LASTFRAG 2
+#define IPFWD_MASQUERADED 4
+#define IPFWD_MULTICASTING 8
+#define IPFWD_MULTITUNNEL 16
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/ipip.h b/i386/i386at/gpl/linux/include/net/ipip.h
new file mode 100644
index 00000000..bba1492e
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ipip.h
@@ -0,0 +1,4 @@
+extern int ipip_rcv(struct sk_buff *skb, struct device *dev, struct options *opt,
+ __u32 daddr, unsigned short len, __u32 saddr,
+ int redo, struct inet_protocol *protocol);
+
diff --git a/i386/i386at/gpl/linux/include/net/ipx.h b/i386/i386at/gpl/linux/include/net/ipx.h
new file mode 100644
index 00000000..96c62405
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ipx.h
@@ -0,0 +1,85 @@
+
+/*
+ * The following information is in its entirety obtained from:
+ *
+ * Novell 'IPX Router Specification' Version 1.10
+ * Part No. 107-000029-001
+ *
+ * Which is available from ftp.novell.com
+ */
+
+#ifndef _NET_INET_IPX_H_
+#define _NET_INET_IPX_H_
+
+#include <linux/skbuff.h>
+#include <net/datalink.h>
+#include <linux/ipx.h>
+
+typedef struct
+{
+ unsigned long net;
+ unsigned char node[IPX_NODE_LEN];
+ unsigned short sock;
+} ipx_address;
+
+#define ipx_broadcast_node "\377\377\377\377\377\377"
+#define ipx_this_node "\0\0\0\0\0\0"
+
+typedef struct ipx_packet
+{
+ unsigned short ipx_checksum;
+#define IPX_NO_CHECKSUM 0xFFFF
+ unsigned short ipx_pktsize;
+ unsigned char ipx_tctrl;
+ unsigned char ipx_type;
+#define IPX_TYPE_UNKNOWN 0x00
+#define IPX_TYPE_RIP 0x01 /* may also be 0 */
+#define IPX_TYPE_SAP 0x04 /* may also be 0 */
+#define IPX_TYPE_SPX 0x05 /* Not yet implemented */
+#define IPX_TYPE_NCP 0x11 /* $lots for docs on this (SPIT) */
+#define IPX_TYPE_PPROP 0x14 /* complicated flood fill brdcast [Not supported] */
+ ipx_address ipx_dest __attribute__ ((packed));
+ ipx_address ipx_source __attribute__ ((packed));
+} ipx_packet;
+
+
+typedef struct sock ipx_socket;
+
+#include <net/ipxcall.h>
+extern int ipx_rcv(struct sk_buff *skb, struct device *dev, struct packet_type *pt);
+extern void ipxrtr_device_down(struct device *dev);
+
+typedef struct ipx_interface {
+ /* IPX address */
+ unsigned long if_netnum;
+ unsigned char if_node[IPX_NODE_LEN];
+
+ /* physical device info */
+ struct device *if_dev;
+ struct datalink_proto *if_dlink;
+ unsigned short if_dlink_type;
+
+ /* socket support */
+ unsigned short if_sknum;
+ ipx_socket *if_sklist;
+
+ /* administrative overhead */
+ int if_ipx_offset;
+ unsigned char if_internal;
+ unsigned char if_primary;
+
+ struct ipx_interface *if_next;
+} ipx_interface;
+
+typedef struct ipx_route {
+ unsigned long ir_net;
+ ipx_interface *ir_intrfc;
+ unsigned char ir_routed;
+ unsigned char ir_router_node[IPX_NODE_LEN];
+ struct ipx_route *ir_next;
+} ipx_route;
+
+#define IPX_MIN_EPHEMERAL_SOCKET 0x4000
+#define IPX_MAX_EPHEMERAL_SOCKET 0x7fff
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/ipxcall.h b/i386/i386at/gpl/linux/include/net/ipxcall.h
new file mode 100644
index 00000000..eb5bd2bd
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/ipxcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of protocols.c simpler */
+extern void ipx_proto_init(struct net_proto *pro);
diff --git a/i386/i386at/gpl/linux/include/net/netlink.h b/i386/i386at/gpl/linux/include/net/netlink.h
new file mode 100644
index 00000000..e32af15b
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/netlink.h
@@ -0,0 +1,26 @@
+#ifndef __NET_NETLINK_H
+#define __NET_NETLINK_H
+
+#define NET_MAJOR 36 /* Major 18 is reserved for networking */
+#define MAX_LINKS 4 /* 18,0 for route updates, 18,1 for SKIP, 18,2 debug tap 18,3 PPP reserved */
+#define MAX_QBYTES 32768 /* Maximum bytes in the queue */
+
+#include <linux/config.h>
+
+extern int netlink_attach(int unit, int (*function)(struct sk_buff *skb));
+extern int netlink_donothing(struct sk_buff *skb);
+extern void netlink_detach(int unit);
+extern int netlink_post(int unit, struct sk_buff *skb);
+extern int init_netlink(void);
+
+#define NETLINK_ROUTE 0 /* Routing/device hook */
+#define NETLINK_SKIP 1 /* Reserved for ENskip */
+#define NETLINK_USERSOCK 2 /* Reserved for user mode socket protocols */
+#define NETLINK_FIREWALL 3 /* Firewalling hook */
+
+#ifdef CONFIG_RTNETLINK
+extern void ip_netlink_msg(unsigned long, __u32, __u32, __u32, short, short, char *);
+#else
+#define ip_netlink_msg(a,b,c,d,e,f,g)
+#endif
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/netrom.h b/i386/i386at/gpl/linux/include/net/netrom.h
new file mode 100644
index 00000000..5e343bbc
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/netrom.h
@@ -0,0 +1,139 @@
+/*
+ * Declarations of NET/ROM type objects.
+ *
+ * Jonathan Naylor G4KLX 9/4/95
+ */
+
+#ifndef _NETROM_H
+#define _NETROM_H
+#include <linux/netrom.h>
+
+#define NR_T1CLAMPLO (1 * PR_SLOWHZ) /* If defined, clamp at 1 second **/
+#define NR_T1CLAMPHI (300 * PR_SLOWHZ) /* If defined, clamp at 30 seconds **/
+
+#define NR_NETWORK_LEN 15
+#define NR_TRANSPORT_LEN 5
+
+#define NR_PROTO_IP 0x0C
+
+#define NR_PROTOEXT 0x00
+#define NR_CONNREQ 0x01
+#define NR_CONNACK 0x02
+#define NR_DISCREQ 0x03
+#define NR_DISCACK 0x04
+#define NR_INFO 0x05
+#define NR_INFOACK 0x06
+
+#define NR_CHOKE_FLAG 0x80
+#define NR_NAK_FLAG 0x40
+#define NR_MORE_FLAG 0x20
+
+/* Define Link State constants. */
+
+#define NR_STATE_0 0
+#define NR_STATE_1 1
+#define NR_STATE_2 2
+#define NR_STATE_3 3
+
+#define NR_DEFAULT_T1 (120 * PR_SLOWHZ) /* Outstanding frames - 120 seconds */
+#define NR_DEFAULT_T2 (5 * PR_SLOWHZ) /* Response delay - 5 seconds */
+#define NR_DEFAULT_N2 3 /* Number of Retries */
+#define NR_DEFAULT_T4 (180 * PR_SLOWHZ) /* Transport Busy Delay */
+#define NR_DEFAULT_WINDOW 4 /* Default Window Size */
+#define NR_DEFAULT_OBS 6 /* Default Obscolesence Count */
+#define NR_DEFAULT_QUAL 10 /* Default Neighbour Quality */
+#define NR_DEFAULT_TTL 16 /* Default Time To Live */
+#define NR_MODULUS 256
+#define NR_MAX_WINDOW_SIZE 127 /* Maximum Window Allowable */
+
+typedef struct {
+ ax25_address user_addr, source_addr, dest_addr;
+ struct device *device;
+ unsigned char my_index, my_id;
+ unsigned char your_index, your_id;
+ unsigned char state, condition, bpqext, hdrincl;
+ unsigned short vs, vr, va, vl;
+ unsigned char n2, n2count;
+ unsigned short t1, t2, rtt;
+ unsigned short t1timer, t2timer, t4timer;
+ unsigned short fraglen;
+ struct sk_buff_head ack_queue;
+ struct sk_buff_head reseq_queue;
+ struct sk_buff_head frag_queue;
+ struct sock *sk; /* Backlink to socket */
+} nr_cb;
+
+struct nr_route {
+ unsigned char quality;
+ unsigned char obs_count;
+ unsigned short neighbour;
+};
+
+struct nr_node {
+ struct nr_node *next;
+ ax25_address callsign;
+ char mnemonic[7];
+ unsigned char which;
+ unsigned char count;
+ struct nr_route routes[3];
+};
+
+struct nr_neigh {
+ struct nr_neigh *next;
+ ax25_address callsign;
+ ax25_digi *digipeat;
+ struct device *dev;
+ unsigned char quality;
+ unsigned char locked;
+ unsigned short count;
+ unsigned short number;
+};
+
+/* af_netrom.c */
+extern struct nr_parms_struct nr_default;
+extern int nr_rx_frame(struct sk_buff *, struct device *);
+extern void nr_destroy_socket(struct sock *);
+
+/* nr_dev.c */
+extern int nr_rx_ip(struct sk_buff *, struct device *);
+extern int nr_init(struct device *);
+
+#include <net/nrcall.h>
+
+/* nr_in.c */
+extern int nr_process_rx_frame(struct sock *, struct sk_buff *);
+
+/* nr_out.c */
+extern void nr_output(struct sock *, struct sk_buff *);
+extern void nr_send_nak_frame(struct sock *);
+extern void nr_kick(struct sock *);
+extern void nr_transmit_buffer(struct sock *, struct sk_buff *);
+extern void nr_establish_data_link(struct sock *);
+extern void nr_enquiry_response(struct sock *);
+extern void nr_check_iframes_acked(struct sock *, unsigned short);
+
+/* nr_route.c */
+extern void nr_rt_device_down(struct device *);
+extern struct device *nr_dev_first(void);
+extern struct device *nr_dev_get(ax25_address *);
+extern int nr_rt_ioctl(unsigned int, void *);
+extern void nr_link_failed(ax25_address *, struct device *);
+extern int nr_route_frame(struct sk_buff *, ax25_cb *);
+extern int nr_nodes_get_info(char *, char **, off_t, int, int);
+extern int nr_neigh_get_info(char *, char **, off_t, int, int);
+
+/* nr_subr.c */
+extern void nr_clear_queues(struct sock *);
+extern void nr_frames_acked(struct sock *, unsigned short);
+extern void nr_requeue_frames(struct sock *);
+extern int nr_validate_nr(struct sock *, unsigned short);
+extern int nr_in_rx_window(struct sock *, unsigned short);
+extern void nr_write_internal(struct sock *, int);
+extern void nr_transmit_dm(struct sk_buff *);
+extern unsigned short nr_calculate_t1(struct sock *);
+extern void nr_calculate_rtt(struct sock *);
+
+/* ax25_timer */
+extern void nr_set_timer(struct sock *);
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/nrcall.h b/i386/i386at/gpl/linux/include/net/nrcall.h
new file mode 100644
index 00000000..f58c2d4f
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/nrcall.h
@@ -0,0 +1,2 @@
+/* Seperate to keep compilation of protocols.c simpler */
+extern void nr_proto_init(struct net_proto *pro);
diff --git a/i386/i386at/gpl/linux/include/net/p8022.h b/i386/i386at/gpl/linux/include/net/p8022.h
new file mode 100644
index 00000000..52c676be
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/p8022.h
@@ -0,0 +1,2 @@
+struct datalink_proto *register_8022_client(unsigned char type, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+
diff --git a/i386/i386at/gpl/linux/include/net/p8022call.h b/i386/i386at/gpl/linux/include/net/p8022call.h
new file mode 100644
index 00000000..14f0c2ce
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/p8022call.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void p8022_proto_init(struct net_proto *);
diff --git a/i386/i386at/gpl/linux/include/net/protocol.h b/i386/i386at/gpl/linux/include/net/protocol.h
new file mode 100644
index 00000000..ae328b69
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/protocol.h
@@ -0,0 +1,55 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the protocol dispatcher.
+ *
+ * Version: @(#)protocol.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * 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.
+ *
+ * Changes:
+ * Alan Cox : Added a name field and a frag handler
+ * field for later.
+ * Alan Cox : Cleaned up, and sorted types.
+ */
+
+#ifndef _PROTOCOL_H
+#define _PROTOCOL_H
+
+#define MAX_INET_PROTOS 32 /* Must be a power of 2 */
+
+
+/* This is used to register protocols. */
+struct inet_protocol {
+ int (*handler)(struct sk_buff *skb, struct device *dev,
+ struct options *opt, __u32 daddr,
+ unsigned short len, __u32 saddr,
+ int redo, struct inet_protocol *protocol);
+ void (*err_handler)(int type, int code, unsigned char *buff,
+ __u32 daddr,
+ __u32 saddr,
+ struct inet_protocol *protocol);
+ struct inet_protocol *next;
+ unsigned char protocol;
+ unsigned char copy:1;
+ void *data;
+ const char *name;
+};
+
+
+extern struct inet_protocol *inet_protocol_base;
+extern struct inet_protocol *inet_protos[MAX_INET_PROTOS];
+
+
+extern void inet_add_protocol(struct inet_protocol *prot);
+extern int inet_del_protocol(struct inet_protocol *prot);
+
+
+#endif /* _PROTOCOL_H */
diff --git a/i386/i386at/gpl/linux/include/net/psnap.h b/i386/i386at/gpl/linux/include/net/psnap.h
new file mode 100644
index 00000000..b69859db
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/psnap.h
@@ -0,0 +1,2 @@
+struct datalink_proto *register_snap_client(unsigned char *desc, int (*rcvfunc)(struct sk_buff *, struct device *, struct packet_type *));
+
diff --git a/i386/i386at/gpl/linux/include/net/psnapcall.h b/i386/i386at/gpl/linux/include/net/psnapcall.h
new file mode 100644
index 00000000..9da5763c
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/psnapcall.h
@@ -0,0 +1,2 @@
+/* Separate to keep compilation of Space.c simpler */
+extern void snap_proto_init(struct net_proto *);
diff --git a/i386/i386at/gpl/linux/include/net/rarp.h b/i386/i386at/gpl/linux/include/net/rarp.h
new file mode 100644
index 00000000..7bfb08ef
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/rarp.h
@@ -0,0 +1,12 @@
+/* linux/net/inet/rarp.h */
+#ifndef _RARP_H
+#define _RARP_H
+
+extern int rarp_ioctl(unsigned int cmd, void *arg);
+extern int rarp_get_info(char *buffer,
+ char **start,
+ off_t offset,
+ int length,
+ int dummy);
+#endif /* _RARP_H */
+
diff --git a/i386/i386at/gpl/linux/include/net/raw.h b/i386/i386at/gpl/linux/include/net/raw.h
new file mode 100644
index 00000000..4b424879
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/raw.h
@@ -0,0 +1,34 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the RAW-IP module.
+ *
+ * Version: @(#)raw.h 1.0.2 05/07/93
+ *
+ * Author: Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _RAW_H
+#define _RAW_H
+
+
+extern struct proto raw_prot;
+
+
+extern void raw_err(int type, int code, unsigned char *header, __u32 daddr,
+ __u32 saddr, struct inet_protocol *protocol);
+extern int raw_recvfrom(struct sock *sk, unsigned char *to,
+ int len, int noblock, unsigned flags,
+ struct sockaddr_in *sin, int *addr_len);
+extern int raw_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags);
+extern int raw_rcv(struct sock *, struct sk_buff *, struct device *,
+ __u32, __u32);
+
+#endif /* _RAW_H */
diff --git a/i386/i386at/gpl/linux/include/net/route.h b/i386/i386at/gpl/linux/include/net/route.h
new file mode 100644
index 00000000..8ce67383
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/route.h
@@ -0,0 +1,280 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the IP router.
+ *
+ * Version: @(#)route.h 1.0.4 05/27/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Fixes:
+ * Alan Cox : Reformatted. Added ip_rt_local()
+ * Alan Cox : Support for TCP parameters.
+ * Alexey Kuznetsov: Major changes for new routing code.
+ *
+ * FIXME:
+ * Modules stuff is broken at the moment.
+ * Make atomic ops more generic and hide them in asm/...
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _ROUTE_H
+#define _ROUTE_H
+
+#include <linux/config.h>
+
+/*
+ * 0 - no debugging messages
+ * 1 - rare events and bugs situations (default)
+ * 2 - trace mode.
+ */
+#define RT_CACHE_DEBUG 1
+
+#define RT_HASH_DIVISOR 256
+#define RT_CACHE_SIZE_MAX 256
+
+#define RTZ_HASH_DIVISOR 256
+
+#if RT_CACHE_DEBUG >= 2
+#define RTZ_HASHING_LIMIT 0
+#else
+#define RTZ_HASHING_LIMIT 16
+#endif
+
+/*
+ * Maximal time to live for unused entry.
+ */
+#define RT_CACHE_TIMEOUT (HZ*300)
+
+/*
+ * Prevents LRU trashing, entries considered equivalent,
+ * if the difference between last use times is less then this number.
+ */
+#define RT_CACHE_BUBBLE_THRESHOULD (HZ*5)
+
+#include <linux/route.h>
+
+#ifdef __KERNEL__
+#define RTF_LOCAL 0x8000
+#endif
+
+/*
+ * Semaphores.
+ */
+#if defined(__alpha__)
+
+static __inline__ void ATOMIC_INCR(unsigned int * addr)
+{
+ unsigned tmp;
+
+ __asm__ __volatile__(
+ "1:\n\
+ ldl_l %1,%2\n\
+ addl %1,1,%1\n\
+ stl_c %1,%0\n\
+ beq %1,1b\n"
+ : "m=" (*addr), "r=&" (tmp)
+ : "m"(*addr));
+}
+
+static __inline__ void ATOMIC_DECR(unsigned int * addr)
+{
+ unsigned tmp;
+
+ __asm__ __volatile__(
+ "1:\n\
+ ldl_l %1,%2\n\
+ subl %1,1,%1\n\
+ stl_c %1,%0\n\
+ beq %1,1b\n"
+ : "m=" (*addr), "r=&" (tmp)
+ : "m"(*addr));
+}
+
+static __inline__ int ATOMIC_DECR_AND_CHECK (unsigned int * addr)
+{
+ unsigned tmp;
+ int result;
+
+ __asm__ __volatile__(
+ "1:\n\
+ ldl_l %1,%3\n\
+ subl %1,1,%1\n\
+ mov %1,%2\n\
+ stl_c %1,%0\n\
+ beq %1,1b\n"
+ : "m=" (*addr), "r=&" (tmp), "r=&"(result)
+ : "m"(*addr));
+ return result;
+}
+
+#elif defined(__i386__)
+#include <asm/bitops.h>
+
+extern __inline__ void ATOMIC_INCR(void * addr)
+{
+ __asm__ __volatile__(
+ "incl %0"
+ :"=m" (ADDR));
+}
+
+extern __inline__ void ATOMIC_DECR(void * addr)
+{
+ __asm__ __volatile__(
+ "decl %0"
+ :"=m" (ADDR));
+}
+
+/*
+ * It is DECR that is ATOMIC, not CHECK!
+ * If you want to do atomic checks, use cli()/sti(). --ANK
+ */
+
+extern __inline__ unsigned long ATOMIC_DECR_AND_CHECK(void * addr)
+{
+ unsigned long retval;
+ __asm__ __volatile__(
+ "decl %0\nmovl %0,%1"
+ : "=m" (ADDR), "=r"(retval));
+ return retval;
+}
+
+
+#else
+
+static __inline__ void ATOMIC_INCR(unsigned int * addr)
+{
+ (*(__volatile__ unsigned int*)addr)++;
+}
+
+static __inline__ void ATOMIC_DECR(unsigned int * addr)
+{
+ (*(__volatile__ unsigned int*)addr)--;
+}
+
+static __inline__ int ATOMIC_DECR_AND_CHECK (unsigned int * addr)
+{
+ ATOMIC_DECR(addr);
+ return *(volatile unsigned int*)addr;
+}
+
+#endif
+
+
+
+struct rtable
+{
+ struct rtable *rt_next;
+ __u32 rt_dst;
+ __u32 rt_src;
+ __u32 rt_gateway;
+ unsigned rt_refcnt;
+ unsigned rt_use;
+ unsigned long rt_window;
+ unsigned long rt_lastuse;
+ struct hh_cache *rt_hh;
+ struct device *rt_dev;
+ unsigned short rt_flags;
+ unsigned short rt_mtu;
+ unsigned short rt_irtt;
+ unsigned char rt_tos;
+};
+
+extern void ip_rt_flush(struct device *dev);
+extern void ip_rt_redirect(__u32 src, __u32 dst, __u32 gw, struct device *dev);
+extern struct rtable *ip_rt_slow_route(__u32 daddr, int local);
+extern int rt_get_info(char * buffer, char **start, off_t offset, int length, int dummy);
+extern int rt_cache_get_info(char *buffer, char **start, off_t offset, int length, int dummy);
+extern int ip_rt_ioctl(unsigned int cmd, void *arg);
+extern int ip_rt_new(struct rtentry *rt);
+extern void ip_rt_check_expire(void);
+extern void ip_rt_advice(struct rtable **rp, int advice);
+
+extern void ip_rt_run_bh(void);
+extern int ip_rt_lock;
+extern unsigned ip_rt_bh_mask;
+extern struct rtable *ip_rt_hash_table[RT_HASH_DIVISOR];
+
+extern __inline__ void ip_rt_fast_lock(void)
+{
+ ATOMIC_INCR(&ip_rt_lock);
+}
+
+extern __inline__ void ip_rt_fast_unlock(void)
+{
+ ATOMIC_DECR(&ip_rt_lock);
+}
+
+extern __inline__ void ip_rt_unlock(void)
+{
+ if (!ATOMIC_DECR_AND_CHECK(&ip_rt_lock) && ip_rt_bh_mask)
+ ip_rt_run_bh();
+}
+
+extern __inline__ unsigned ip_rt_hash_code(__u32 addr)
+{
+ unsigned tmp = addr + (addr>>16);
+ return (tmp + (tmp>>8)) & 0xFF;
+}
+
+
+extern __inline__ void ip_rt_put(struct rtable * rt)
+#ifndef MODULE
+{
+ if (rt)
+ ATOMIC_DECR(&rt->rt_refcnt);
+}
+#else
+;
+#endif
+
+#ifdef CONFIG_KERNELD
+extern struct rtable * ip_rt_route(__u32 daddr, int local);
+#else
+extern __inline__ struct rtable * ip_rt_route(__u32 daddr, int local)
+#ifndef MODULE
+{
+ struct rtable * rth;
+
+ ip_rt_fast_lock();
+
+ for (rth=ip_rt_hash_table[ip_rt_hash_code(daddr)^local]; rth; rth=rth->rt_next)
+ {
+ if (rth->rt_dst == daddr)
+ {
+ rth->rt_lastuse = jiffies;
+ ATOMIC_INCR(&rth->rt_use);
+ ATOMIC_INCR(&rth->rt_refcnt);
+ ip_rt_unlock();
+ return rth;
+ }
+ }
+ return ip_rt_slow_route (daddr, local);
+}
+#else
+;
+#endif
+#endif
+
+extern __inline__ struct rtable * ip_check_route(struct rtable ** rp,
+ __u32 daddr, int local)
+{
+ struct rtable * rt = *rp;
+
+ if (!rt || rt->rt_dst != daddr || !(rt->rt_flags&RTF_UP)
+ || ((local==1)^((rt->rt_flags&RTF_LOCAL) != 0)))
+ {
+ ip_rt_put(rt);
+ rt = ip_rt_route(daddr, local);
+ *rp = rt;
+ }
+ return rt;
+}
+
+
+#endif /* _ROUTE_H */
diff --git a/i386/i386at/gpl/linux/include/net/slhc.h b/i386/i386at/gpl/linux/include/net/slhc.h
new file mode 100644
index 00000000..c7b39db5
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/slhc.h
@@ -0,0 +1,6 @@
+#ifndef __NET_SLHC_H
+#define __NET_SLHC_H
+
+extern void slhc_install(void);
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/snmp.h b/i386/i386at/gpl/linux/include/net/snmp.h
new file mode 100644
index 00000000..552292be
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/snmp.h
@@ -0,0 +1,107 @@
+/*
+ *
+ * SNMP MIB entries for the IP subsystem.
+ *
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ *
+ * We don't chose to implement SNMP in the kernel (this would
+ * be silly as SNMP is a pain in the backside in places). We do
+ * however need to collect the MIB statistics and export them
+ * out of /proc (eventually)
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ *
+ */
+
+#ifndef _SNMP_H
+#define _SNMP_H
+
+/*
+ * We use all unsigned longs. Linux will soon be so reliable that even these
+ * will rapidly get too small 8-). Seriously consider the IpInReceives count
+ * on the 20Gb/s + networks people expect in a few years time!
+ */
+
+struct ip_mib
+{
+ unsigned long IpForwarding;
+ unsigned long IpDefaultTTL;
+ unsigned long IpInReceives;
+ unsigned long IpInHdrErrors;
+ unsigned long IpInAddrErrors;
+ unsigned long IpForwDatagrams;
+ unsigned long IpInUnknownProtos;
+ unsigned long IpInDiscards;
+ unsigned long IpInDelivers;
+ unsigned long IpOutRequests;
+ unsigned long IpOutDiscards;
+ unsigned long IpOutNoRoutes;
+ unsigned long IpReasmTimeout;
+ unsigned long IpReasmReqds;
+ unsigned long IpReasmOKs;
+ unsigned long IpReasmFails;
+ unsigned long IpFragOKs;
+ unsigned long IpFragFails;
+ unsigned long IpFragCreates;
+};
+
+
+struct icmp_mib
+{
+ unsigned long IcmpInMsgs;
+ unsigned long IcmpInErrors;
+ unsigned long IcmpInDestUnreachs;
+ unsigned long IcmpInTimeExcds;
+ unsigned long IcmpInParmProbs;
+ unsigned long IcmpInSrcQuenchs;
+ unsigned long IcmpInRedirects;
+ unsigned long IcmpInEchos;
+ unsigned long IcmpInEchoReps;
+ unsigned long IcmpInTimestamps;
+ unsigned long IcmpInTimestampReps;
+ unsigned long IcmpInAddrMasks;
+ unsigned long IcmpInAddrMaskReps;
+ unsigned long IcmpOutMsgs;
+ unsigned long IcmpOutErrors;
+ unsigned long IcmpOutDestUnreachs;
+ unsigned long IcmpOutTimeExcds;
+ unsigned long IcmpOutParmProbs;
+ unsigned long IcmpOutSrcQuenchs;
+ unsigned long IcmpOutRedirects;
+ unsigned long IcmpOutEchos;
+ unsigned long IcmpOutEchoReps;
+ unsigned long IcmpOutTimestamps;
+ unsigned long IcmpOutTimestampReps;
+ unsigned long IcmpOutAddrMasks;
+ unsigned long IcmpOutAddrMaskReps;
+};
+
+struct tcp_mib
+{
+ unsigned long TcpRtoAlgorithm;
+ unsigned long TcpRtoMin;
+ unsigned long TcpRtoMax;
+ unsigned long TcpMaxConn;
+ unsigned long TcpActiveOpens;
+ unsigned long TcpPassiveOpens;
+ unsigned long TcpAttemptFails;
+ unsigned long TcpEstabResets;
+ unsigned long TcpCurrEstab;
+ unsigned long TcpInSegs;
+ unsigned long TcpOutSegs;
+ unsigned long TcpRetransSegs;
+};
+
+struct udp_mib
+{
+ unsigned long UdpInDatagrams;
+ unsigned long UdpNoPorts;
+ unsigned long UdpInErrors;
+ unsigned long UdpOutDatagrams;
+};
+
+
+#endif
diff --git a/i386/i386at/gpl/linux/include/net/sock.h b/i386/i386at/gpl/linux/include/net/sock.h
new file mode 100644
index 00000000..dc7a4a90
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/sock.h
@@ -0,0 +1,486 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the AF_INET socket handler.
+ *
+ * Version: @(#)sock.h 1.0.4 05/13/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Corey Minyard <wf-rch!minyard@relay.EU.net>
+ * Florian La Roche <flla@stud.uni-sb.de>
+ *
+ * Fixes:
+ * Alan Cox : Volatiles in skbuff pointers. See
+ * skbuff comments. May be overdone,
+ * better to prove they can be removed
+ * than the reverse.
+ * Alan Cox : Added a zapped field for tcp to note
+ * a socket is reset and must stay shut up
+ * Alan Cox : New fields for options
+ * Pauline Middelink : identd support
+ * Alan Cox : Eliminate low level recv/recvfrom
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _SOCK_H
+#define _SOCK_H
+
+#include <linux/timer.h>
+#include <linux/ip.h> /* struct options */
+#include <linux/in.h> /* struct sockaddr_in */
+#include <linux/tcp.h> /* struct tcphdr */
+#include <linux/config.h>
+
+#include <linux/netdevice.h>
+#include <linux/skbuff.h> /* struct sk_buff */
+#include <net/protocol.h> /* struct inet_protocol */
+#ifdef CONFIG_AX25
+#include <net/ax25.h>
+#ifdef CONFIG_NETROM
+#include <net/netrom.h>
+#endif
+#endif
+#ifdef CONFIG_IPX
+#include <net/ipx.h>
+#endif
+#ifdef CONFIG_ATALK
+#include <linux/atalk.h>
+#endif
+
+#include <linux/igmp.h>
+
+/* Think big (also on some systems a byte is faster) */
+#define SOCK_ARRAY_SIZE 256
+
+
+/*
+ * The AF_UNIX specific socket options
+ */
+
+struct unix_opt
+{
+ int family;
+ char * name;
+ int locks;
+ struct inode * inode;
+ struct semaphore readsem;
+ struct sock * other;
+};
+
+/*
+ * IP packet socket options
+ */
+
+struct inet_packet_opt
+{
+ struct notifier_block notifier; /* Used when bound */
+ struct device *bound_dev;
+ unsigned long dev_stamp;
+ struct packet_type *prot_hook;
+ char device_name[15];
+};
+
+
+/*
+ * This structure really needs to be cleaned up.
+ * Most of it is for TCP, and not used by any of
+ * the other protocols.
+ */
+struct sock
+{
+ struct options *opt;
+ volatile unsigned long wmem_alloc;
+ volatile unsigned long rmem_alloc;
+ unsigned long allocation; /* Allocation mode */
+ __u32 write_seq;
+ __u32 sent_seq;
+ __u32 acked_seq;
+ __u32 copied_seq;
+ __u32 rcv_ack_seq;
+ __u32 window_seq;
+ __u32 fin_seq;
+ __u32 urg_seq;
+ __u32 urg_data;
+ int users; /* user count */
+ /*
+ * Not all are volatile, but some are, so we
+ * might as well say they all are.
+ */
+ volatile char dead,
+ urginline,
+ intr,
+ blog,
+ done,
+ reuse,
+ keepopen,
+ linger,
+ delay_acks,
+ destroy,
+ ack_timed,
+ no_check,
+ zapped, /* In ax25 & ipx means not linked */
+ broadcast,
+ nonagle,
+ bsdism;
+ unsigned long lingertime;
+ int proc;
+ struct sock *next;
+ struct sock *prev; /* Doubly linked chain.. */
+ struct sock *pair;
+ struct sk_buff * volatile send_head;
+ struct sk_buff * volatile send_tail;
+ struct sk_buff_head back_log;
+ struct sk_buff *partial;
+ struct timer_list partial_timer;
+ long retransmits;
+ struct sk_buff_head write_queue,
+ receive_queue;
+ struct proto *prot;
+ struct wait_queue **sleep;
+ __u32 daddr;
+ __u32 saddr; /* Sending source */
+ __u32 rcv_saddr; /* Bound address */
+ unsigned short max_unacked;
+ unsigned short window;
+ __u32 lastwin_seq; /* sequence number when we last updated the window we offer */
+ volatile unsigned long ato; /* ack timeout */
+ volatile unsigned long lrcvtime; /* jiffies at last rcv */
+ unsigned short bytes_rcv;
+/*
+ * mss is min(mtu, max_window)
+ */
+ unsigned short mtu; /* mss negotiated in the syn's */
+ volatile unsigned short mss; /* current eff. mss - can change */
+ volatile unsigned short user_mss; /* mss requested by user in ioctl */
+ volatile unsigned short max_window;
+ unsigned long window_clamp;
+ unsigned short num;
+ volatile unsigned short cong_window;
+ volatile unsigned short cong_count;
+ volatile unsigned short ssthresh;
+ volatile unsigned short packets_out;
+ volatile unsigned short shutdown;
+ volatile unsigned long rtt;
+ volatile unsigned long mdev;
+ volatile unsigned long rto;
+
+/*
+ * currently backoff isn't used, but I'm maintaining it in case
+ * we want to go back to a backoff formula that needs it
+ */
+
+ volatile unsigned short backoff;
+ volatile int err, err_soft; /* Soft holds errors that don't
+ cause failure but are the cause
+ of a persistent failure not just
+ 'timed out' */
+ unsigned char protocol;
+ volatile unsigned char state;
+ volatile unsigned char ack_backlog;
+ unsigned char max_ack_backlog;
+ unsigned char priority;
+ unsigned char debug;
+ unsigned short rcvbuf;
+ unsigned short sndbuf;
+ unsigned short type;
+ unsigned char localroute; /* Route locally only */
+#ifdef CONFIG_IPX
+/*
+ * Once the IPX ncpd patches are in these are going into protinfo
+ */
+ ipx_address ipx_dest_addr;
+ ipx_interface *ipx_intrfc;
+ unsigned short ipx_port;
+
+/* To handle asynchronous messages from the NetWare server, we have to
+ * know the connection this socket belongs to. Sorry to blow up this
+ * structure even more. */
+ struct ncp_server *ipx_ncp_server;
+
+#ifdef CONFIG_IPX_INTERN
+ unsigned char ipx_node[IPX_NODE_LEN];
+#endif
+ unsigned short ipx_type;
+#endif
+#ifdef CONFIG_AX25
+ ax25_cb *ax25;
+#ifdef CONFIG_NETROM
+ nr_cb *nr;
+#endif
+#endif
+
+/*
+ * This is where all the private (optional) areas that don't
+ * overlap will eventually live.
+ */
+
+ union
+ {
+ struct unix_opt af_unix;
+#ifdef CONFIG_ATALK
+ struct atalk_sock af_at;
+#endif
+#ifdef CONFIG_INET
+ struct inet_packet_opt af_packet;
+#endif
+ } protinfo;
+
+/*
+ * IP 'private area' or will be eventually
+ */
+ int ip_ttl; /* TTL setting */
+ int ip_tos; /* TOS */
+ struct tcphdr dummy_th;
+ struct timer_list keepalive_timer; /* TCP keepalive hack */
+ struct timer_list retransmit_timer; /* TCP retransmit timer */
+ struct timer_list ack_timer; /* TCP delayed ack timer */
+ int ip_xmit_timeout; /* Why the timeout is running */
+ struct rtable *ip_route_cache; /* Cached output route */
+ unsigned char ip_hdrincl; /* Include headers ? */
+#ifdef CONFIG_IP_MULTICAST
+ int ip_mc_ttl; /* Multicasting TTL */
+ int ip_mc_loop; /* Loopback */
+ char ip_mc_name[MAX_ADDR_LEN];/* Multicast device name */
+ struct ip_mc_socklist *ip_mc_list; /* Group array */
+#endif
+
+/*
+ * This part is used for the timeout functions (timer.c).
+ */
+
+ int timeout; /* What are we waiting for? */
+ struct timer_list timer; /* This is the TIME_WAIT/receive timer
+ * when we are doing IP
+ */
+ struct timeval stamp;
+
+ /*
+ * Identd
+ */
+
+ struct socket *socket;
+
+ /*
+ * Callbacks
+ */
+
+ void (*state_change)(struct sock *sk);
+ void (*data_ready)(struct sock *sk,int bytes);
+ void (*write_space)(struct sock *sk);
+ void (*error_report)(struct sock *sk);
+
+};
+
+/*
+ * IP protocol blocks we attach to sockets.
+ */
+
+struct proto
+{
+ void (*close)(struct sock *sk, unsigned long timeout);
+ int (*build_header)(struct sk_buff *skb,
+ __u32 saddr,
+ __u32 daddr,
+ struct device **dev, int type,
+ struct options *opt, int len,
+ int tos, int ttl, struct rtable ** rp);
+ int (*connect)(struct sock *sk,
+ struct sockaddr_in *usin, int addr_len);
+ struct sock * (*accept) (struct sock *sk, int flags);
+ void (*queue_xmit)(struct sock *sk,
+ struct device *dev, struct sk_buff *skb,
+ int free);
+ void (*retransmit)(struct sock *sk, int all);
+ void (*write_wakeup)(struct sock *sk);
+ void (*read_wakeup)(struct sock *sk);
+ int (*rcv)(struct sk_buff *buff, struct device *dev,
+ struct options *opt, __u32 daddr,
+ unsigned short len, __u32 saddr,
+ int redo, struct inet_protocol *protocol);
+ int (*select)(struct sock *sk, int which,
+ select_table *wait);
+ int (*ioctl)(struct sock *sk, int cmd,
+ unsigned long arg);
+ int (*init)(struct sock *sk);
+ void (*shutdown)(struct sock *sk, int how);
+ int (*setsockopt)(struct sock *sk, int level, int optname,
+ char *optval, int optlen);
+ int (*getsockopt)(struct sock *sk, int level, int optname,
+ char *optval, int *option);
+ int (*sendmsg)(struct sock *sk, struct msghdr *msg, int len,
+ int noblock, int flags);
+ int (*recvmsg)(struct sock *sk, struct msghdr *msg, int len,
+ int noblock, int flags, int *addr_len);
+ int (*bind)(struct sock *sk, struct sockaddr *uaddr, int addr_len);
+ unsigned short max_header;
+ unsigned long retransmits;
+ char name[32];
+ int inuse, highestinuse;
+ struct sock * sock_array[SOCK_ARRAY_SIZE];
+};
+
+#define TIME_WRITE 1
+#define TIME_CLOSE 2
+#define TIME_KEEPOPEN 3
+#define TIME_DESTROY 4
+#define TIME_DONE 5 /* Used to absorb those last few packets */
+#define TIME_PROBE0 6
+/*
+ * About 10 seconds
+ */
+#define SOCK_DESTROY_TIME (10*HZ)
+
+
+/*
+ * Sockets 0-1023 can't be bound too unless you are superuser
+ */
+
+#define PROT_SOCK 1024
+
+
+#define SHUTDOWN_MASK 3
+#define RCV_SHUTDOWN 1
+#define SEND_SHUTDOWN 2
+
+/*
+ * Used by processes to "lock" a socket state, so that
+ * interrupts and bottom half handlers won't change it
+ * from under us. It essentially blocks any incoming
+ * packets, so that we won't get any new data or any
+ * packets that change the state of the socket.
+ *
+ * Note the 'barrier()' calls: gcc may not move a lock
+ * "downwards" or a unlock "upwards" when optimizing.
+ */
+extern void __release_sock(struct sock *sk);
+
+static inline void lock_sock(struct sock *sk)
+{
+#if 1
+/* debugging code: the test isn't even 100% correct, but it can catch bugs */
+/* Note that a double lock is ok in theory - it's just _usually_ a bug */
+ if (sk->users) {
+ __label__ here;
+ printk("double lock on socket at %p\n", &&here);
+here:
+ }
+#endif
+ sk->users++;
+ barrier();
+}
+
+static inline void release_sock(struct sock *sk)
+{
+ barrier();
+#if 1
+/* debugging code: remove me when ok */
+ if (sk->users == 0) {
+ __label__ here;
+ sk->users = 1;
+ printk("trying to unlock unlocked socket at %p\n", &&here);
+here:
+ }
+#endif
+ if (!--sk->users)
+ __release_sock(sk);
+}
+
+
+extern void destroy_sock(struct sock *sk);
+extern unsigned short get_new_socknum(struct proto *,
+ unsigned short);
+extern void put_sock(unsigned short, struct sock *);
+extern struct sock *get_sock(struct proto *, unsigned short,
+ unsigned long, unsigned short,
+ unsigned long);
+extern struct sock *get_sock_mcast(struct sock *, unsigned short,
+ unsigned long, unsigned short,
+ unsigned long);
+extern struct sock *get_sock_raw(struct sock *, unsigned short,
+ unsigned long, unsigned long);
+
+extern struct sk_buff *sock_wmalloc(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+extern struct sk_buff *sock_rmalloc(struct sock *sk,
+ unsigned long size, int force,
+ int priority);
+extern void sock_wfree(struct sock *sk,
+ struct sk_buff *skb);
+extern void sock_rfree(struct sock *sk,
+ struct sk_buff *skb);
+extern unsigned long sock_rspace(struct sock *sk);
+extern unsigned long sock_wspace(struct sock *sk);
+
+extern int sock_setsockopt(struct sock *sk, int level,
+ int op, char *optval,
+ int optlen);
+
+extern int sock_getsockopt(struct sock *sk, int level,
+ int op, char *optval,
+ int *optlen);
+extern struct sk_buff *sock_alloc_send_skb(struct sock *skb,
+ unsigned long size,
+ unsigned long fallback,
+ int noblock,
+ int *errcode);
+
+/*
+ * Queue a received datagram if it will fit. Stream and sequenced
+ * protocols can't normally use this as they need to fit buffers in
+ * and play with them.
+ *
+ * Inlined as its very short and called for pretty much every
+ * packet ever received.
+ */
+
+extern __inline__ int sock_queue_rcv_skb(struct sock *sk, struct sk_buff *skb)
+{
+ unsigned long flags;
+ if(sk->rmem_alloc + skb->truesize >= sk->rcvbuf)
+ return -ENOMEM;
+ save_flags(flags);
+ cli();
+ sk->rmem_alloc+=skb->truesize;
+ skb->sk=sk;
+ restore_flags(flags);
+ skb_queue_tail(&sk->receive_queue,skb);
+ if(!sk->dead)
+ sk->data_ready(sk,skb->len);
+ return 0;
+}
+
+/*
+ * Recover an error report and clear atomically
+ */
+
+extern __inline__ int sock_error(struct sock *sk)
+{
+ int err=xchg(&sk->err,0);
+ return -err;
+}
+
+/*
+ * Declarations from timer.c
+ */
+
+extern struct sock *timer_base;
+
+extern void delete_timer (struct sock *);
+extern void reset_timer (struct sock *, int, unsigned long);
+extern void net_timer (unsigned long);
+
+
+/*
+ * Enable debug/info messages
+ */
+
+#define NETDEBUG(x) x
+
+#endif /* _SOCK_H */
diff --git a/i386/i386at/gpl/linux/include/net/tcp.h b/i386/i386at/gpl/linux/include/net/tcp.h
new file mode 100644
index 00000000..3c7eb7de
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/tcp.h
@@ -0,0 +1,329 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the TCP module.
+ *
+ * Version: @(#)tcp.h 1.0.5 05/23/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _TCP_H
+#define _TCP_H
+
+#include <linux/tcp.h>
+#include <net/checksum.h>
+
+#define MAX_SYN_SIZE 44 + MAX_HEADER + 15
+#define MAX_FIN_SIZE 40 + MAX_HEADER + 15
+#define MAX_ACK_SIZE 40 + MAX_HEADER + 15
+#define MAX_RESET_SIZE 40 + MAX_HEADER + 15
+#define MAX_WINDOW 32767 /* Never offer a window over 32767 without using
+ window scaling (not yet supported). Some poor
+ stacks do signed 16bit maths! */
+#define MIN_WINDOW 2048
+#define MAX_ACK_BACKLOG 2
+#define MIN_WRITE_SPACE 2048
+#define TCP_WINDOW_DIFF 2048
+
+/* urg_data states */
+#define URG_VALID 0x0100
+#define URG_NOTYET 0x0200
+#define URG_READ 0x0400
+
+#define TCP_RETR1 7 /*
+ * This is how many retries it does before it
+ * tries to figure out if the gateway is
+ * down.
+ */
+
+#define TCP_RETR2 15 /*
+ * This should take at least
+ * 90 minutes to time out.
+ */
+
+#define TCP_TIMEOUT_LEN (15*60*HZ) /* should be about 15 mins */
+#define TCP_TIMEWAIT_LEN (60*HZ) /* how long to wait to successfully
+ * close the socket, about 60 seconds */
+#define TCP_FIN_TIMEOUT (3*60*HZ) /* BSD style FIN_WAIT2 deadlock breaker */
+#define TCP_ACK_TIME (3*HZ) /* time to delay before sending an ACK */
+#define TCP_DONE_TIME (5*HZ/2)/* maximum time to wait before actually
+ * destroying a socket */
+#define TCP_WRITE_TIME (30*HZ) /* initial time to wait for an ACK,
+ * after last transmit */
+#define TCP_TIMEOUT_INIT (3*HZ) /* RFC 1122 initial timeout value */
+#define TCP_SYN_RETRIES 10 /* number of times to retry opening a
+ * connection (TCP_RETR2-....) */
+#define TCP_PROBEWAIT_LEN (1*HZ)/* time to wait between probes when
+ * I've got something to write and
+ * there is no window */
+
+#define TCP_NO_CHECK 0 /* turn to one if you want the default
+ * to be no checksum */
+
+
+/*
+ * TCP option
+ */
+
+#define TCPOPT_NOP 1 /* Padding */
+#define TCPOPT_EOL 0 /* End of options */
+#define TCPOPT_MSS 2 /* Segment size negotiating */
+/*
+ * We don't use these yet, but they are for PAWS and big windows
+ */
+#define TCPOPT_WINDOW 3 /* Window scaling */
+#define TCPOPT_TIMESTAMP 8 /* Better RTT estimations/PAWS */
+
+
+/*
+ * The next routines deal with comparing 32 bit unsigned ints
+ * and worry about wraparound (automatic with unsigned arithmetic).
+ */
+
+extern __inline int before(__u32 seq1, __u32 seq2)
+{
+ return (__s32)(seq1-seq2) < 0;
+}
+
+extern __inline int after(__u32 seq1, __u32 seq2)
+{
+ return (__s32)(seq2-seq1) < 0;
+}
+
+
+/* is s2<=s1<=s3 ? */
+extern __inline int between(__u32 seq1, __u32 seq2, __u32 seq3)
+{
+ return (after(seq1+1, seq2) && before(seq1, seq3+1));
+}
+
+static __inline__ int min(unsigned int a, unsigned int b)
+{
+ if (a < b)
+ return(a);
+ return(b);
+}
+
+extern struct proto tcp_prot;
+extern struct tcp_mib tcp_statistics;
+extern struct wait_queue *master_select_wakeup;
+
+extern void tcp_err(int type, int code, unsigned char *header, __u32 daddr,
+ __u32, struct inet_protocol *protocol);
+extern void tcp_shutdown (struct sock *sk, int how);
+extern int tcp_rcv(struct sk_buff *skb, struct device *dev,
+ struct options *opt, __u32 daddr,
+ unsigned short len, __u32 saddr, int redo,
+ struct inet_protocol *protocol);
+
+extern int tcp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+
+extern void tcp_read_wakeup(struct sock *);
+extern void tcp_write_xmit(struct sock *);
+extern void tcp_time_wait(struct sock *);
+extern void tcp_retransmit(struct sock *, int);
+extern void tcp_do_retransmit(struct sock *, int);
+extern void tcp_send_check(struct tcphdr *th, unsigned long saddr,
+ unsigned long daddr, int len, struct sk_buff *skb);
+
+/* tcp_output.c */
+
+extern void tcp_send_probe0(struct sock *);
+extern void tcp_send_partial(struct sock *);
+extern void tcp_write_wakeup(struct sock *);
+extern void tcp_send_fin(struct sock *sk);
+extern void tcp_send_synack(struct sock *, struct sock *, struct sk_buff *);
+extern void tcp_send_skb(struct sock *, struct sk_buff *);
+extern void tcp_send_ack(u32, u32, struct sock *sk, struct tcphdr *th, u32);
+extern void tcp_send_reset(unsigned long saddr, unsigned long daddr, struct tcphdr *th,
+ struct proto *prot, struct options *opt, struct device *dev, int tos, int ttl);
+
+extern void tcp_enqueue_partial(struct sk_buff *, struct sock *);
+extern struct sk_buff * tcp_dequeue_partial(struct sock *);
+
+/* tcp_input.c */
+extern void tcp_cache_zap(void);
+
+/* tcp_timer.c */
+#define tcp_reset_msl_timer(x,y,z) reset_timer(x,y,z)
+extern void tcp_reset_xmit_timer(struct sock *, int, unsigned long);
+extern void tcp_retransmit_timer(unsigned long);
+
+/*
+ * Default sequence number picking algorithm.
+ * As close as possible to RFC 793, which
+ * suggests using a 250kHz clock.
+ * Further reading shows this assumes 2MB/s networks.
+ * For 10MB/s ethernet, a 1MHz clock is appropriate.
+ * That's funny, Linux has one built in! Use it!
+ */
+
+static inline u32 tcp_init_seq(void)
+{
+ struct timeval tv;
+ do_gettimeofday(&tv);
+ return tv.tv_usec+tv.tv_sec*1000000;
+}
+
+/*
+ * This function returns the amount that we can raise the
+ * usable window based on the following constraints
+ *
+ * 1. The window can never be shrunk once it is offered (RFC 793)
+ * 2. We limit memory per socket
+ */
+
+static __inline__ unsigned short tcp_raise_window(struct sock *sk)
+{
+ long free_space = sock_rspace(sk);
+ long window;
+
+ if (free_space > 1024)
+ free_space &= ~0x3FF; /* make free space a multiple of 1024 */
+
+ if(sk->window_clamp)
+ free_space = min(sk->window_clamp, free_space);
+
+ /*
+ * compute the actual window i.e.
+ * old_window - received_bytes_on_that_win
+ */
+
+ window = sk->window - (sk->acked_seq - sk->lastwin_seq);
+
+ if (sk->mss == 0)
+ sk->mss = sk->mtu;
+
+ if ( window < 0 ) {
+ window = 0;
+ printk(KERN_DEBUG "TRW: win < 0 w=%d 1=%u 2=%u\n",
+ sk->window, sk->acked_seq, sk->lastwin_seq);
+ }
+
+ if ( (free_space - window) >= min(sk->mss, MAX_WINDOW/2) )
+ return ((free_space - window) / sk->mss) * sk->mss;
+
+ return 0;
+}
+
+static __inline__ unsigned short tcp_select_window(struct sock *sk)
+{
+ long free_space = sock_rspace(sk);
+ long window;
+
+ if (free_space > 1024)
+ free_space &= ~0x3FF; /* make free space a multiple of 1024 */
+
+ if (sk->window_clamp)
+ free_space = min(sk->window_clamp, free_space);
+
+ /*
+ * compute the actual window i.e.
+ * old_window - received_bytes_on_that_win
+ */
+
+ if (sk->mss == 0)
+ sk->mss = sk->mtu;
+
+ window = sk->window - (sk->acked_seq - sk->lastwin_seq);
+
+ if ( window < 0 ) {
+ window = 0;
+ printk(KERN_DEBUG "TSW: win < 0 w=%d 1=%u 2=%u\n",
+ sk->window, sk->acked_seq, sk->lastwin_seq);
+ }
+
+ /*
+ * RFC 1122:
+ * "the suggested [SWS] avoidance algoritm for the receiver is to keep
+ * RECV.NEXT + RCV.WIN fixed until:
+ * RCV.BUFF - RCV.USER - RCV.WINDOW >= min(1/2 RCV.BUFF, MSS)"
+ *
+ * i.e. don't raise the right edge of the window until you can't raise
+ * it MSS bytes
+ */
+
+ if ( (free_space - window) >= min(sk->mss, MAX_WINDOW/2) )
+ window += ((free_space - window) / sk->mss) * sk->mss;
+
+ sk->window = window;
+ sk->lastwin_seq = sk->acked_seq;
+
+ return sk->window;
+}
+
+/*
+ * List all states of a TCP socket that can be viewed as a "connected"
+ * state. This now includes TCP_SYN_RECV, although I am not yet fully
+ * convinced that this is the solution for the 'getpeername(2)'
+ * problem. Thanks to Stephen A. Wood <saw@cebaf.gov> -FvK
+ */
+
+extern __inline const int tcp_connected(const int state)
+{
+ return(state == TCP_ESTABLISHED || state == TCP_CLOSE_WAIT ||
+ state == TCP_FIN_WAIT1 || state == TCP_FIN_WAIT2 ||
+ state == TCP_SYN_RECV);
+}
+
+/*
+ * Calculate(/check) TCP checksum
+ */
+static __inline__ u16 tcp_check(struct tcphdr *th, int len,
+ unsigned long saddr, unsigned long daddr, unsigned long base)
+{
+ return csum_tcpudp_magic(saddr,daddr,len,IPPROTO_TCP,base);
+}
+
+#undef STATE_TRACE
+
+#ifdef STATE_TRACE
+static char *statename[]={
+ "Unused","Established","Syn Sent","Syn Recv",
+ "Fin Wait 1","Fin Wait 2","Time Wait", "Close",
+ "Close Wait","Last ACK","Listen","Closing"
+};
+#endif
+
+static __inline__ void tcp_set_state(struct sock *sk, int state)
+{
+ int oldstate = sk->state;
+
+ sk->state = state;
+
+#ifdef STATE_TRACE
+ if(sk->debug)
+ printk("TCP sk=%p, State %s -> %s\n",sk, statename[oldstate],statename[state]);
+#endif
+
+ switch (state) {
+ case TCP_ESTABLISHED:
+ if (oldstate != TCP_ESTABLISHED) {
+ tcp_statistics.TcpCurrEstab++;
+ /* This is a hack but it doesn't occur often and it's going to
+ be a real to fix nicely */
+ if (oldstate == TCP_SYN_RECV)
+ wake_up_interruptible(&master_select_wakeup);
+ }
+ break;
+
+ case TCP_CLOSE:
+ tcp_cache_zap();
+ /* Should be about 2 rtt's */
+ reset_timer(sk, TIME_DONE, min(sk->rtt * 2, TCP_DONE_TIME));
+ /* fall through */
+ default:
+ if (oldstate==TCP_ESTABLISHED)
+ tcp_statistics.TcpCurrEstab--;
+ }
+}
+
+#endif /* _TCP_H */
diff --git a/i386/i386at/gpl/linux/include/net/udp.h b/i386/i386at/gpl/linux/include/net/udp.h
new file mode 100644
index 00000000..13735d17
--- /dev/null
+++ b/i386/i386at/gpl/linux/include/net/udp.h
@@ -0,0 +1,52 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Definitions for the UDP module.
+ *
+ * Version: @(#)udp.h 1.0.2 05/07/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ *
+ * Fixes:
+ * Alan Cox : Turned on udp checksums. I don't want to
+ * chase 'memory corruption' bugs that aren't!
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#ifndef _UDP_H
+#define _UDP_H
+
+#include <linux/udp.h>
+
+
+#define UDP_NO_CHECK 0
+
+
+extern struct proto udp_prot;
+
+
+extern void udp_err(int type, int code, unsigned char *header, __u32 daddr,
+ __u32 saddr, struct inet_protocol *protocol);
+extern void udp_send_check(struct udphdr *uh, __u32 saddr,
+ __u32 daddr, int len, struct sock *sk);
+extern int udp_recvfrom(struct sock *sk, unsigned char *to,
+ int len, int noblock, unsigned flags,
+ struct sockaddr_in *sin, int *addr_len);
+extern int udp_read(struct sock *sk, unsigned char *buff,
+ int len, int noblock, unsigned flags);
+extern int udp_connect(struct sock *sk,
+ struct sockaddr_in *usin, int addr_len);
+extern int udp_rcv(struct sk_buff *skb, struct device *dev,
+ struct options *opt, __u32 daddr,
+ unsigned short len, __u32 saddr, int redo,
+ struct inet_protocol *protocol);
+extern int udp_ioctl(struct sock *sk, int cmd, unsigned long arg);
+extern void udp_cache_zap(void); /* Remove udp last socket cache */
+
+#endif /* _UDP_H */
diff --git a/i386/i386at/gpl/linux/linux_autoirq.c b/i386/i386at/gpl/linux/linux_autoirq.c
new file mode 100644
index 00000000..2e3d4e61
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_autoirq.c
@@ -0,0 +1,161 @@
+/*
+ * Linux auto-irq support.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * Written 1994 by Donald Becker.
+ *
+ * The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ * Center of Excellence in Space Data and Information Sciences
+ * Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+ *
+ * This code is a general-purpose IRQ line detector for devices with
+ * jumpered IRQ lines. If you can make the device raise an IRQ (and
+ * that IRQ line isn't already being used), these routines will tell
+ * you what IRQ line it's using -- perfect for those oh-so-cool boot-time
+ * device probes!
+ *
+ * To use this, first call autoirq_setup(timeout). TIMEOUT is how many
+ * 'jiffies' (1/100 sec.) to detect other devices that have active IRQ lines,
+ * and can usually be zero at boot. 'autoirq_setup()' returns the bit
+ * vector of nominally-available IRQ lines (lines may be physically in-use,
+ * but not yet registered to a device).
+ * Next, set up your device to trigger an interrupt.
+ * Finally call autoirq_report(TIMEOUT) to find out which IRQ line was
+ * most recently active. The TIMEOUT should usually be zero, but may
+ * be set to the number of jiffies to wait for a slow device to raise an IRQ.
+ *
+ * The idea of using the setup timeout to filter out bogus IRQs came from
+ * the serial driver.
+ */
+
+#include <i386/pic.h>
+#include <i386/ipl.h>
+
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+
+#include <asm/bitops.h>
+#include <asm/system.h>
+
+/*
+ * IRQ to network device map.
+ */
+void *irq2dev_map[16];
+
+/*
+ * Set of fixed IRQs
+ * (fpu, rtc, com1, PIC slave cascade, keyboard, timer).
+ */
+int irqs_busy = 0x2147;
+
+static volatile int irq_number; /* latest irq found */
+static volatile int irq_bitmap; /* bitmap of IRQs found */
+static int irq_handled; /* irq lines we have a handler on */
+
+extern unsigned long loops_per_sec;
+
+/*
+ * Interrupt handler when probing an IRQ.
+ */
+static void
+autoirq_probe(irq)
+ int irq;
+{
+ /*
+ * Mark this IRQ as the last one
+ * that interrupted and disable it.
+ */
+ irq_number = irq;
+ set_bit(irq, (void *)&irq_bitmap);
+ disable_irq(irq);
+}
+
+/*
+ * Set up for auto-irq.
+ */
+int
+autoirq_setup(waittime)
+ int waittime;
+{
+ int i, mask;
+ int timeout = jiffies + waittime;
+ int boguscount = (waittime * loops_per_sec) / 100;
+
+ /*
+ * Allocate all possible IRQs.
+ */
+ irq_handled = 0;
+ for (i = 0; i < 16; i++) {
+ if (test_bit(i, (void *)&irqs_busy) == 0
+ && request_irq(i, autoirq_probe, 0, 0) == 0)
+ set_bit(i, (void *)&irq_handled);
+ }
+
+ irq_number = 0;
+ irq_bitmap = 0;
+
+ /*
+ * Hang out at least <waittime>
+ * jiffies waiting for bogus IRQ hits.
+ */
+ while (timeout > jiffies && --boguscount > 0)
+ ;
+
+ /*
+ * Free IRQs that caused bogus hits.
+ */
+ for (i = 0, mask = 0x01; i < 16; i++, mask <<= 1) {
+ if (irq_bitmap & irq_handled & mask) {
+ irq_handled &= ~mask;
+ free_irq(i);
+ }
+ }
+
+ return (irq_handled);
+}
+
+/*
+ * Return the last IRQ that caused an interrupt.
+ */
+int
+autoirq_report(waittime)
+ int waittime;
+{
+ int i;
+ int timeout = jiffies + waittime;
+ int boguscount = (waittime * loops_per_sec) / 100;
+
+ /*
+ * Hang out at least <waittime>
+ * jiffies waiting for the IRQ.
+ */
+ while (timeout > jiffies && --boguscount > 0)
+ if (irq_number)
+ break;
+
+ /*
+ * Retract the IRQ handlers that we handled.
+ */
+ for (i = 0; i < 16; i++) {
+ if (test_bit(i, (void *)&irq_handled))
+ free_irq(i);
+ }
+
+ return (irq_number);
+}
diff --git a/i386/i386at/gpl/linux/linux_block.c b/i386/i386at/gpl/linux/linux_block.c
new file mode 100644
index 00000000..e06cc403
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_block.c
@@ -0,0 +1,2579 @@
+/*
+ * Linux block driver support.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+/*
+ * linux/drivers/block/ll_rw_blk.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ * Copyright (C) 1994, Karl Keyte: Added support for disk statistics
+ */
+
+/*
+ * linux/fs/block_dev.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * linux/fs/buffer.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <sys/types.h>
+#include <mach/mach_types.h>
+#include <mach/kern_return.h>
+#include <mach/mig_errors.h>
+#include <mach/port.h>
+#include <mach/vm_param.h>
+#include <mach/notify.h>
+
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <device/device_types.h>
+#include <device/device_port.h>
+#include <device/disk_status.h>
+#include "device_reply.h"
+
+#include <i386at/dev_hdr.h>
+#include <i386at/device_emul.h>
+#include <i386at/disk.h>
+
+#include <i386at/gpl/linux/linux_emul.h>
+
+#define MACH_INCLUDE
+#include <linux/fs.h>
+#include <linux/blk.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/major.h>
+#include <linux/kdev_t.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+
+/* Location of VTOC in units for sectors (512 bytes). */
+#define PDLOCATION 29
+
+/* Linux kernel variables. */
+
+/* One of these exists for each
+ driver associated with a major number. */
+struct device_struct
+{
+ const char *name; /* device name */
+ struct file_operations *fops; /* operations vector */
+ int busy:1; /* driver is being opened/closed */
+ int want:1; /* someone wants to open/close driver */
+ struct gendisk *gd; /* DOS partition information */
+ int *default_slice; /* what slice to use when none is given */
+ struct disklabel **label; /* disklabels for each DOS partition */
+};
+
+/* An entry in the Mach name to Linux major number conversion table. */
+struct name_map
+{
+ const char *name; /* Mach name for device */
+ unsigned major; /* Linux major number */
+ unsigned unit; /* Linux unit number */
+ int read_only; /* 1 if device is read only */
+};
+
+/* Driver operation table. */
+static struct device_struct blkdevs[MAX_BLKDEV];
+
+/* Driver request function table. */
+struct blk_dev_struct blk_dev[MAX_BLKDEV] =
+{
+ { NULL, NULL }, /* 0 no_dev */
+ { NULL, NULL }, /* 1 dev mem */
+ { NULL, NULL }, /* 2 dev fd */
+ { NULL, NULL }, /* 3 dev ide0 or hd */
+ { NULL, NULL }, /* 4 dev ttyx */
+ { NULL, NULL }, /* 5 dev tty */
+ { NULL, NULL }, /* 6 dev lp */
+ { NULL, NULL }, /* 7 dev pipes */
+ { NULL, NULL }, /* 8 dev sd */
+ { NULL, NULL }, /* 9 dev st */
+ { NULL, NULL }, /* 10 */
+ { NULL, NULL }, /* 11 */
+ { NULL, NULL }, /* 12 */
+ { NULL, NULL }, /* 13 */
+ { NULL, NULL }, /* 14 */
+ { NULL, NULL }, /* 15 */
+ { NULL, NULL }, /* 16 */
+ { NULL, NULL }, /* 17 */
+ { NULL, NULL }, /* 18 */
+ { NULL, NULL }, /* 19 */
+ { NULL, NULL }, /* 20 */
+ { NULL, NULL }, /* 21 */
+ { NULL, NULL } /* 22 dev ide1 */
+};
+
+/*
+ * blk_size contains the size of all block-devices in units of 1024 byte
+ * sectors:
+ *
+ * blk_size[MAJOR][MINOR]
+ *
+ * if (!blk_size[MAJOR]) then no minor size checking is done.
+ */
+int *blk_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * blksize_size contains the size of all block-devices:
+ *
+ * blksize_size[MAJOR][MINOR]
+ *
+ * if (!blksize_size[MAJOR]) then 1024 bytes is assumed.
+ */
+int *blksize_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/*
+ * hardsect_size contains the size of the hardware sector of a device.
+ *
+ * hardsect_size[MAJOR][MINOR]
+ *
+ * if (!hardsect_size[MAJOR])
+ * then 512 bytes is assumed.
+ * else
+ * sector_size is hardsect_size[MAJOR][MINOR]
+ * This is currently set by some scsi device and read by the msdos fs driver
+ * This might be a some uses later.
+ */
+int *hardsect_size[MAX_BLKDEV] = { NULL, NULL, };
+
+/* This specifies how many sectors to read ahead on the disk.
+ This is unused in Mach. It is here to make drivers compile. */
+int read_ahead[MAX_BLKDEV] = {0, };
+
+/* Use to wait on when there are no free requests.
+ This is unused in Mach. It is here to make drivers compile. */
+struct wait_queue *wait_for_request = NULL;
+
+/* Initialize block drivers. */
+void
+blk_dev_init ()
+{
+#ifdef CONFIG_BLK_DEV_IDE
+ ide_init ();
+#endif
+#ifdef CONFIG_BLK_DEV_FD
+ floppy_init ();
+#endif
+}
+
+/* Return 1 if major number MAJOR corresponds to a disk device. */
+static inline int
+disk_major (int major)
+{
+ return (major == IDE0_MAJOR
+ || major == IDE1_MAJOR
+ || major == IDE2_MAJOR
+ || major == IDE3_MAJOR
+ || major == SCSI_DISK_MAJOR);
+}
+
+/* Linux kernel block support routines. */
+
+/* Register a driver for major number MAJOR,
+ with name NAME, and operations vector FOPS. */
+int
+register_blkdev (unsigned major, const char *name,
+ struct file_operations *fops)
+{
+ int err = 0;
+
+ if (major == 0)
+ {
+ for (major = MAX_BLKDEV - 1; major > 0; major--)
+ if (blkdevs[major].fops == NULL)
+ goto out;
+ return -LINUX_EBUSY;
+ }
+ if (major >= MAX_BLKDEV)
+ return -LINUX_EINVAL;
+ if (blkdevs[major].fops && blkdevs[major].fops != fops)
+ return -LINUX_EBUSY;
+
+out:
+ blkdevs[major].name = name;
+ blkdevs[major].fops = fops;
+ blkdevs[major].busy = 0;
+ blkdevs[major].want = 0;
+ blkdevs[major].gd = NULL;
+ blkdevs[major].default_slice = NULL;
+ blkdevs[major].label = NULL;
+ return 0;
+}
+
+/* Unregister the driver associated with
+ major number MAJOR and having the name NAME. */
+int
+unregister_blkdev (unsigned major, const char *name)
+{
+ int err;
+
+ if (major >= MAX_BLKDEV)
+ return -LINUX_EINVAL;
+ if (! blkdevs[major].fops || strcmp (blkdevs[major].name, name))
+ return -LINUX_EINVAL;
+ blkdevs[major].fops = NULL;
+ if (blkdevs[major].default_slice)
+ {
+ assert (blkdevs[major].gd);
+ kfree ((vm_offset_t) blkdevs[major].default_slice,
+ sizeof (int) * blkdevs[major].gd->max_nr);
+ }
+ if (blkdevs[major].label)
+ {
+ assert (blkdevs[major].gd);
+ kfree ((vm_offset_t) blkdevs[major].label,
+ (sizeof (struct disklabel *)
+ * blkdevs[major].gd->max_p * blkdevs[major].gd->max_nr));
+ }
+ return 0;
+}
+
+/* One of these is associated with
+ each page allocated by the buffer management routines. */
+struct pagehdr
+{
+ unsigned char busy; /* page header is in use */
+ unsigned char avail; /* number of blocks available in page */
+ unsigned short bitmap; /* free space bitmap */
+ void *blks; /* the actual page */
+ struct pagehdr *next; /* next header in list */
+};
+
+/* This structure describes the different block sizes. */
+struct bufsize
+{
+ unsigned short size; /* size of block */
+ unsigned short avail; /* # available blocks */
+ struct pagehdr *pages; /* page list */
+};
+
+/* List of supported block sizes. */
+static struct bufsize bufsizes[] =
+{
+ { 512, 0, NULL },
+ { 1024, 0, NULL },
+ { 2048, 0, NULL },
+ { 4096, 0, NULL },
+};
+
+/* Page headers. */
+static struct pagehdr pagehdrs[50]; /* XXX: needs to be dynamic */
+
+/* Find the block size that is greater than or equal to SIZE. */
+static struct bufsize *
+get_bufsize (int size)
+{
+ struct bufsize *bs, *ebs;
+
+ bs = &bufsizes[0];
+ ebs = &bufsizes[sizeof (bufsizes) / sizeof (bufsizes[0])];
+ while (bs < ebs)
+ {
+ if (bs->size >= size)
+ return bs;
+ bs++;
+ }
+
+ panic ("%s:%d: alloc_buffer: bad buffer size %d", __FILE__, __LINE__, size);
+}
+
+/* Free all pages that are not in use.
+ Called by __get_free_pages when pages are running low. */
+void
+collect_buffer_pages ()
+{
+ struct bufsize *bs, *ebs;
+ struct pagehdr *ph, **prev_ph;
+
+ bs = &bufsizes[0];
+ ebs = &bufsizes[sizeof (bufsizes) / sizeof (bufsizes[0])];
+ while (bs < ebs)
+ {
+ if (bs->avail >= PAGE_SIZE / bs->size)
+ {
+ ph = bs->pages;
+ prev_ph = &bs->pages;
+ while (ph)
+ if (ph->avail == PAGE_SIZE / bs->size)
+ {
+ bs->avail -= ph->avail;
+ ph->busy = 0;
+ *prev_ph = ph->next;
+ free_pages ((unsigned long) ph->blks, 0);
+ ph = *prev_ph;
+ }
+ else
+ {
+ prev_ph = &ph->next;
+ ph = ph->next;
+ }
+ }
+ bs++;
+ }
+}
+
+/* Allocate a buffer of at least SIZE bytes. */
+static void *
+alloc_buffer (int size)
+{
+ int i;
+ unsigned flags;
+ struct bufsize *bs;
+ struct pagehdr *ph, *eph;
+
+ bs = get_bufsize (size);
+ save_flags (flags);
+ cli ();
+ if (bs->avail == 0)
+ {
+ ph = &pagehdrs[0];
+ eph = &pagehdrs[sizeof (pagehdrs) / sizeof (pagehdrs[0])];
+ while (ph < eph && ph->busy)
+ ph++;
+ if (ph == eph)
+ {
+ restore_flags (flags);
+ printf ("%s:%d: alloc_buffer: ran out of page headers\n",
+ __FILE__, __LINE__);
+ return NULL;
+ }
+ ph->blks = (void *) __get_free_pages (GFP_KERNEL, 0, ~0UL);
+ if (! ph->blks)
+ {
+ restore_flags (flags);
+ return NULL;
+ }
+ ph->busy = 1;
+ ph->avail = PAGE_SIZE / bs->size;
+ ph->bitmap = 0;
+ ph->next = bs->pages;
+ bs->pages = ph;
+ bs->avail += ph->avail;
+ }
+ for (ph = bs->pages; ph; ph = ph->next)
+ if (ph->avail)
+ for (i = 0; i < PAGE_SIZE / bs->size; i++)
+ if ((ph->bitmap & (1 << i)) == 0)
+ {
+ bs->avail--;
+ ph->avail--;
+ ph->bitmap |= 1 << i;
+ restore_flags (flags);
+ return ph->blks + i * bs->size;
+ }
+
+ panic ("%s:%d: alloc_buffer: list destroyed", __FILE__, __LINE__);
+}
+
+/* Free buffer P of SIZE bytes previously allocated by alloc_buffer. */
+static void
+free_buffer (void *p, int size)
+{
+ int i;
+ unsigned flags;
+ struct bufsize *bs;
+ struct pagehdr *ph;
+
+ bs = get_bufsize (size);
+ save_flags (flags);
+ cli ();
+ for (ph = bs->pages; ph; ph = ph->next)
+ if (p >= ph->blks && p < ph->blks + PAGE_SIZE)
+ break;
+ assert (ph);
+ i = (int) (p - ph->blks) / bs->size;
+ assert (ph->bitmap & (1 << i));
+ ph->bitmap &= ~(1 << i);
+ ph->avail++;
+ bs->avail++;
+ restore_flags (flags);
+}
+
+/* Allocate a buffer of SIZE bytes and
+ associate it with block number BLOCK of device DEV. */
+struct buffer_head *
+getblk (kdev_t dev, int block, int size)
+{
+ struct buffer_head *bh;
+
+ assert (size <= PAGE_SIZE);
+
+ bh = linux_kmalloc (sizeof (struct buffer_head), GFP_KERNEL);
+ if (! bh)
+ return NULL;
+ bh->b_data = alloc_buffer (size);
+ if (! bh->b_data)
+ {
+ linux_kfree (bh);
+ return NULL;
+ }
+ bh->b_dev = dev;
+ bh->b_size = size;
+ bh->b_state = 1 << BH_Lock;
+ bh->b_blocknr = block;
+ bh->b_page_list = NULL;
+ bh->b_request = NULL;
+ bh->b_reqnext = NULL;
+ bh->b_wait = NULL;
+ bh->b_sem = NULL;
+ return bh;
+}
+
+/* Release buffer BH previously allocated by getblk. */
+void
+__brelse (struct buffer_head *bh)
+{
+ if (bh->b_request)
+ linux_kfree (bh->b_request);
+ free_buffer (bh->b_data, bh->b_size);
+ linux_kfree (bh);
+}
+
+/* Check for I/O errors upon completion of I/O operation RW
+ on the buffer list BH. The number of buffers is NBUF.
+ Copy any data from bounce buffers and free them. */
+static int
+check_for_error (int rw, int nbuf, struct buffer_head **bh)
+{
+ int err;
+ struct request *req;
+
+ req = bh[0]->b_request;
+ if (! req)
+ {
+ while (--nbuf >= 0)
+ if (bh[nbuf]->b_page_list)
+ {
+ bh[nbuf]->b_page_list = NULL;
+ free_buffer (bh[nbuf]->b_data, bh[nbuf]->b_size);
+ }
+ return -LINUX_ENOMEM;
+ }
+
+ bh[0]->b_request = NULL;
+ err = 0;
+
+ while (--nbuf >= 0)
+ {
+ struct buffer_head *bhp = bh[nbuf];
+
+ if (bhp->b_page_list)
+ {
+ if (rw == READ && buffer_uptodate (bhp))
+ {
+ int amt;
+ vm_page_t *pages = bhp->b_page_list;
+
+ amt = PAGE_SIZE - bhp->b_off;
+ if (amt > bhp->b_usrcnt)
+ amt = bhp->b_usrcnt;
+ memcpy ((void *) pages[bhp->b_index]->phys_addr + bhp->b_off,
+ bhp->b_data, amt);
+ if (amt < bhp->b_usrcnt)
+ memcpy ((void *) pages[bhp->b_index + 1]->phys_addr,
+ bhp->b_data + amt, bhp->b_usrcnt - amt);
+ }
+ bhp->b_page_list = NULL;
+ free_buffer (bhp->b_data, bhp->b_size);
+ }
+ if (! buffer_uptodate (bhp))
+ err = -LINUX_EIO;
+ }
+
+ linux_kfree (req);
+ return err;
+}
+
+/* Allocate a buffer of SIZE bytes and fill it with data
+ from device DEV starting at block number BLOCK. */
+struct buffer_head *
+bread (kdev_t dev, int block, int size)
+{
+ int err;
+ struct buffer_head *bh;
+
+ bh = getblk (dev, block, size);
+ if (! bh)
+ return NULL;
+ ll_rw_block (READ, 1, &bh);
+ wait_on_buffer (bh);
+ err = check_for_error (READ, 1, &bh);
+ if (err)
+ {
+ __brelse (bh);
+ return NULL;
+ }
+ return bh;
+}
+
+/* Return the block size for device DEV in *BSIZE and
+ log2(block size) in *BSHIFT. */
+static inline void
+get_block_size (kdev_t dev, int *bsize, int *bshift)
+{
+ int i, size, shift;
+
+ size = BLOCK_SIZE;
+ if (blksize_size[MAJOR (dev)]
+ && blksize_size[MAJOR (dev)][MINOR (dev)])
+ size = blksize_size[MAJOR (dev)][MINOR (dev)];
+ for (i = size, shift = 0; i != 1; shift++, i >>= 1)
+ ;
+ *bsize = size;
+ *bshift = shift;
+}
+
+/* Enqueue request REQ on a driver's queue. */
+static inline void
+enqueue_request (struct request *req)
+{
+ struct request *tmp;
+ struct blk_dev_struct *dev;
+
+ dev = blk_dev + MAJOR (req->rq_dev);
+ cli ();
+ tmp = dev->current_request;
+ if (! tmp)
+ {
+ dev->current_request = req;
+ (*dev->request_fn) ();
+ sti ();
+ return;
+ }
+ while (tmp->next)
+ {
+ if ((IN_ORDER (tmp, req) || ! IN_ORDER (tmp, tmp->next))
+ && IN_ORDER (req, tmp->next))
+ break;
+ tmp = tmp->next;
+ }
+ req->next = tmp->next;
+ tmp->next = req;
+ if (scsi_major (MAJOR (req->rq_dev)))
+ (*dev->request_fn) ();
+ sti ();
+}
+
+/* Perform the I/O operation RW on the buffer list BH
+ containing NR buffers. */
+void
+ll_rw_block (int rw, int nr, struct buffer_head **bh)
+{
+ int i, bsize, bshift;
+ unsigned major;
+ struct request *r;
+
+ r = (struct request *) linux_kmalloc (sizeof (struct request), GFP_KERNEL);
+ if (! r)
+ {
+ bh[0]->b_request = NULL;
+ return;
+ }
+ bh[0]->b_request = r;
+
+ major = MAJOR (bh[0]->b_dev);
+ assert (major < MAX_BLKDEV);
+
+ get_block_size (bh[0]->b_dev, &bsize, &bshift);
+ assert (bsize <= PAGE_SIZE);
+
+ for (i = 0, r->nr_sectors = 0; i < nr - 1; i++)
+ {
+ r->nr_sectors += bh[i]->b_size >> 9;
+ bh[i]->b_reqnext = bh[i + 1];
+ }
+ r->nr_sectors += bh[i]->b_size >> 9;
+ bh[i]->b_reqnext = NULL;
+
+ r->rq_status = RQ_ACTIVE;
+ r->rq_dev = bh[0]->b_dev;
+ r->cmd = rw;
+ r->errors = 0;
+ r->sector = bh[0]->b_blocknr << (bshift - 9);
+ r->current_nr_sectors = bh[0]->b_size >> 9;
+ r->buffer = bh[0]->b_data;
+ r->sem = bh[0]->b_sem;
+ r->bh = bh[0];
+ r->bhtail = bh[nr - 1];
+ r->next = NULL;
+
+ enqueue_request (r);
+}
+
+/* Maximum amount of data to write per invocation of the driver. */
+#define WRITE_MAXPHYS (VM_MAP_COPY_PAGE_LIST_MAX << PAGE_SHIFT)
+#define WRITE_MAXPHYSPG (WRITE_MAXPHYS >> PAGE_SHIFT)
+
+int linux_block_write_trace = 0;
+
+/* Write COUNT bytes of data from user buffer BUF
+ to device specified by INODE at location specified by FILP. */
+int
+block_write (struct inode *inode, struct file *filp,
+ const char *buf, int count)
+{
+ char *p;
+ int i, bsize, bmask, bshift;
+ int err = 0, have_page_list = 1;
+ int resid = count, unaligned;
+ int page_index, pages, amt, cnt, nbuf;
+ unsigned blk;
+ vm_map_copy_t copy;
+ struct request req;
+ struct semaphore sem;
+ struct name_map *np = filp->f_np;
+ struct buffer_head *bh, *bhp, **bhlist;
+
+ /* Compute device block size. */
+ get_block_size (inode->i_rdev, &bsize, &bshift);
+ assert (bsize <= PAGE_SIZE);
+ bmask = bsize - 1;
+
+ copy = (vm_map_copy_t) buf;
+ assert (copy);
+ assert (copy->type == VM_MAP_COPY_PAGE_LIST);
+
+ p = (char *) copy->offset;
+ pages = copy->cpy_npages;
+ blk = (filp->f_pos + bmask) >> bshift;
+
+ if (linux_block_write_trace)
+ printf ("block_write: at %d: f_pos 0x%x, count %d, blk 0x%x, p 0x%x\n",
+ __LINE__, (unsigned) filp->f_pos, count, blk, p);
+
+ /* Allocate buffer headers. */
+ nbuf = ((round_page ((vm_offset_t) p + resid) - trunc_page ((vm_offset_t) p))
+ >> PAGE_SHIFT);
+ if (nbuf > WRITE_MAXPHYSPG)
+ nbuf = WRITE_MAXPHYSPG;
+ if ((filp->f_pos & bmask) || ((int) p & PAGE_MASK))
+ nbuf *= 2;
+ bh = (struct buffer_head *) kalloc ((sizeof (*bh) + sizeof (*bhlist))
+ * nbuf);
+ if (! bh)
+ {
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhlist = (struct buffer_head **) (bh + nbuf);
+
+ /* Write any partial block. */
+ if (filp->f_pos & bmask)
+ {
+ char *b, *q;
+ int use_req;
+
+ use_req = (disk_major (MAJOR (inode->i_rdev)) && ! np->read_only);
+
+ amt = bsize - (filp->f_pos & bmask);
+ if (amt > resid)
+ amt = resid;
+
+ if (linux_block_write_trace)
+ printf ("block_write: at %d: amt %d, resid %d\n",
+ __LINE__, amt, resid);
+
+ if (use_req)
+ {
+ i = (amt + 511) & ~511;
+ req.buffer = b = alloc_buffer (i);
+ if (! b)
+ {
+ printf ("%s:%d: block_write: ran out of buffers\n",
+ __FILE__, __LINE__);
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ req.sector = filp->f_pos >> 9;
+ req.nr_sectors = i >> 9;
+ req.current_nr_sectors = i >> 9;
+ req.rq_status = RQ_ACTIVE;
+ req.rq_dev = inode->i_rdev;
+ req.cmd = READ;
+ req.errors = 0;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ enqueue_request (&req);
+ __down (&sem);
+
+ if (req.errors)
+ {
+ free_buffer (b, i);
+ err = -LINUX_EIO;
+ goto out;
+ }
+ q = b + (filp->f_pos & 511);
+ }
+ else
+ {
+ i = bsize;
+ bhp = bh;
+ bhp->b_data = b = alloc_buffer (i);
+ if (! b)
+ {
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhp->b_blocknr = filp->f_pos >> bshift;
+ bhp->b_dev = inode->i_rdev;
+ bhp->b_size = bsize;
+ bhp->b_state = 1 << BH_Lock;
+ bhp->b_page_list = NULL;
+ bhp->b_request = NULL;
+ bhp->b_reqnext = NULL;
+ bhp->b_wait = NULL;
+ bhp->b_sem = NULL;
+
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ free_buffer (b, i);
+ goto out;
+ }
+ q = b + (filp->f_pos & bmask);
+ }
+
+ cnt = PAGE_SIZE - ((int) p & PAGE_MASK);
+ if (cnt > amt)
+ cnt = amt;
+ memcpy (q, ((void *) copy->cpy_page_list[0]->phys_addr
+ + ((int) p & PAGE_MASK)),
+ cnt);
+ if (cnt < amt)
+ {
+ assert (copy->cpy_npages >= 2);
+ memcpy (q + cnt,
+ (void *) copy->cpy_page_list[1]->phys_addr, amt - cnt);
+ }
+ else
+ assert (copy->cpy_npages >= 1);
+
+ if (use_req)
+ {
+ req.buffer = b;
+ req.sector = filp->f_pos >> 9;
+ req.nr_sectors = i >> 9;
+ req.current_nr_sectors = i >> 9;
+ req.rq_status = RQ_ACTIVE;
+ req.rq_dev = inode->i_rdev;
+ req.cmd = WRITE;
+ req.errors = 0;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ enqueue_request (&req);
+ __down (&sem);
+
+ if (req.errors)
+ err = -LINUX_EIO;
+ }
+ else
+ {
+ bhp->b_state = (1 << BH_Dirty) | (1 << BH_Lock);
+ ll_rw_block (WRITE, 1, &bhp);
+ err = check_for_error (WRITE, 1, &bhp);
+ }
+ free_buffer (b, i);
+ if (err)
+ {
+ if (linux_block_write_trace)
+ printf ("block_write: at %d\n", __LINE__);
+
+ goto out;
+ }
+ resid -= amt;
+ if (resid == 0)
+ goto out;
+ p += amt;
+ }
+
+ unaligned = (int) p & 511;
+
+ /* Write full blocks. */
+ while (resid > bsize)
+ {
+ assert (have_page_list == 1);
+
+ /* Construct buffer list. */
+ for (i = 0, bhp = bh; resid > bsize && i < nbuf; i++, bhp++)
+ {
+ page_index = ((trunc_page ((vm_offset_t) p)
+ - trunc_page (copy->offset))
+ >> PAGE_SHIFT);
+
+ if (page_index == pages)
+ break;
+
+ bhlist[i] = bhp;
+ bhp->b_dev = inode->i_rdev;
+ bhp->b_state = (1 << BH_Dirty) | (1 << BH_Lock);
+ bhp->b_blocknr = blk;
+ bhp->b_wait = NULL;
+ bhp->b_page_list = NULL;
+ bhp->b_sem = &sem;
+
+ cnt = PAGE_SIZE - ((int) p & PAGE_MASK);
+ if (! unaligned && cnt >= bsize)
+ {
+ if (cnt > resid)
+ cnt = resid;
+ bhp->b_size = cnt & ~bmask;
+ bhp->b_data = (((char *)
+ copy->cpy_page_list[page_index]->phys_addr)
+ + ((int) p & PAGE_MASK));
+ }
+ else
+ {
+ if (cnt < bsize)
+ {
+ if (page_index == pages - 1)
+ break;
+ bhp->b_size = bsize;
+ }
+ else
+ {
+ bhp->b_size = cnt;
+ if (bhp->b_size > resid)
+ bhp->b_size = resid;
+ bhp->b_size &= ~bmask;
+ }
+ bhp->b_data = alloc_buffer (bhp->b_size);
+ if (! bhp->b_data)
+ {
+ printf ("%s:%d: block_write: ran out of buffers\n",
+ __FILE__, __LINE__);
+ while (--i >= 0)
+ if (bhlist[i]->b_page_list)
+ free_buffer (bhlist[i]->b_data, bhlist[i]->b_size);
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhp->b_page_list = (void *) 1;
+ if (cnt > bhp->b_size)
+ cnt = bhp->b_size;
+ memcpy (bhp->b_data,
+ ((void *) copy->cpy_page_list[page_index]->phys_addr
+ + ((int) p & PAGE_MASK)),
+ cnt);
+ if (cnt < bhp->b_size)
+ memcpy (bhp->b_data + cnt,
+ ((void *)
+ copy->cpy_page_list[page_index + 1]->phys_addr),
+ bhp->b_size - cnt);
+ }
+
+ p += bhp->b_size;
+ resid -= bhp->b_size;
+ blk += bhp->b_size >> bshift;
+ }
+
+ assert (i > 0);
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ /* Do the write. */
+ ll_rw_block (WRITE, i, bhlist);
+ __down (&sem);
+ err = check_for_error (WRITE, i, bhlist);
+ if (err || resid == 0)
+ goto out;
+
+ /* Discard current page list. */
+ vm_map_copy_discard (copy);
+ have_page_list = 0;
+
+ /* Compute # pages to wire down. */
+ pages = ((round_page ((vm_offset_t) p + resid)
+ - trunc_page ((vm_offset_t) p))
+ >> PAGE_SHIFT);
+ if (pages > WRITE_MAXPHYSPG)
+ pages = WRITE_MAXPHYSPG;
+
+ /* Wire down user pages and get page list. */
+ err = vm_map_copyin_page_list (current_map (),
+ trunc_page ((vm_offset_t) p),
+ pages << PAGE_SHIFT, FALSE,
+ FALSE, &copy, FALSE);
+ if (err)
+ {
+ if (err == KERN_INVALID_ADDRESS || err == KERN_PROTECTION_FAILURE)
+ err = -LINUX_EINVAL;
+ else
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+
+ assert (pages == copy->cpy_npages);
+ assert (! vm_map_copy_has_cont (copy));
+
+ have_page_list = 1;
+ }
+
+ /* Write any partial count. */
+ if (resid > 0)
+ {
+ char *b;
+ int use_req;
+
+ assert (have_page_list);
+ assert (pages >= 1);
+
+ use_req = (disk_major (MAJOR (inode->i_rdev)) && ! np->read_only);
+
+ if (linux_block_write_trace)
+ printf ("block_write: at %d: resid %d\n", __LINE__, resid);
+
+ if (use_req)
+ {
+ i = (resid + 511) & ~511;
+ req.buffer = b = alloc_buffer (i);
+ if (! b)
+ {
+ printf ("%s:%d: block_write: ran out of buffers\n",
+ __FILE__, __LINE__);
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ req.sector = blk << (bshift - 9);
+ req.nr_sectors = i >> 9;
+ req.current_nr_sectors = i >> 9;
+ req.rq_status = RQ_ACTIVE;
+ req.rq_dev = inode->i_rdev;
+ req.cmd = READ;
+ req.errors = 0;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ enqueue_request (&req);
+ __down (&sem);
+
+ if (req.errors)
+ {
+ free_buffer (b, i);
+ err = -LINUX_EIO;
+ goto out;
+ }
+ }
+ else
+ {
+ i = bsize;
+ bhp = bh;
+ bhp->b_data = b = alloc_buffer (i);
+ if (! b)
+ {
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhp->b_blocknr = blk;
+ bhp->b_dev = inode->i_rdev;
+ bhp->b_size = bsize;
+ bhp->b_state = 1 << BH_Lock;
+ bhp->b_page_list = NULL;
+ bhp->b_request = NULL;
+ bhp->b_reqnext = NULL;
+ bhp->b_wait = NULL;
+ bhp->b_sem = NULL;
+
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ free_buffer (b, i);
+ goto out;
+ }
+ }
+
+ page_index = ((trunc_page ((vm_offset_t) p) - trunc_page (copy->offset))
+ >> PAGE_SHIFT);
+ cnt = PAGE_SIZE - ((int) p & PAGE_MASK);
+ if (cnt > resid)
+ cnt = resid;
+ memcpy (b, ((void *) copy->cpy_page_list[page_index]->phys_addr
+ + ((int) p & PAGE_MASK)),
+ cnt);
+ if (cnt < resid)
+ {
+ assert (copy->cpy_npages >= 2);
+ memcpy (b + cnt,
+ (void *) copy->cpy_page_list[page_index + 1]->phys_addr,
+ resid - cnt);
+ }
+ else
+ assert (copy->cpy_npages >= 1);
+
+ if (use_req)
+ {
+ req.buffer = b;
+ req.sector = blk << (bshift - 9);
+ req.nr_sectors = i >> 9;
+ req.current_nr_sectors = i >> 9;
+ req.rq_status = RQ_ACTIVE;
+ req.rq_dev = inode->i_rdev;
+ req.cmd = WRITE;
+ req.errors = 0;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ enqueue_request (&req);
+ __down (&sem);
+
+ if (req.errors)
+ err = -LINUX_EIO;
+ }
+ else
+ {
+ bhp->b_state = (1 << BH_Dirty) | (1 << BH_Lock);
+ ll_rw_block (WRITE, 1, &bhp);
+ err = check_for_error (WRITE, 1, &bhp);
+ }
+ free_buffer (b, i);
+ if (! err)
+ resid = 0;
+ }
+
+out:
+ if (have_page_list)
+ vm_map_copy_discard (copy);
+ if (bh)
+ kfree ((vm_offset_t) bh,
+ (sizeof (*bh) + sizeof (*bhlist)) * nbuf);
+ filp->f_resid = resid;
+ return err;
+}
+
+int linux_block_read_trace = 0;
+#define LINUX_BLOCK_READ_TRACE (linux_block_read_trace == -1 \
+ || linux_block_read_trace == inode->i_rdev)
+
+/* Maximum amount of data to read per driver invocation. */
+#define READ_MAXPHYS (64*1024)
+#define READ_MAXPHYSPG (READ_MAXPHYS >> PAGE_SHIFT)
+
+/* Read COUNT bytes of data into user buffer BUF
+ from device specified by INODE from location specified by FILP. */
+int
+block_read (struct inode *inode, struct file *filp, char *buf, int count)
+{
+ int err = 0, resid = count;
+ int i, bsize, bmask, bshift;
+ int pages, amt, unaligned;
+ int page_index, nbuf;
+ int have_page_list = 0;
+ unsigned blk;
+ vm_offset_t off, wire_offset, offset;
+ vm_object_t object;
+ vm_page_t *page_list;
+ struct request req;
+ struct semaphore sem;
+ struct name_map *np = filp->f_np;
+ struct buffer_head *bh, *bhp, **bhlist;
+
+ /* Get device block size. */
+ get_block_size (inode->i_rdev, &bsize, &bshift);
+ assert (bsize <= PAGE_SIZE);
+ bmask = bsize - 1;
+
+ off = 0;
+ blk = (filp->f_pos + bmask) >> bshift;
+
+ /* Allocate buffer headers. */
+ nbuf = round_page (count) >> PAGE_SHIFT;
+ if (nbuf > READ_MAXPHYSPG)
+ nbuf = READ_MAXPHYSPG;
+ if (filp->f_pos & bmask)
+ nbuf *= 2;
+ bh = (struct buffer_head *) kalloc ((sizeof (*bh) + sizeof (*bhlist)) * nbuf
+ + sizeof (*page_list) * READ_MAXPHYSPG);
+ if (! bh)
+ return -LINUX_ENOMEM;
+ bhlist = (struct buffer_head **) (bh + nbuf);
+ page_list = (vm_page_t *) (bhlist + nbuf);
+
+ /* Allocate an object to hold the data. */
+ object = vm_object_allocate (round_page (count));
+ if (! object)
+ {
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+
+ /* Compute number of pages to be wired at a time. */
+ pages = round_page (count) >> PAGE_SHIFT;
+ if (pages > READ_MAXPHYSPG)
+ pages = READ_MAXPHYSPG;
+
+ /* Allocate and wire down pages in the object. */
+ for (i = 0, wire_offset = offset = 0; i < pages; i++, offset += PAGE_SIZE)
+ {
+ while (1)
+ {
+ page_list[i] = vm_page_grab ();
+ if (page_list[i])
+ {
+ assert (page_list[i]->busy);
+ assert (! page_list[i]->wanted);
+ break;
+ }
+ vm_page_wait (NULL);
+ }
+ vm_object_lock (object);
+ vm_page_lock_queues ();
+ assert (! vm_page_lookup (object, offset));
+ vm_page_insert (page_list[i], object, offset);
+ assert (page_list[i]->wire_count == 0);
+ vm_page_wire (page_list[i]);
+ vm_page_unlock_queues ();
+ vm_object_unlock (object);
+ }
+ have_page_list = 1;
+
+ /* Read any partial block. */
+ if (filp->f_pos & bmask)
+ {
+ char *b, *q;
+ int use_req;
+
+ use_req = (disk_major (MAJOR (inode->i_rdev)) && ! np->read_only);
+
+ amt = bsize - (filp->f_pos & bmask);
+ if (amt > resid)
+ amt = resid;
+
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("block_read: at %d: amt %d, resid %d\n",
+ __LINE__, amt, resid);
+
+ if (use_req)
+ {
+ i = (amt + 511) & ~511;
+ req.buffer = b = alloc_buffer (i);
+ if (! b)
+ {
+ printf ("%s:%d: block_read: ran out of buffers\n",
+ __FILE__, __LINE__);
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ req.sector = filp->f_pos >> 9;
+ req.nr_sectors = i >> 9;
+ req.current_nr_sectors = i >> 9;
+ req.rq_status = RQ_ACTIVE;
+ req.rq_dev = inode->i_rdev;
+ req.cmd = READ;
+ req.errors = 0;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ enqueue_request (&req);
+ __down (&sem);
+
+ if (req.errors)
+ {
+ free_buffer (b, i);
+ err = -LINUX_EIO;
+ goto out;
+ }
+ q = b + (filp->f_pos & 511);
+ }
+ else
+ {
+ i = bsize;
+ bhp = bh;
+ bhp->b_data = b = alloc_buffer (i);
+ if (! b)
+ {
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhp->b_blocknr = filp->f_pos >> bshift;
+ bhp->b_dev = inode->i_rdev;
+ bhp->b_size = bsize;
+ bhp->b_state = 1 << BH_Lock;
+ bhp->b_page_list = NULL;
+ bhp->b_request = NULL;
+ bhp->b_reqnext = NULL;
+ bhp->b_wait = NULL;
+ bhp->b_sem = NULL;
+
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ free_buffer (b, i);
+ goto out;
+ }
+ q = b + (filp->f_pos & bmask);
+ }
+
+ memcpy ((void *) page_list[0]->phys_addr, q, amt);
+
+ free_buffer (b, i);
+ resid -= amt;
+ if (resid == 0)
+ {
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("block_read: at %d\n", __LINE__);
+
+ assert (pages == 1);
+ goto out;
+ }
+ off += amt;
+ }
+
+ unaligned = off & 511;
+
+ /* Read full blocks. */
+ while (resid > bsize)
+ {
+ /* Construct buffer list to hand to the driver. */
+ for (i = 0, bhp = bh; resid > bsize && i < nbuf; bhp++, i++)
+ {
+ if (off == wire_offset + (pages << PAGE_SHIFT))
+ break;
+
+ bhlist[i] = bhp;
+ bhp->b_dev = inode->i_rdev;
+ bhp->b_state = 1 << BH_Lock;
+ bhp->b_blocknr = blk;
+ bhp->b_wait = NULL;
+ bhp->b_sem = &sem;
+
+ page_index = (trunc_page (off) - wire_offset) >> PAGE_SHIFT;
+ amt = PAGE_SIZE - (off & PAGE_MASK);
+ if (! unaligned && amt >= bsize)
+ {
+ if (amt > resid)
+ amt = resid;
+ bhp->b_size = amt & ~bmask;
+ bhp->b_data = ((char *) page_list[page_index]->phys_addr
+ + (off & PAGE_MASK));
+ bhp->b_page_list = NULL;
+ }
+ else
+ {
+ if (amt < bsize)
+ {
+ if (page_index == pages - 1)
+ {
+ assert (round_page (count) - off >= resid);
+ break;
+ }
+ bhp->b_size = bsize;
+ }
+ else
+ {
+ if (amt > resid)
+ amt = resid;
+ bhp->b_size = amt & ~bmask;
+ }
+ bhp->b_data = alloc_buffer (bhp->b_size);
+ if (! bhp->b_data)
+ {
+ printf ("%s:%d: block_read: ran out of buffers\n",
+ __FILE__, __LINE__);
+
+ while (--i >= 0)
+ if (bhp->b_page_list)
+ free_buffer (bhp->b_data, bhp->b_size);
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhp->b_page_list = page_list;
+ bhp->b_index = page_index;
+ bhp->b_off = off & PAGE_MASK;
+ bhp->b_usrcnt = bhp->b_size;
+ }
+
+ resid -= bhp->b_size;
+ off += bhp->b_size;
+ blk += bhp->b_size >> bshift;
+ }
+
+ assert (i > 0);
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ /* Do the read. */
+ ll_rw_block (READ, i, bhlist);
+ __down (&sem);
+ err = check_for_error (READ, i, bhlist);
+ if (err || resid == 0)
+ goto out;
+
+ /* Unwire the pages and mark them dirty. */
+ offset = trunc_page (off);
+ for (i = 0; wire_offset < offset; i++, wire_offset += PAGE_SIZE)
+ {
+ vm_object_lock (object);
+ vm_page_lock_queues ();
+ assert (vm_page_lookup (object, wire_offset) == page_list[i]);
+ assert (page_list[i]->wire_count == 1);
+ assert (! page_list[i]->active && ! page_list[i]->inactive);
+ assert (! page_list[i]->reference);
+ page_list[i]->dirty = TRUE;
+ page_list[i]->reference = TRUE;
+ page_list[i]->busy = FALSE;
+ vm_page_unwire (page_list[i]);
+ vm_page_unlock_queues ();
+ vm_object_unlock (object);
+ }
+
+ assert (i <= pages);
+
+ /* Wire down the next chunk of the object. */
+ if (i == pages)
+ {
+ i = 0;
+ offset = wire_offset;
+ have_page_list = 0;
+ }
+ else
+ {
+ int j;
+
+ for (j = 0; i < pages; page_list[j++] = page_list[i++])
+ offset += PAGE_SIZE;
+ i = j;
+ }
+ pages = (round_page (count) - wire_offset) >> PAGE_SHIFT;
+ if (pages > READ_MAXPHYSPG)
+ pages = READ_MAXPHYSPG;
+ while (i < pages)
+ {
+ while (1)
+ {
+ page_list[i] = vm_page_grab ();
+ if (page_list[i])
+ {
+ assert (page_list[i]->busy);
+ assert (! page_list[i]->wanted);
+ break;
+ }
+ vm_page_wait (NULL);
+ }
+ vm_object_lock (object);
+ vm_page_lock_queues ();
+ assert (! vm_page_lookup (object, offset));
+ vm_page_insert (page_list[i], object, offset);
+ assert (page_list[i]->wire_count == 0);
+ vm_page_wire (page_list[i]);
+ vm_page_unlock_queues ();
+ vm_object_unlock (object);
+ i++;
+ offset += PAGE_SIZE;
+ }
+ have_page_list = 1;
+ }
+
+ /* Read any partial count. */
+ if (resid > 0)
+ {
+ char *b;
+ int use_req;
+
+ assert (have_page_list);
+ assert (pages >= 1);
+
+ use_req = (disk_major (MAJOR (inode->i_rdev)) && ! np->read_only);
+
+ amt = bsize - (filp->f_pos & bmask);
+ if (amt > resid)
+ amt = resid;
+
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("block_read: at %d: resid %d\n", __LINE__, amt, resid);
+
+ if (use_req)
+ {
+ i = (resid + 511) & ~511;
+ req.buffer = b = alloc_buffer (i);
+ if (! b)
+ {
+ printf ("%s:%d: block_read: ran out of buffers\n",
+ __FILE__, __LINE__);
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ req.sector = blk << (bshift - 9);
+ req.nr_sectors = i >> 9;
+ req.current_nr_sectors = i >> 9;
+ req.rq_status = RQ_ACTIVE;
+ req.rq_dev = inode->i_rdev;
+ req.cmd = READ;
+ req.errors = 0;
+ req.sem = &sem;
+ req.bh = NULL;
+ req.bhtail = NULL;
+ req.next = NULL;
+
+ sem.count = 0;
+ sem.wait = NULL;
+
+ enqueue_request (&req);
+ __down (&sem);
+
+ if (req.errors)
+ {
+ free_buffer (b, i);
+ err = -LINUX_EIO;
+ goto out;
+ }
+ }
+ else
+ {
+ i = bsize;
+ bhp = bh;
+ bhp->b_data = b = alloc_buffer (i);
+ if (! b)
+ {
+ err = -LINUX_ENOMEM;
+ goto out;
+ }
+ bhp->b_blocknr = blk;
+ bhp->b_dev = inode->i_rdev;
+ bhp->b_size = bsize;
+ bhp->b_state = 1 << BH_Lock;
+ bhp->b_page_list = NULL;
+ bhp->b_request = NULL;
+ bhp->b_reqnext = NULL;
+ bhp->b_wait = NULL;
+ bhp->b_sem = NULL;
+
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ free_buffer (b, i);
+ goto out;
+ }
+ }
+
+ page_index = (trunc_page (off) - wire_offset) >> PAGE_SHIFT;
+ amt = PAGE_SIZE - (off & PAGE_MASK);
+ if (amt > resid)
+ amt = resid;
+ memcpy (((void *) page_list[page_index]->phys_addr
+ + (off & PAGE_MASK)),
+ b, amt);
+ if (amt < resid)
+ {
+ assert (pages >= 2);
+ memcpy ((void *) page_list[page_index + 1]->phys_addr,
+ b + amt, resid - amt);
+ }
+ else
+ assert (pages >= 1);
+
+ free_buffer (b, i);
+ }
+
+out:
+ if (have_page_list)
+ {
+ for (i = 0; i < pages; i++, wire_offset += PAGE_SIZE)
+ {
+ vm_object_lock (object);
+ vm_page_lock_queues ();
+ assert (vm_page_lookup (object, wire_offset) == page_list[i]);
+ assert (page_list[i]->wire_count == 1);
+ assert (! page_list[i]->active && ! page_list[i]->inactive);
+ assert (! page_list[i]->reference);
+ page_list[i]->dirty = TRUE;
+ page_list[i]->reference = TRUE;
+ page_list[i]->busy = FALSE;
+ vm_page_unwire (page_list[i]);
+ vm_page_unlock_queues ();
+ vm_object_unlock (object);
+ }
+ }
+ kfree ((vm_offset_t) bh,
+ ((sizeof (*bh) + sizeof (*bhlist)) * nbuf
+ + sizeof (*page_list) * READ_MAXPHYSPG));
+ if (err)
+ {
+ if (object)
+ {
+ assert (object->ref_count == 1);
+ vm_object_deallocate (object);
+ }
+ }
+ else
+ {
+ assert (object);
+ assert (object->ref_count == 1);
+
+ filp->f_resid = 0;
+ filp->f_object = object;
+ }
+
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("block_read: at %d: err %d\n", __LINE__, err);
+
+ return err;
+}
+
+/*
+ * This routine checks whether a removable media has been changed,
+ * and invalidates all buffer-cache-entries in that case. This
+ * is a relatively slow routine, so we have to try to minimize using
+ * it. Thus it is called only upon a 'mount' or 'open'. This
+ * is the best way of combining speed and utility, I think.
+ * People changing diskettes in the middle of an operation deserve
+ * to loose :-)
+ */
+int
+check_disk_change (kdev_t dev)
+{
+ unsigned i;
+ struct file_operations * fops;
+
+ i = MAJOR(dev);
+ if (i >= MAX_BLKDEV || (fops = blkdevs[i].fops) == NULL)
+ return 0;
+ if (fops->check_media_change == NULL)
+ return 0;
+ if (! (*fops->check_media_change) (dev))
+ return 0;
+
+ printf ("Disk change detected on device %s\n", kdevname(dev));
+
+ if (fops->revalidate)
+ (*fops->revalidate) (dev);
+
+ return 1;
+}
+
+/* Mach device interface routines. */
+
+/* Mach name to Linux major/minor number mapping table. */
+static struct name_map name_to_major[] =
+{
+ /* IDE disks */
+ { "hd0", IDE0_MAJOR, 0, 0 },
+ { "hd1", IDE0_MAJOR, 1, 0 },
+ { "hd2", IDE1_MAJOR, 0, 0 },
+ { "hd3", IDE1_MAJOR, 1, 0 },
+ { "hd4", IDE2_MAJOR, 0, 0 },
+ { "hd5", IDE2_MAJOR, 1, 0 },
+ { "hd6", IDE3_MAJOR, 0, 0 },
+ { "hd7", IDE3_MAJOR, 1, 0 },
+
+ /* IDE CDROMs */
+ { "wcd0", IDE0_MAJOR, 0, 1 },
+ { "wcd1", IDE0_MAJOR, 1, 1 },
+ { "wcd2", IDE1_MAJOR, 0, 1 },
+ { "wcd3", IDE1_MAJOR, 1, 1 },
+ { "wcd4", IDE2_MAJOR, 0, 1 },
+ { "wcd5", IDE2_MAJOR, 1, 1 },
+ { "wcd6", IDE3_MAJOR, 0, 1 },
+ { "wcd7", IDE3_MAJOR, 1, 1 },
+
+ /* SCSI disks */
+ { "sd0", SCSI_DISK_MAJOR, 0, 0 },
+ { "sd1", SCSI_DISK_MAJOR, 1, 0 },
+ { "sd2", SCSI_DISK_MAJOR, 2, 0 },
+ { "sd3", SCSI_DISK_MAJOR, 3, 0 },
+ { "sd4", SCSI_DISK_MAJOR, 4, 0 },
+ { "sd5", SCSI_DISK_MAJOR, 5, 0 },
+ { "sd6", SCSI_DISK_MAJOR, 6, 0 },
+ { "sd7", SCSI_DISK_MAJOR, 7, 0 },
+
+ /* SCSI CDROMs */
+ { "cd0", SCSI_CDROM_MAJOR, 0, 1 },
+ { "cd1", SCSI_CDROM_MAJOR, 1, 1 },
+
+ /* Floppy disks */
+ { "fd0", FLOPPY_MAJOR, 0, 0 },
+ { "fd1", FLOPPY_MAJOR, 1, 0 },
+};
+
+#define NUM_NAMES (sizeof (name_to_major) / sizeof (name_to_major[0]))
+
+/* One of these is associated with each open instance of a device. */
+struct block_data
+{
+ const char *name; /* Mach name for device */
+ int want:1; /* someone is waiting for I/O to complete */
+ int open_count; /* number of opens */
+ int iocount; /* number of pending I/O operations */
+ int part; /* BSD partition number (-1 if none) */
+ ipc_port_t port; /* port representing device */
+ struct device_struct *ds; /* driver operation table entry */
+ struct device device; /* generic device header */
+ struct file file; /* Linux file structure */
+ struct inode inode; /* Linux inode structure */
+ struct name_map *np; /* name to inode map */
+ struct block_data *next; /* forward link */
+};
+
+/* List of open devices. */
+static struct block_data *open_list;
+
+/* Forward declarations. */
+
+extern struct device_emulation_ops linux_block_emulation_ops;
+
+static io_return_t device_close (void *);
+
+/* Return a send right for block device BD. */
+static ipc_port_t
+dev_to_port (void *bd)
+{
+ return (bd
+ ? ipc_port_make_send (((struct block_data *) bd)->port)
+ : IP_NULL);
+}
+
+/* Return 1 if C is a letter of the alphabet. */
+static inline int
+isalpha (int c)
+{
+ return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');
+}
+
+/* Return 1 if C is a digit. */
+static inline int
+isdigit (int c)
+{
+ return c >= '0' && c <= '9';
+}
+
+int linux_device_open_trace = 0;
+
+static io_return_t
+device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type,
+ dev_mode_t mode, char *name, device_t *devp)
+{
+ char *p;
+ int i, part = -1, slice = 0, err = 0;
+ unsigned major, minor;
+ kdev_t dev;
+ ipc_port_t notify;
+ struct file file;
+ struct inode inode;
+ struct name_map *np;
+ struct device_struct *ds;
+ struct block_data *bd = NULL, *bdp;
+
+ if (linux_device_open_trace)
+ printf ("device_open: at %d: name %s\n", __LINE__, name);
+
+ /* Parse name into name, unit, DOS partition (slice) and partition. */
+ for (p = name; isalpha (*p); p++)
+ ;
+ if (p == name || ! isdigit (*p))
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ return D_NO_SUCH_DEVICE;
+ }
+ do
+ p++;
+ while (isdigit (*p));
+ if (*p)
+ {
+ char *q = p;
+
+ if (! isalpha (*q))
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ return D_NO_SUCH_DEVICE;
+ }
+ if (*q == 's' && isdigit (*(q + 1)))
+ {
+ q++;
+ slice = 0;
+ do
+ slice = slice * 10 + *q++ - '0';
+ while (isdigit (*q));
+ if (! *q)
+ goto find_major;
+ if (! isalpha (*q))
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ return D_NO_SUCH_DEVICE;
+ }
+ }
+ if (*(q + 1))
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ return D_NO_SUCH_DEVICE;
+ }
+ part = *q - 'a';
+ }
+ else
+ slice = -1;
+
+find_major:
+ /* Convert name to major number. */
+ for (i = 0, np = name_to_major; i < NUM_NAMES; i++, np++)
+ {
+ int len = strlen (np->name);
+
+ if (len == p - name && ! strncmp (np->name, name, len))
+ break;
+ }
+ if (i == NUM_NAMES)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ return D_NO_SUCH_DEVICE;
+ }
+
+ major = np->major;
+ ds = &blkdevs[major];
+
+ /* Check that driver exists. */
+ if (! ds->fops)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ return D_NO_SUCH_DEVICE;
+ }
+
+ /* Slice and partition numbers are only used by disk drives.
+ The test for read-only is for IDE CDROMs. */
+ if (! disk_major (major) || np->read_only)
+ {
+ slice = -1;
+ part = -1;
+ }
+
+ /* Wait for any other open/close calls to finish. */
+ ds = &blkdevs[major];
+ while (ds->busy)
+ {
+ ds->want = 1;
+ assert_wait ((event_t) ds, FALSE);
+ thread_block (0);
+ }
+ ds->busy = 1;
+
+ /* Compute minor number. */
+ if (disk_major (major) && ! ds->gd)
+ {
+ struct gendisk *gd;
+
+ for (gd = gendisk_head; gd && gd->major != major; gd = gd->next)
+ ;
+ assert (gd);
+ ds->gd = gd;
+ }
+ minor = np->unit;
+ if (ds->gd)
+ minor <<= ds->gd->minor_shift;
+ dev = MKDEV (major, minor);
+
+ /* If no DOS partition is specified, find one we can handle. */
+ if (slice == 0 && (! ds->default_slice || ds->default_slice[np->unit] == 0))
+ {
+ int sysid, bsize, bshift;
+ struct mboot *mp;
+ struct ipart *pp;
+ struct buffer_head *bhp;
+
+ /* Open partition 0. */
+ inode.i_rdev = dev;
+ file.f_mode = O_RDONLY;
+ file.f_flags = 0;
+ if (ds->fops->open)
+ {
+ linux_intr_pri = SPL5;
+ err = (*ds->fops->open) (&inode, &file);
+ if (err)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = linux_to_mach_error (err);
+ goto out;
+ }
+ }
+
+ /* Allocate a buffer for I/O. */
+ get_block_size (inode.i_rdev, &bsize, &bshift);
+ assert (bsize <= PAGE_SIZE);
+ bhp = getblk (inode.i_rdev, 0, bsize);
+ if (! bhp)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = D_NO_MEMORY;
+ goto slice_done;
+ }
+
+ /* Read DOS partition table. */
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ printf ("%s: error reading boot sector\n", np->name);
+ err = linux_to_mach_error (err);
+ goto slice_done;
+ }
+
+ /* Check for valid partition table. */
+ mp = (struct mboot *) bhp->b_data;
+ if (mp->signature != BOOT_MAGIC)
+ {
+ printf ("%s: invalid partition table\n", np->name);
+ err = D_NO_SUCH_DEVICE;
+ goto slice_done;
+ }
+
+ /* Search for a Mach, BSD or Linux partition. */
+ sysid = 0;
+ pp = (struct ipart *) mp->parts;
+ for (i = 0; i < FD_NUMPART; i++, pp++)
+ {
+ if ((pp->systid == UNIXOS
+ || pp->systid == BSDOS
+ || pp->systid == LINUXOS)
+ && (! sysid || pp->bootid == ACTIVE))
+ {
+ sysid = pp->systid;
+ slice = i + 1;
+ }
+ }
+ if (! sysid)
+ {
+ printf ("%s: No Mach, BSD or Linux partition found\n", np->name);
+ err = D_NO_SUCH_DEVICE;
+ goto slice_done;
+ }
+
+ printf ("%s: default slice %d: %s OS\n", np->name, slice,
+ (sysid == UNIXOS ? "Mach" : (sysid == BSDOS ? "BSD" : "LINUX")));
+
+ slice_done:
+ if (ds->fops->release)
+ (*ds->fops->release) (&inode, &file);
+ __brelse (bhp);
+ if (err)
+ goto out;
+ if (! ds->default_slice)
+ {
+ ds->default_slice = (int *) kalloc (sizeof (int) * ds->gd->max_nr);
+ if (! ds->default_slice)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = D_NO_MEMORY;
+ goto out;
+ }
+ memset (ds->default_slice, 0, sizeof (int) * ds->gd->max_nr);
+ }
+ ds->default_slice[np->unit] = slice;
+ }
+
+ /* Add slice to minor number. */
+ if (slice == 0)
+ slice = ds->default_slice[np->unit];
+ if (slice > 0)
+ {
+ if (slice >= ds->gd->max_p)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = D_NO_SUCH_DEVICE;
+ goto out;
+ }
+ minor |= slice;
+
+ if (linux_device_open_trace)
+ printf ("device_open: at %d: start_sect 0x%x, nr_sects %d\n",
+ __LINE__, ds->gd->part[minor].start_sect,
+ ds->gd->part[minor].nr_sects);
+ }
+ dev = MKDEV (major, minor);
+
+ /* Initialize file structure. */
+ file.f_mode = (mode == D_READ || np->read_only) ? O_RDONLY : O_RDWR;
+ file.f_flags = (mode & D_NODELAY) ? O_NDELAY : 0;
+
+ /* Check if the device is currently open. */
+ for (bdp = open_list; bdp; bdp = bdp->next)
+ if (bdp->inode.i_rdev == dev
+ && bdp->part == part
+ && bdp->file.f_mode == file.f_mode
+ && bdp->file.f_flags == file.f_flags)
+ {
+ bd = bdp;
+ goto out;
+ }
+
+ /* Open the device. */
+ if (ds->fops->open)
+ {
+ inode.i_rdev = dev;
+ linux_intr_pri = SPL5;
+ err = (*ds->fops->open) (&inode, &file);
+ if (err)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = linux_to_mach_error (err);
+ goto out;
+ }
+ }
+
+ /* Read disklabel. */
+ if (part >= 0 && (! ds->label || ! ds->label[minor]))
+ {
+ int bsize, bshift;
+ struct evtoc *evp;
+ struct disklabel *lp, *dlp;
+ struct buffer_head *bhp;
+
+ assert (disk_major (major));
+
+ /* Allocate a disklabel. */
+ lp = (struct disklabel *) kalloc (sizeof (struct disklabel));
+ if (! lp)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = D_NO_MEMORY;
+ goto bad;
+ }
+
+ /* Allocate a buffer for I/O. */
+ get_block_size (dev, &bsize, &bshift);
+ assert (bsize <= PAGE_SIZE);
+ bhp = getblk (dev, LBLLOC >> (bshift - 9), bsize);
+ if (! bhp)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = D_NO_MEMORY;
+ goto label_done;
+ }
+
+ /* Set up 'c' partition to span the entire DOS partition. */
+ lp->d_npartitions = PART_DISK + 1;
+ memset (lp->d_partitions, 0, MAXPARTITIONS * sizeof (struct partition));
+ lp->d_partitions[PART_DISK].p_offset = ds->gd->part[minor].start_sect;
+ lp->d_partitions[PART_DISK].p_size = ds->gd->part[minor].nr_sects;
+
+ /* Try reading a BSD disklabel. */
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ printf ("%s: error reading BSD label\n", np->name);
+ err = 0;
+ goto vtoc;
+ }
+ dlp = (struct disklabel *) (bhp->b_data + ((LBLLOC << 9) & (bsize - 1)));
+ if (dlp->d_magic != DISKMAGIC || dlp->d_magic2 != DISKMAGIC)
+ goto vtoc;
+ printf ("%s: BSD LABEL\n", np->name);
+ lp->d_npartitions = dlp->d_npartitions;
+ memcpy (lp->d_partitions, dlp->d_partitions,
+ MAXPARTITIONS * sizeof (struct partition));
+
+ /* Check for NetBSD DOS partition bogosity. */
+ for (i = 0; i < lp->d_npartitions; i++)
+ if (lp->d_partitions[i].p_size > ds->gd->part[minor].nr_sects)
+ ds->gd->part[minor].nr_sects = lp->d_partitions[i].p_size;
+ goto label_done;
+
+ vtoc:
+ /* Try reading VTOC. */
+ bhp->b_blocknr = PDLOCATION >> (bshift - 9);
+ bhp->b_state = 1 << BH_Lock;
+ ll_rw_block (READ, 1, &bhp);
+ wait_on_buffer (bhp);
+ err = check_for_error (READ, 1, &bhp);
+ if (err)
+ {
+ printf ("%s: error reading evtoc\n", np->name);
+ err = linux_to_mach_error (err);
+ goto label_done;
+ }
+ evp = (struct evtoc *) (bhp->b_data + ((PDLOCATION << 9) & (bsize - 1)));
+ if (evp->sanity != VTOC_SANE)
+ {
+ printf ("%s: No BSD or Mach label found\n", np->name);
+ err = D_NO_SUCH_DEVICE;
+ goto label_done;
+ }
+ printf ("%s: LOCAL LABEL\n", np->name);
+ lp->d_npartitions = (evp->nparts > MAXPARTITIONS
+ ? MAXPARTITIONS : evp->nparts);
+ for (i = 0; i < lp->d_npartitions; i++)
+ {
+ lp->d_partitions[i].p_size = evp->part[i].p_size;
+ lp->d_partitions[i].p_offset = evp->part[i].p_start;
+ lp->d_partitions[i].p_fstype = FS_BSDFFS;
+ }
+
+ label_done:
+ if (bhp)
+ __brelse (bhp);
+ if (err)
+ {
+ kfree ((vm_offset_t) lp, sizeof (struct disklabel));
+ goto bad;
+ }
+ if (! ds->label)
+ {
+ ds->label = (struct disklabel **) kalloc (sizeof (struct disklabel *)
+ * ds->gd->max_p
+ * ds->gd->max_nr);
+ if (! ds->label)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ kfree ((vm_offset_t) lp, sizeof (struct disklabel));
+ err = D_NO_MEMORY;
+ goto bad;
+ }
+ memset (ds->label, 0,
+ (sizeof (struct disklabel *)
+ * ds->gd->max_p * ds->gd->max_nr));
+ }
+ ds->label[minor] = lp;
+ }
+
+ /* Check partition number. */
+ if (part >= 0
+ && (part >= ds->label[minor]->d_npartitions
+ || ds->label[minor]->d_partitions[part].p_size == 0))
+ {
+ err = D_NO_SUCH_DEVICE;
+ goto bad;
+ }
+
+ /* Allocate and initialize device data. */
+ bd = (struct block_data *) kalloc (sizeof (struct block_data));
+ if (! bd)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = D_NO_MEMORY;
+ goto bad;
+ }
+ bd->want = 0;
+ bd->open_count = 0;
+ bd->iocount = 0;
+ bd->part = part;
+ bd->ds = ds;
+ bd->device.emul_data = bd;
+ bd->device.emul_ops = &linux_block_emulation_ops;
+ bd->inode.i_rdev = dev;
+ bd->file.f_mode = file.f_mode;
+ bd->file.f_np = np;
+ bd->file.f_flags = file.f_flags;
+ bd->port = ipc_port_alloc_kernel ();
+ if (bd->port == IP_NULL)
+ {
+ if (linux_device_open_trace)
+ printf ("device_open: at %d\n", __LINE__);
+
+ err = KERN_RESOURCE_SHORTAGE;
+ goto bad;
+ }
+ ipc_kobject_set (bd->port, (ipc_kobject_t) &bd->device, IKOT_DEVICE);
+ notify = ipc_port_make_sonce (bd->port);
+ ip_lock (bd->port);
+ ipc_port_nsrequest (bd->port, 1, notify, &notify);
+ assert (notify == IP_NULL);
+
+ goto out;
+
+bad:
+ if (ds->fops->release)
+ (*ds->fops->release) (&inode, &file);
+
+out:
+ ds->busy = 0;
+ if (ds->want)
+ {
+ ds->want = 0;
+ thread_wakeup ((event_t) ds);
+ }
+
+ if (bd && bd->open_count > 0)
+ {
+ if (err)
+ *devp = NULL;
+ else
+ {
+ *devp = &bd->device;
+ bd->open_count++;
+ }
+ return err;
+ }
+
+ if (err)
+ {
+ if (bd)
+ {
+ if (bd->port != IP_NULL)
+ {
+ ipc_kobject_set (bd->port, IKO_NULL, IKOT_NONE);
+ ipc_port_dealloc_kernel (bd->port);
+ }
+ kfree ((vm_offset_t) bd, sizeof (struct block_data));
+ bd = NULL;
+ }
+ }
+ else
+ {
+ bd->open_count = 1;
+ bd->next = open_list;
+ open_list = bd;
+ }
+
+ if (IP_VALID (reply_port))
+ ds_device_open_reply (reply_port, reply_port_type, err, dev_to_port (bd));
+ else if (! err)
+ device_close (bd);
+
+ return MIG_NO_REPLY;
+}
+
+static io_return_t
+device_close (void *d)
+{
+ struct block_data *bd = d, *bdp, **prev;
+ struct device_struct *ds = bd->ds;
+
+ /* Wait for any other open/close to complete. */
+ while (ds->busy)
+ {
+ ds->want = 1;
+ assert_wait ((event_t) ds, FALSE);
+ thread_block (0);
+ }
+ ds->busy = 1;
+
+ if (--bd->open_count == 0)
+ {
+ /* Wait for pending I/O to complete. */
+ while (bd->iocount > 0)
+ {
+ bd->want = 1;
+ assert_wait ((event_t) bd, FALSE);
+ thread_block (0);
+ }
+
+ /* Remove device from open list. */
+ prev = &open_list;
+ bdp = open_list;
+ while (bdp)
+ {
+ if (bdp == bd)
+ {
+ *prev = bdp->next;
+ break;
+ }
+ prev = &bdp->next;
+ bdp = bdp->next;
+ }
+
+ assert (bdp == bd);
+
+ if (ds->fops->release)
+ (*ds->fops->release) (&bd->inode, &bd->file);
+
+ ipc_kobject_set (bd->port, IKO_NULL, IKOT_NONE);
+ ipc_port_dealloc_kernel (bd->port);
+ kfree ((vm_offset_t) bd, sizeof (struct block_data));
+ }
+
+ ds->busy = 0;
+ if (ds->want)
+ {
+ ds->want = 0;
+ thread_wakeup ((event_t) ds);
+ }
+ return D_SUCCESS;
+}
+
+/* XXX: Assumes all drivers use block_write. */
+static io_return_t
+device_write (void *d, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ recnum_t bn, io_buf_ptr_t data, unsigned int count,
+ int *bytes_written)
+{
+ int major, minor;
+ unsigned sz, maxsz, off;
+ io_return_t err = 0;
+ struct block_data *bd = d;
+
+ if (! bd->ds->fops->write)
+ {
+ printf ("device_write: at %d\n", __LINE__);
+ return D_INVALID_OPERATION;
+ }
+
+ if ((int) count <= 0)
+ {
+ printf ("device_write: at %d\n", __LINE__);
+ return D_INVALID_SIZE;
+ }
+
+ major = MAJOR (bd->inode.i_rdev);
+ minor = MINOR (bd->inode.i_rdev);
+
+ if (disk_major (major))
+ {
+ assert (bd->ds->gd);
+
+ if (bd->part >= 0)
+ {
+ struct disklabel *lp;
+
+ assert (bd->ds->label);
+ lp = bd->ds->label[minor];
+ assert (lp);
+ maxsz = lp->d_partitions[bd->part].p_size;
+ off = (lp->d_partitions[bd->part].p_offset
+ - bd->ds->gd->part[minor].start_sect);
+
+ if (linux_block_write_trace)
+ printf ("device_write: at %d: dev %s, part %d, "
+ "offset 0x%x (%u), start_sect 0x%x (%u), "
+ "maxsz 0x%x (%u)\n",
+ __LINE__,
+ kdevname (bd->inode.i_rdev),
+ bd->part,
+ lp->d_partitions[bd->part].p_offset,
+ lp->d_partitions[bd->part].p_offset,
+ bd->ds->gd->part[minor].start_sect,
+ bd->ds->gd->part[minor].start_sect,
+ maxsz, maxsz);
+
+ assert (off < bd->ds->gd->part[minor].nr_sects);
+ }
+ else
+ {
+ maxsz = bd->ds->gd->part[minor].nr_sects;
+ off = 0;
+ }
+ }
+ else
+ {
+ assert (blk_size[major]);
+ maxsz = blk_size[major][minor] << (BLOCK_SIZE_BITS - 9);
+ off = 0;
+ }
+
+ if (bn >= maxsz)
+ {
+ if (linux_block_write_trace)
+ printf ("device_write: at %d\n", __LINE__);
+
+ return D_INVALID_SIZE;
+ }
+
+ bd->iocount++;
+
+ sz = (count + 511) >> 9;
+ if (sz > maxsz - bn)
+ {
+ sz = maxsz - bn;
+ if (count > (sz << 9))
+ count = sz << 9;
+ }
+
+ bd->file.f_pos = (loff_t) (bn + off) << 9;
+
+ err = (*bd->ds->fops->write) (&bd->inode, &bd->file, (char *) data, count);
+ if (err)
+ err = linux_to_mach_error (err);
+
+ if (linux_block_write_trace)
+ printf ("device_write: at %d: err %d\n", __LINE__, err);
+
+ if (IP_VALID (reply_port))
+ ds_device_write_reply (reply_port, reply_port_type,
+ err, count - bd->file.f_resid);
+
+ if (--bd->iocount == 0 && bd->want)
+ {
+ bd->want = 0;
+ thread_wakeup ((event_t) bd);
+ }
+ return MIG_NO_REPLY;
+}
+
+/* XXX: Assumes all drivers use block_read. */
+static io_return_t
+device_read (void *d, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ recnum_t bn, int count, io_buf_ptr_t *data,
+ unsigned *bytes_read)
+{
+ int major, minor;
+ unsigned sz, maxsz, off;
+ io_return_t err = 0;
+ vm_offset_t addr;
+ vm_object_t object;
+ vm_map_copy_t copy;
+ struct block_data *bd = d;
+ struct inode *inode = &bd->inode;
+
+ *data = 0;
+ *bytes_read = 0;
+
+ if (! bd->ds->fops->read)
+ return D_INVALID_OPERATION;
+
+ if (count <= 0)
+ return D_INVALID_SIZE;
+
+ major = MAJOR (bd->inode.i_rdev);
+ minor = MINOR (bd->inode.i_rdev);
+
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("device_read: at %d: major %d, minor %d, count %d, recnum %u\n",
+ __LINE__, major, minor, count, bn);
+
+ if (disk_major (major))
+ {
+ assert (bd->ds->gd);
+
+ if (bd->part >= 0)
+ {
+ struct disklabel *lp;
+
+ assert (bd->ds->label);
+ lp = bd->ds->label[minor];
+ assert (lp);
+ maxsz = lp->d_partitions[bd->part].p_size;
+ off = (lp->d_partitions[bd->part].p_offset
+ - bd->ds->gd->part[minor].start_sect);
+
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("device_read: at %d: dev %s, part %d, offset 0x%x, "
+ "size %d, start_sect 0x%x, nr_sects %d\n",
+ __LINE__, kdevname (major), bd->part, off, maxsz,
+ bd->ds->gd->part[minor].start_sect,
+ bd->ds->gd->part[minor].nr_sects);
+
+ assert (off < bd->ds->gd->part[minor].nr_sects);
+ }
+ else
+ {
+ maxsz = bd->ds->gd->part[minor].nr_sects;
+ off = 0;
+ }
+ }
+ else
+ {
+ assert (blk_size[major]);
+ maxsz = blk_size[major][minor] << (BLOCK_SIZE_BITS - 9);
+ off = 0;
+ }
+
+ if (bn > maxsz)
+ return D_INVALID_SIZE;
+
+ /* Be backward compatible with Unix. */
+ if (bn == maxsz)
+ {
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("device_read: at %d\n", __LINE__);
+ return 0;
+ }
+
+ sz = (count + 511) >> 9;
+ if (sz > maxsz - bn)
+ {
+ sz = maxsz - bn;
+ if (count > (sz << 9))
+ count = sz << 9;
+ }
+
+ bd->file.f_pos = (loff_t) (bn + off) << 9;
+ bd->file.f_object = NULL;
+
+ if (LINUX_BLOCK_READ_TRACE)
+ printf ("device_read: at %d: f_pos 0x%x\n",
+ __LINE__, (unsigned) bd->file.f_pos);
+
+ bd->iocount++;
+
+ err = (*bd->ds->fops->read) (&bd->inode, &bd->file, (char *) data, count);
+ if (err)
+ err = linux_to_mach_error (err);
+ else
+ {
+ object = bd->file.f_object;
+ assert (object);
+ assert (object->ref_count == 1);
+ err = vm_map_copyin_object (object, 0, round_page (count), &copy);
+ assert (object->ref_count == 1);
+ if (err)
+ vm_object_deallocate (object);
+ else
+ {
+ assert (copy->cpy_object->ref_count == 1);
+ *data = (io_buf_ptr_t) copy;
+ *bytes_read = count - bd->file.f_resid;
+ }
+ }
+ if (--bd->iocount == 0 && bd->want)
+ {
+ bd->want = 0;
+ thread_wakeup ((event_t) bd);
+ }
+ return err;
+}
+
+static io_return_t
+device_get_status (void *d, dev_flavor_t flavor, dev_status_t status,
+ mach_msg_type_number_t *status_count)
+{
+ struct block_data *bd = d;
+
+ switch (flavor)
+ {
+ case DEV_GET_SIZE:
+ if (*status_count != DEV_GET_SIZE_COUNT)
+ return D_INVALID_SIZE;
+ if (disk_major (MAJOR (bd->inode.i_rdev)))
+ {
+ assert (bd->ds->gd);
+
+ if (bd->part >= 0)
+ {
+ struct disklabel *lp;
+
+ assert (bd->ds->label);
+ lp = bd->ds->label[MINOR (bd->inode.i_rdev)];
+ assert (lp);
+ (status[DEV_GET_SIZE_DEVICE_SIZE]
+ = lp->d_partitions[bd->part].p_size << 9);
+ }
+ else
+ (status[DEV_GET_SIZE_DEVICE_SIZE]
+ = bd->ds->gd->part[MINOR (bd->inode.i_rdev)].nr_sects << 9);
+ }
+ else
+ {
+ assert (blk_size[MAJOR (bd->inode.i_rdev)]);
+ (status[DEV_GET_SIZE_DEVICE_SIZE]
+ = (blk_size[MAJOR (bd->inode.i_rdev)][MINOR (bd->inode.i_rdev)]
+ << BLOCK_SIZE_BITS));
+ }
+ /* It would be nice to return the block size as reported by
+ the driver, but a lot of user level code assumes the sector
+ size to be 512. */
+ status[DEV_GET_SIZE_RECORD_SIZE] = 512;
+ break;
+
+ default:
+ return D_INVALID_OPERATION;
+ }
+
+ return D_SUCCESS;
+}
+
+struct device_emulation_ops linux_block_emulation_ops =
+{
+ NULL,
+ NULL,
+ dev_to_port,
+ device_open,
+ device_close,
+ device_write,
+ NULL,
+ device_read,
+ NULL,
+ NULL,
+ device_get_status,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
diff --git a/i386/i386at/gpl/linux/linux_dma.c b/i386/i386at/gpl/linux/linux_dma.c
new file mode 100644
index 00000000..aab0fa8e
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_dma.c
@@ -0,0 +1,52 @@
+/*
+ * Linux DMA channel management.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#define MACH_INCLUDE
+#include <linux/errno.h>
+#include <asm/dma.h>
+
+/*
+ * Bitmap of allocated/free DMA channels.
+ */
+static int dma_busy = 0x10;
+
+/*
+ * Allocate a DMA channel.
+ */
+int
+request_dma(unsigned int drq, const char *name)
+{
+ if (drq > 7)
+ panic("request_dma: bad DRQ number");
+ if (dma_busy & (1 << drq))
+ return (-LINUX_EBUSY);
+ dma_busy |= 1 << drq;
+ return (0);
+}
+
+/*
+ * Free a DMA channel.
+ */
+void
+free_dma(unsigned int drq)
+{
+ if (drq > 7)
+ panic("free_dma: bad DRQ number");
+ dma_busy &= ~(1 << drq);
+}
diff --git a/i386/i386at/gpl/linux/linux_emul.h b/i386/i386at/gpl/linux/linux_emul.h
new file mode 100644
index 00000000..dc338020
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_emul.h
@@ -0,0 +1,32 @@
+/*
+ * Defintions for Linux driver emulation.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+#include <i386/ipl.h>
+
+extern int linux_auto_config;
+extern int linux_intr_pri;
+
+int linux_to_mach_error (int);
+void *alloc_contig_mem (unsigned, unsigned, unsigned, vm_page_t *);
+void free_contig_mem (vm_page_t);
+void collect_buffer_pages (void);
diff --git a/i386/i386at/gpl/linux/linux_init.c b/i386/i386at/gpl/linux/linux_init.c
new file mode 100644
index 00000000..d2abae28
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_init.c
@@ -0,0 +1,412 @@
+/*
+ * Linux initialization.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+/*
+ * linux/init/main.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <sys/types.h>
+
+#include <mach/vm_param.h>
+#include <mach/vm_prot.h>
+#include <mach/machine.h>
+
+#include <vm/vm_page.h>
+
+#include <i386/ipl.h>
+#include <i386/pic.h>
+#include <i386/pit.h>
+#include <i386/machspl.h>
+#include <i386/pmap.h>
+#include <i386/vm_param.h>
+
+#include <i386at/gpl/linux/linux_emul.h>
+
+#define MACH_INCLUDE
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+
+#include <asm/system.h>
+
+/*
+ * Set if the machine has an EISA bus.
+ */
+int EISA_bus = 0;
+
+/*
+ * Timing loop count.
+ */
+unsigned long loops_per_sec = 1;
+
+/*
+ * End of physical memory.
+ */
+unsigned long high_memory;
+
+/*
+ * Flag to indicate auto-configuration is in progress.
+ */
+int linux_auto_config = 1;
+
+/*
+ * Hard drive parameters obtained from the BIOS.
+ */
+struct drive_info_struct {
+ char dummy[32];
+} drive_info;
+
+/*
+ * Forward declarations.
+ */
+static void calibrate_delay(void);
+
+extern int hz;
+extern vm_offset_t phys_last_addr;
+
+extern void timer_bh(void *);
+extern void tqueue_bh(void *);
+extern void startrtclock(void);
+extern void linux_version_init(void);
+extern void linux_kmem_init(void);
+extern unsigned long pci_init(unsigned long, unsigned long);
+extern void linux_net_emulation_init (void);
+extern void device_setup(void);
+extern void linux_printk(char *, ...);
+extern int linux_timer_intr();
+
+/*
+ * Amount of contiguous memory to allocate for initialization.
+ */
+#define CONTIG_ALLOC (512 * 1024)
+
+/*
+ * Initialize Linux drivers.
+ */
+void
+linux_init()
+{
+ char *p;
+ int i, addr;
+ int (*old_clock_handler)(), old_clock_pri;
+ unsigned memory_start, memory_end;
+ vm_page_t pages;
+
+ /*
+ * Initialize memory size.
+ */
+ high_memory = phys_last_addr;
+
+ /*
+ * Ensure interrupts are disabled.
+ */
+ (void) splhigh();
+
+ /*
+ * Program counter 0 of 8253 to interrupt hz times per second.
+ */
+ outb(PITCTL_PORT, PIT_C0|PIT_SQUAREMODE|PIT_READMODE);
+ outb(PITCTR0_PORT, CLKNUM / hz);
+ outb(PITCTR0_PORT, (CLKNUM / hz) >> 8);
+
+ /*
+ * Install our clock interrupt handler.
+ */
+ old_clock_handler = ivect[0];
+ old_clock_pri = intpri[0];
+ ivect[0] = linux_timer_intr;
+ intpri[0] = SPLHI;
+ form_pic_mask();
+
+ /*
+ * Enable interrupts.
+ */
+ (void) spl0();
+
+ /*
+ * Set Linux version.
+ */
+ linux_version_init();
+
+ /*
+ * Check if the machine has an EISA bus.
+ */
+ p = (char *)0x0FFFD9;
+ if (*p++ == 'E' && *p++ == 'I' && *p++ == 'S' && *p == 'A')
+ EISA_bus = 1;
+
+ /*
+ * Permanently allocate standard device ports.
+ */
+ request_region(0x00, 0x20, "dma1");
+ request_region(0x40, 0x20, "timer");
+ request_region(0x70, 0x10, "rtc");
+ request_region(0x80, 0x20, "dma page reg");
+ request_region(0xc0, 0x20, "dma2");
+ request_region(0xf0, 0x02, "fpu");
+ request_region(0xf8, 0x08, "fpu");
+
+ /*
+ * Install software interrupt handlers.
+ */
+ bh_base[TIMER_BH].routine = timer_bh;
+ bh_base[TIMER_BH].data = 0;
+ enable_bh(TIMER_BH);
+ bh_base[TQUEUE_BH].routine = tqueue_bh;
+ bh_base[TQUEUE_BH].data = 0;
+ enable_bh(TQUEUE_BH);
+
+ /*
+ * Set loop count.
+ */
+ calibrate_delay();
+
+ /*
+ * Initialize drive info.
+ */
+ addr = *((unsigned *)phystokv(0x104));
+ memcpy (&drive_info,
+ (void *)((addr & 0xffff) + ((addr >> 12) & 0xffff0)), 16);
+ addr = *((unsigned *)phystokv(0x118));
+ memcpy ((char *)&drive_info + 16,
+ (void *)((addr & 0xffff) + ((addr >> 12) & 0xffff0)), 16);
+
+ /*
+ * Initialize Linux memory allocator.
+ */
+ linux_kmem_init();
+
+ /*
+ * Allocate contiguous memory below 16 MB.
+ */
+ memory_start = (unsigned long)alloc_contig_mem(CONTIG_ALLOC,
+ 16 * 1024 * 1024,
+ 0, &pages);
+ if (memory_start == 0)
+ panic("linux_init: alloc_contig_mem failed");
+ memory_end = memory_start + CONTIG_ALLOC;
+
+ /*
+ * Initialize PCI bus.
+ */
+ memory_start = pci_init(memory_start, memory_end);
+
+ if (memory_start > memory_end)
+ panic("linux_init: ran out memory");
+
+ /*
+ * Free unused memory.
+ */
+ while (pages && pages->phys_addr < round_page(memory_start))
+ pages = (vm_page_t)pages->pageq.next;
+ if (pages)
+ free_contig_mem(pages);
+
+ /*
+ * Initialize devices.
+ */
+ linux_net_emulation_init();
+ cli();
+ device_setup();
+
+ /*
+ * Disable interrupts.
+ */
+ (void) splhigh();
+
+ /*
+ * Restore clock interrupt handler.
+ */
+ ivect[0] = old_clock_handler;
+ intpri[0] = old_clock_pri;
+ form_pic_mask();
+
+ linux_auto_config = 0;
+}
+
+#ifndef NBPW
+#define NBPW 32
+#endif
+
+/*
+ * Allocate contiguous memory with the given constraints.
+ * This routine is horribly inefficient but it is presently
+ * only used during initialization so it's not that bad.
+ */
+void *
+alloc_contig_mem(unsigned size, unsigned limit,
+ unsigned mask, vm_page_t *pages)
+{
+ int i, j, bits_len;
+ unsigned *bits, len;
+ void *m;
+ vm_page_t p, page_list, tail, prev;
+ vm_offset_t addr, max_addr;
+
+ if (size == 0)
+ return (NULL);
+ size = round_page(size);
+ if ((size >> PAGE_SHIFT) > vm_page_free_count)
+ return (NULL);
+
+ /* Allocate bit array. */
+ max_addr = phys_last_addr;
+ if (max_addr > limit)
+ max_addr = limit;
+ bits_len = ((((max_addr >> PAGE_SHIFT) + NBPW - 1) / NBPW)
+ * sizeof(unsigned));
+ bits = (unsigned *)kalloc(bits_len);
+ if (!bits)
+ return (NULL);
+ memset (bits, 0, bits_len);
+
+ /*
+ * Walk the page free list and set a bit for every usable page.
+ */
+ simple_lock(&vm_page_queue_free_lock);
+ p = vm_page_queue_free;
+ while (p) {
+ if (p->phys_addr < limit)
+ (bits[(p->phys_addr >> PAGE_SHIFT) / NBPW]
+ |= 1 << ((p->phys_addr >> PAGE_SHIFT) % NBPW));
+ p = (vm_page_t)p->pageq.next;
+ }
+
+ /*
+ * Scan bit array for contiguous pages.
+ */
+ len = 0;
+ m = NULL;
+ for (i = 0; len < size && i < bits_len / sizeof (unsigned); i++)
+ for (j = 0; len < size && j < NBPW; j++)
+ if (!(bits[i] & (1 << j))) {
+ len = 0;
+ m = NULL;
+ } else {
+ if (len == 0) {
+ addr = ((vm_offset_t)(i * NBPW + j)
+ << PAGE_SHIFT);
+ if ((addr & mask) == 0) {
+ len += PAGE_SIZE;
+ m = (void *) addr;
+ }
+ } else
+ len += PAGE_SIZE;
+ }
+
+ if (len != size) {
+ simple_unlock(&vm_page_queue_free_lock);
+ kfree ((vm_offset_t)bits, bits_len);
+ return (NULL);
+ }
+
+ /*
+ * Remove pages from free list
+ * and construct list to return to caller.
+ */
+ page_list = NULL;
+ for (len = 0; len < size; len += PAGE_SIZE, addr += PAGE_SIZE) {
+ prev = NULL;
+ for (p = vm_page_queue_free; p; p = (vm_page_t)p->pageq.next) {
+ if (p->phys_addr == addr)
+ break;
+ prev = p;
+ }
+ if (!p)
+ panic("alloc_contig_mem: page not on free list");
+ if (prev)
+ prev->pageq.next = p->pageq.next;
+ else
+ vm_page_queue_free = (vm_page_t)p->pageq.next;
+ p->free = FALSE;
+ p->pageq.next = NULL;
+ if (!page_list)
+ page_list = tail = p;
+ else {
+ tail->pageq.next = (queue_entry_t)p;
+ tail = p;
+ }
+ vm_page_free_count--;
+ }
+
+ simple_unlock(&vm_page_queue_free_lock);
+ kfree((vm_offset_t)bits, bits_len);
+ if (pages)
+ *pages = page_list;
+ return (m);
+}
+
+/*
+ * Free memory allocated by alloc_contig_mem.
+ */
+void
+free_contig_mem(vm_page_t pages)
+{
+ int i;
+ vm_page_t p;
+
+ for (p = pages, i = 0; p->pageq.next; p = (vm_page_t)p->pageq.next, i++)
+ p->free = TRUE;
+ p->free = TRUE;
+ simple_lock(&vm_page_queue_free_lock);
+ vm_page_free_count += i + 1;
+ p->pageq.next = (queue_entry_t)vm_page_queue_free;
+ vm_page_queue_free = pages;
+ simple_unlock(&vm_page_queue_free_lock);
+}
+
+/*
+ * Calibrate delay loop.
+ * Lifted straight from Linux.
+ */
+static void
+calibrate_delay()
+{
+ int ticks;
+
+ printk("Calibrating delay loop.. ");
+ while (loops_per_sec <<= 1) {
+ /* Wait for "start of" clock tick. */
+ ticks = jiffies;
+ while (ticks == jiffies)
+ /* nothing */;
+ /* Go .. */
+ ticks = jiffies;
+ __delay(loops_per_sec);
+ ticks = jiffies - ticks;
+ if (ticks >= hz) {
+ loops_per_sec = muldiv(loops_per_sec,
+ hz, ticks);
+ printk("ok - %lu.%02lu BogoMips\n",
+ loops_per_sec / 500000,
+ (loops_per_sec / 5000) % 100);
+ return;
+ }
+ }
+ printk("failed\n");
+}
diff --git a/i386/i386at/gpl/linux/linux_irq.c b/i386/i386at/gpl/linux/linux_irq.c
new file mode 100644
index 00000000..d04e0531
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_irq.c
@@ -0,0 +1,246 @@
+/*
+ * Linux IRQ management.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * linux/arch/i386/kernel/irq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ */
+
+#include <sys/types.h>
+
+#include <kern/assert.h>
+
+#include <i386/machspl.h>
+#include <i386/ipl.h>
+#include <i386/pic.h>
+
+#define MACH_INCLUDE
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/kernel_stat.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+
+/*
+ * Priority at which a Linux handler should be called.
+ * This is used at the time of an IRQ allocation. It is
+ * set by emulation routines for each class of device.
+ */
+spl_t linux_intr_pri;
+
+/*
+ * Flag indicating an interrupt is being handled.
+ */
+unsigned long intr_count = 0;
+
+/*
+ * List of Linux interrupt handlers.
+ */
+static void (*linux_handlers[16])(int, struct pt_regs *);
+
+extern spl_t curr_ipl;
+extern int curr_pic_mask;
+extern int pic_mask[];
+
+extern int intnull(), prtnull();
+
+/*
+ * Generic interrupt handler for Linux devices.
+ * Set up a fake `struct pt_regs' then call the real handler.
+ */
+static int
+linux_intr(irq)
+ int irq;
+{
+ struct pt_regs regs;
+
+ kstat.interrupts[irq]++;
+ intr_count++;
+ (*linux_handlers[irq])(irq, &regs);
+ intr_count--;
+}
+
+/*
+ * Mask an IRQ.
+ */
+void
+disable_irq(irq)
+ unsigned int irq;
+{
+ int i, flags;
+
+ assert (irq < NR_IRQS);
+
+ save_flags(flags);
+ cli();
+ for (i = 0; i < intpri[irq]; i++)
+ pic_mask[i] |= 1 << irq;
+ if (curr_pic_mask != pic_mask[curr_ipl]) {
+ curr_pic_mask = pic_mask[curr_ipl];
+ outb(PIC_MASTER_OCW, curr_pic_mask);
+ outb(PIC_SLAVE_OCW, curr_pic_mask >> 8);
+ }
+ restore_flags(flags);
+}
+
+/*
+ * Unmask an IRQ.
+ */
+void
+enable_irq(irq)
+ unsigned int irq;
+{
+ int mask, i, flags;
+
+ assert (irq < NR_IRQS);
+
+ mask = 1 << irq;
+ if (irq >= 8)
+ mask |= 1 << 2;
+ save_flags(flags);
+ cli();
+ for (i = 0; i < intpri[irq]; i++)
+ pic_mask[i] &= ~mask;
+ if (curr_pic_mask != pic_mask[curr_ipl]) {
+ curr_pic_mask = pic_mask[curr_ipl];
+ outb(PIC_MASTER_OCW, curr_pic_mask);
+ outb(PIC_SLAVE_OCW, curr_pic_mask >> 8);
+ }
+ restore_flags(flags);
+}
+
+/*
+ * Attach a handler to an IRQ.
+ */
+int
+request_irq(unsigned int irq, void (*handler)(int, struct pt_regs *),
+ unsigned long flags, const char *device)
+{
+ assert(irq < 16);
+
+ if (ivect[irq] == intnull || ivect[irq] == prtnull) {
+ if (!handler)
+ return (-LINUX_EINVAL);
+ linux_handlers[irq] = handler;
+ ivect[irq] = linux_intr;
+ iunit[irq] = irq;
+ intpri[irq] = linux_intr_pri;
+ enable_irq(irq);
+ return (0);
+ }
+ return (-LINUX_EBUSY);
+}
+
+/*
+ * Deallocate an irq.
+ */
+void
+free_irq(unsigned int irq)
+{
+ if (irq > 15)
+ panic("free_irq: bad irq number");
+
+ disable_irq(irq);
+ ivect[irq] = (irq == 7) ? prtnull : intnull;
+ iunit[irq] = irq;
+ intpri[irq] = SPL0;
+}
+
+/*
+ * IRQ probe interrupt handler.
+ */
+void
+probe_intr(irq)
+ int irq;
+{
+ disable_irq(irq);
+}
+
+/*
+ * Set for an irq probe.
+ */
+unsigned long
+probe_irq_on()
+{
+ unsigned i, irqs = 0;
+ unsigned long delay;
+
+ assert (curr_ipl == 0);
+
+ /*
+ * Allocate all available IRQs.
+ */
+ for (i = 15; i > 0; i--)
+ if (request_irq(i, probe_intr, 0, "probe") == 0)
+ irqs |= 1 << i;
+
+ /*
+ * Wait for spurious interrupts to mask themselves out.
+ */
+ for (delay = jiffies + 2; delay > jiffies; )
+ ;
+
+ /*
+ * Free IRQs that caused spurious interrupts.
+ */
+ for (i = 15; i > 0; i--) {
+ if (irqs & (1 << i) & pic_mask[0]) {
+ irqs ^= 1 << i;
+ free_irq(i);
+ }
+ }
+
+ return (irqs);
+}
+
+/*
+ * Return the result of an irq probe.
+ */
+int
+probe_irq_off(unsigned long irqs)
+{
+ unsigned i, irqs_save = irqs;
+
+ assert (curr_ipl == 0);
+
+ irqs &= pic_mask[0];
+
+ /*
+ * Deallocate IRQs.
+ */
+ for (i = 15; i > 0; i--)
+ if (irqs_save & (1 << i))
+ free_irq(i);
+
+ /*
+ * Return IRQ number.
+ */
+ if (!irqs)
+ return (0);
+ i = ffz(~irqs);
+ if (irqs != (irqs & (1 << i)))
+ i = -i;
+ return (i);
+}
diff --git a/i386/i386at/gpl/linux/linux_kmem.c b/i386/i386at/gpl/linux/linux_kmem.c
new file mode 100644
index 00000000..fe8de194
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_kmem.c
@@ -0,0 +1,481 @@
+/*
+ * Linux memory allocation.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ *
+ */
+
+#include <sys/types.h>
+
+#include <mach/mach_types.h>
+#include <mach/vm_param.h>
+
+#include <kern/assert.h>
+#include <kern/kalloc.h>
+
+#include <vm/vm_page.h>
+
+#include <i386at/gpl/linux/linux_emul.h>
+
+#define MACH_INCLUDE
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <linux/delay.h>
+
+#include <asm/system.h>
+
+/* Amount of memory to reserve for Linux memory allocator.
+ We reserve 64K chunks to stay within DMA limits.
+ Increase MEM_CHUNKS if the kernel is running out of memory. */
+#define MEM_CHUNK_SIZE (64 * 1024)
+#define MEM_CHUNKS 3
+
+/* Mininum amount that linux_kmalloc will allocate. */
+#define MIN_ALLOC 12
+
+#ifndef NBPW
+#define NBPW 32
+#endif
+
+/* Memory block header. */
+struct blkhdr
+{
+ unsigned short free; /* 1 if block is free */
+ unsigned short size; /* size of block */
+};
+
+/* This structure heads a page allocated by linux_kmalloc. */
+struct pagehdr
+{
+ unsigned size; /* size (multiple of PAGE_SIZE) */
+ struct pagehdr *next; /* next header in list */
+};
+
+/* This structure describes a memory chunk. */
+struct chunkhdr
+{
+ unsigned long start; /* start address */
+ unsigned long end; /* end address */
+ unsigned long bitmap; /* busy/free bitmap of pages */
+};
+
+/* Chunks from which pages are allocated. */
+static struct chunkhdr pages_free[MEM_CHUNKS];
+
+/* Memory list maintained by linux_kmalloc. */
+static struct pagehdr *memlist;
+
+/* Some statistics. */
+int num_block_coalesce = 0;
+int num_page_collect = 0;
+int linux_mem_avail;
+
+/* Initialize the Linux memory allocator. */
+void
+linux_kmem_init ()
+{
+ int i, j;
+ vm_page_t p, pages;
+
+ for (i = 0; i < MEM_CHUNKS; i++)
+ {
+ /* Allocate memory. */
+ pages_free[i].start = (unsigned long) alloc_contig_mem (MEM_CHUNK_SIZE,
+ 16 * 1024 * 1024,
+ 0xffff, &pages);
+
+ assert (pages_free[i].start);
+ assert ((pages_free[i].start & 0xffff) == 0);
+
+ /* Sanity check: ensure pages are contiguous and within DMA limits. */
+ for (p = pages, j = 0; j < MEM_CHUNK_SIZE - PAGE_SIZE; j += PAGE_SIZE)
+ {
+ assert (p->phys_addr < 16 * 1024 * 1024);
+ assert (p->phys_addr + PAGE_SIZE
+ == ((vm_page_t) p->pageq.next)->phys_addr);
+
+ p = (vm_page_t) p->pageq.next;
+ }
+
+ pages_free[i].end = pages_free[i].start + MEM_CHUNK_SIZE;
+
+ /* Initialize free page bitmap. */
+ pages_free[i].bitmap = 0;
+ j = MEM_CHUNK_SIZE >> PAGE_SHIFT;
+ while (--j >= 0)
+ pages_free[i].bitmap |= 1 << j;
+ }
+
+ linux_mem_avail = (MEM_CHUNKS * MEM_CHUNK_SIZE) >> PAGE_SHIFT;
+}
+
+/* Return the number by which the page size should be
+ shifted such that the resulting value is >= SIZE. */
+static unsigned long
+get_page_order (int size)
+{
+ unsigned long order;
+
+ for (order = 0; (PAGE_SIZE << order) < size; order++)
+ ;
+ return order;
+}
+
+#ifdef LINUX_DEV_DEBUG
+static void
+check_page_list (int line)
+{
+ unsigned size;
+ struct pagehdr *ph;
+ struct blkhdr *bh;
+
+ for (ph = memlist; ph; ph = ph->next)
+ {
+ if ((int) ph & PAGE_MASK)
+ panic ("%s:%d: page header not aligned", __FILE__, line);
+
+ size = 0;
+ bh = (struct blkhdr *) (ph + 1);
+ while (bh < (struct blkhdr *) ((void *) ph + ph->size))
+ {
+ size += bh->size + sizeof (struct blkhdr);
+ bh = (void *) (bh + 1) + bh->size;
+ }
+
+ if (size + sizeof (struct pagehdr) != ph->size)
+ panic ("%s:%d: memory list destroyed", __FILE__, line);
+ }
+}
+#else
+#define check_page_list(line)
+#endif
+
+/* Merge adjacent free blocks in the memory list. */
+static void
+coalesce_blocks ()
+{
+ struct pagehdr *ph;
+ struct blkhdr *bh, *bhp, *ebh;
+
+ num_block_coalesce++;
+
+ for (ph = memlist; ph; ph = ph->next)
+ {
+ bh = (struct blkhdr *) (ph + 1);
+ ebh = (struct blkhdr *) ((void *) ph + ph->size);
+ while (1)
+ {
+ /* Skip busy blocks. */
+ while (bh < ebh && ! bh->free)
+ bh = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
+ if (bh == ebh)
+ break;
+
+ /* Merge adjacent free blocks. */
+ while (1)
+ {
+ bhp = (struct blkhdr *) ((void *) (bh + 1) + bh->size);
+ if (bhp == ebh)
+ {
+ bh = bhp;
+ break;
+ }
+ if (! bhp->free)
+ {
+ bh = (struct blkhdr *) ((void *) (bhp + 1) + bhp->size);
+ break;
+ }
+ bh->size += bhp->size + sizeof (struct blkhdr);
+ }
+ }
+ }
+}
+
+/* Allocate SIZE bytes of memory.
+ The PRIORITY parameter specifies various flags
+ such as DMA, atomicity, etc. It is not used by Mach. */
+void *
+linux_kmalloc (unsigned int size, int priority)
+{
+ int order, coalesced = 0;
+ unsigned flags;
+ struct pagehdr *ph;
+ struct blkhdr *bh, *new_bh;
+
+ if (size < MIN_ALLOC)
+ size = MIN_ALLOC;
+ else
+ size = (size + sizeof (int) - 1) & ~(sizeof (int) - 1);
+
+ assert (size <= (MEM_CHUNK_SIZE
+ - sizeof (struct pagehdr)
+ - sizeof (struct blkhdr)));
+
+ save_flags (flags);
+ cli ();
+
+again:
+ check_page_list (__LINE__);
+
+ /* Walk the page list and find the first free block with size
+ greater than or equal to the one required. */
+ for (ph = memlist; ph; ph = ph->next)
+ {
+ bh = (struct blkhdr *) (ph + 1);
+ while (bh < (struct blkhdr *) ((void *) ph + ph->size))
+ {
+ if (bh->free && bh->size >= size)
+ {
+ bh->free = 0;
+ if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
+ {
+ /* Split the current block and create a new free block. */
+ new_bh = (void *) (bh + 1) + size;
+ new_bh->free = 1;
+ new_bh->size = bh->size - size - sizeof (struct blkhdr);
+ bh->size = size;
+ }
+
+ check_page_list (__LINE__);
+
+ restore_flags (flags);
+ return bh + 1;
+ }
+ bh = (void *) (bh + 1) + bh->size;
+ }
+ }
+
+ check_page_list (__LINE__);
+
+ /* Allocation failed; coalesce free blocks and try again. */
+ if (! coalesced)
+ {
+ coalesce_blocks ();
+ coalesced = 1;
+ goto again;
+ }
+
+ /* Allocate more pages. */
+ order = get_page_order (size
+ + sizeof (struct pagehdr)
+ + sizeof (struct blkhdr));
+ ph = (struct pagehdr *) __get_free_pages (GFP_KERNEL, order, ~0UL);
+ if (! ph)
+ {
+ restore_flags (flags);
+ return NULL;
+ }
+
+ ph->size = PAGE_SIZE << order;
+ ph->next = memlist;
+ memlist = ph;
+ bh = (struct blkhdr *) (ph + 1);
+ bh->free = 0;
+ bh->size = ph->size - sizeof (struct pagehdr) - sizeof (struct blkhdr);
+ if (bh->size - size >= MIN_ALLOC + sizeof (struct blkhdr))
+ {
+ new_bh = (void *) (bh + 1) + size;
+ new_bh->free = 1;
+ new_bh->size = bh->size - size - sizeof (struct blkhdr);
+ bh->size = size;
+ }
+
+ check_page_list (__LINE__);
+
+ restore_flags (flags);
+ return bh + 1;
+}
+
+/* Free memory P previously allocated by linux_kmalloc. */
+void
+linux_kfree (void *p)
+{
+ unsigned flags;
+ struct blkhdr *bh, *bhp;
+ struct pagehdr *ph;
+
+ assert (((int) p & (sizeof (int) - 1)) == 0);
+
+ save_flags (flags);
+ cli ();
+
+ check_page_list (__LINE__);
+
+ for (ph = memlist; ph; ph = ph->next)
+ if (p >= (void *) ph && p < (void *) ph + ph->size)
+ break;
+
+ assert (ph);
+
+ bh = (struct blkhdr *) p - 1;
+
+ assert (! bh->free);
+ assert (bh->size >= MIN_ALLOC);
+ assert ((bh->size & (sizeof (int) - 1)) == 0);
+
+ bh->free = 1;
+
+ check_page_list (__LINE__);
+
+ restore_flags (flags);
+}
+
+/* Free any pages that are not in use.
+ Called by __get_free_pages when pages are running low. */
+static void
+collect_kmalloc_pages ()
+{
+ struct blkhdr *bh;
+ struct pagehdr *ph, **prev_ph;
+
+ check_page_list (__LINE__);
+
+ coalesce_blocks ();
+
+ check_page_list (__LINE__);
+
+ ph = memlist;
+ prev_ph = &memlist;
+ while (ph)
+ {
+ bh = (struct blkhdr *) (ph + 1);
+ if (bh->free && (void *) (bh + 1) + bh->size == (void *) ph + ph->size)
+ {
+ *prev_ph = ph->next;
+ free_pages ((unsigned long) ph, get_page_order (ph->size));
+ ph = *prev_ph;
+ }
+ else
+ {
+ prev_ph = &ph->next;
+ ph = ph->next;
+ }
+ }
+
+ check_page_list (__LINE__);
+}
+
+/* Allocate ORDER + 1 number of physically contiguous pages.
+ PRIORITY and MAX_ADDR are not used in Mach.
+
+ XXX: This needs to be dynamic. To do that we need to make
+ the Mach page manipulation routines interrupt safe and they
+ must provide machine dependant hooks. */
+unsigned long
+__get_free_pages (int priority, unsigned long order, unsigned long max_addr)
+{
+ int i, pages_collected = 0;
+ unsigned flags, bits, off, j, len;
+
+ assert ((PAGE_SIZE << order) <= MEM_CHUNK_SIZE);
+
+ /* Construct bitmap of contiguous pages. */
+ bits = 0;
+ j = 0;
+ len = 0;
+ while (len < (PAGE_SIZE << order))
+ {
+ bits |= 1 << j++;
+ len += PAGE_SIZE;
+ }
+
+again:
+ save_flags (flags);
+ cli ();
+
+ /* Search each chunk for the required number of contiguous pages. */
+ for (i = 0; i < MEM_CHUNKS; i++)
+ {
+ off = 0;
+ j = bits;
+ while (MEM_CHUNK_SIZE - off >= (PAGE_SIZE << order))
+ {
+ if ((pages_free[i].bitmap & j) == j)
+ {
+ pages_free[i].bitmap &= ~j;
+ linux_mem_avail -= order + 1;
+ restore_flags (flags);
+ return pages_free[i].start + off;
+ }
+ j <<= 1;
+ off += PAGE_SIZE;
+ }
+ }
+
+ /* Allocation failed; collect kmalloc and buffer pages
+ and try again. */
+ if (! pages_collected)
+ {
+ num_page_collect++;
+ collect_kmalloc_pages ();
+ collect_buffer_pages ();
+ pages_collected = 1;
+ goto again;
+ }
+
+ printf ("%s:%d: __get_free_pages: ran out of pages\n", __FILE__, __LINE__);
+
+ restore_flags (flags);
+ return 0;
+}
+
+/* Free ORDER + 1 number of physically
+ contiguous pages starting at address ADDR. */
+void
+free_pages (unsigned long addr, unsigned long order)
+{
+ int i;
+ unsigned flags, bits, len, j;
+
+ assert ((addr & PAGE_MASK) == 0);
+
+ for (i = 0; i < MEM_CHUNKS; i++)
+ if (addr >= pages_free[i].start && addr < pages_free[i].end)
+ break;
+
+ assert (i < MEM_CHUNKS);
+
+ /* Contruct bitmap of contiguous pages. */
+ len = 0;
+ j = 0;
+ bits = 0;
+ while (len < (PAGE_SIZE << order))
+ {
+ bits |= 1 << j++;
+ len += PAGE_SIZE;
+ }
+ bits <<= (addr - pages_free[i].start) >> PAGE_SHIFT;
+
+ save_flags (flags);
+ cli ();
+
+ assert ((pages_free[i].bitmap & bits) == 0);
+
+ pages_free[i].bitmap |= bits;
+ linux_mem_avail += order + 1;
+ restore_flags (flags);
+}
+
+/* Allocate SIZE bytes of memory. The pages need not be contiguous. */
+void *
+vmalloc (unsigned long size)
+{
+ return (void *) __get_free_pages (GFP_KERNEL, get_page_order (size), ~0UL);
+}
diff --git a/i386/i386at/gpl/linux/linux_misc.c b/i386/i386at/gpl/linux/linux_misc.c
new file mode 100644
index 00000000..6e7b33b9
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_misc.c
@@ -0,0 +1,303 @@
+/*
+ * Miscellaneous routines and data for Linux emulation.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+/*
+ * linux/fs/proc/scsi.c
+ * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ *
+ * The original version was derived from linux/fs/proc/net.c,
+ * which is Copyright (C) 1991, 1992 Linus Torvalds.
+ * Much has been rewritten, but some of the code still remains.
+ *
+ * /proc/scsi directory handling functions
+ *
+ * last change: 95/07/04
+ *
+ * Initial version: March '95
+ * 95/05/15 Added subdirectories for each driver and show every
+ * registered HBA as a single file.
+ * 95/05/30 Added rudimentary write support for parameter passing
+ * 95/07/04 Fixed bugs in directory handling
+ * 95/09/13 Update to support the new proc-dir tree
+ *
+ * TODO: Improve support to write to the driver files
+ * Add some more comments
+ */
+
+/*
+ * linux/fs/buffer.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <sys/types.h>
+#include <mach/vm_param.h>
+#include <kern/thread.h>
+#include <vm/vm_map.h>
+#include <vm/vm_page.h>
+#include <device/device_types.h>
+#include <i386at/gpl/linux/linux_emul.h>
+
+#define MACH_INCLUDE
+#include <linux/errno.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/blk.h>
+#include <linux/proc_fs.h>
+#include <linux/kernel_stat.h>
+
+int (*dispatch_scsi_info_ptr) (int ino, char *buffer, char **start,
+ off_t offset, int length, int inout) = 0;
+
+struct kernel_stat kstat;
+
+int
+linux_to_mach_error (int err)
+{
+ switch (err)
+ {
+ case 0:
+ return D_SUCCESS;
+
+ case -LINUX_EPERM:
+ return D_INVALID_OPERATION;
+
+ case -LINUX_EIO:
+ return D_IO_ERROR;
+
+ case -LINUX_ENXIO:
+ return D_NO_SUCH_DEVICE;
+
+ case -LINUX_EACCES:
+ return D_INVALID_OPERATION;
+
+ case -LINUX_EFAULT:
+ return D_INVALID_SIZE;
+
+ case -LINUX_EBUSY:
+ return D_ALREADY_OPEN;
+
+ case -LINUX_EINVAL:
+ return D_INVALID_SIZE;
+
+ case -LINUX_EROFS:
+ return D_READ_ONLY;
+
+ case -LINUX_EWOULDBLOCK:
+ return D_WOULD_BLOCK;
+
+ default:
+ printf ("linux_to_mach_error: unknown code %d\n", err);
+ return D_IO_ERROR;
+ }
+}
+
+int
+issig ()
+{
+ return current_thread ()->wait_result != THREAD_AWAKENED;
+}
+
+int
+block_fsync (struct inode *inode, struct file *filp)
+{
+ return 0;
+}
+
+int
+verify_area (int rw, const void *p, unsigned long size)
+{
+ vm_prot_t prot = (rw == VERIFY_WRITE) ? VM_PROT_WRITE : VM_PROT_READ;
+ vm_offset_t addr = trunc_page ((vm_offset_t) p);
+ vm_size_t len = round_page ((vm_size_t) size);
+ vm_map_entry_t entry;
+
+ vm_map_lock_read (current_map ());
+
+ while (1)
+ {
+ if (! vm_map_lookup_entry (current_map (), addr, &entry)
+ || (entry->protection & prot) != prot)
+ {
+ vm_map_unlock_read (current_map ());
+ return -LINUX_EFAULT;
+ }
+ if (entry->vme_end - entry->vme_start >= len)
+ break;
+ len -= entry->vme_end - entry->vme_start;
+ addr += entry->vme_end - entry->vme_start;
+ }
+
+ vm_map_unlock_read (current_map ());
+ return 0;
+}
+
+/*
+ * Print device name (in decimal, hexadecimal or symbolic) -
+ * at present hexadecimal only.
+ * Note: returns pointer to static data!
+ */
+char *
+kdevname(kdev_t dev)
+{
+ static char buffer[32];
+ sprintf(buffer, "%02x:%02x", MAJOR(dev), MINOR(dev));
+ return buffer;
+}
+
+/* RO fail safe mechanism */
+
+static long ro_bits[MAX_BLKDEV][8];
+
+int
+is_read_only(kdev_t dev)
+{
+ int minor,major;
+
+ major = MAJOR(dev);
+ minor = MINOR(dev);
+ if (major < 0 || major >= MAX_BLKDEV) return 0;
+ return ro_bits[major][minor >> 5] & (1 << (minor & 31));
+}
+
+void
+set_device_ro(kdev_t dev,int flag)
+{
+ int minor,major;
+
+ major = MAJOR(dev);
+ minor = MINOR(dev);
+ if (major < 0 || major >= MAX_BLKDEV) return;
+ if (flag) ro_bits[major][minor >> 5] |= 1 << (minor & 31);
+ else ro_bits[major][minor >> 5] &= ~(1 << (minor & 31));
+}
+
+/*
+ * linux/lib/string.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/*
+ * stupid library routines.. The optimized versions should generally be found
+ * as inline code in <asm-xx/string.h>
+ *
+ * These are buggy as well..
+ */
+
+#include <linux/types.h>
+#include <linux/string.h>
+
+char * ___strtok = NULL;
+
+#ifndef __HAVE_ARCH_STRSPN
+size_t strspn(const char *s, const char *accept)
+{
+ const char *p;
+ const char *a;
+ size_t count = 0;
+
+ for (p = s; *p != '\0'; ++p) {
+ for (a = accept; *a != '\0'; ++a) {
+ if (*p == *a)
+ break;
+ }
+ if (*a == '\0')
+ return count;
+ ++count;
+ }
+
+ return count;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRPBRK
+char * strpbrk(const char * cs,const char * ct)
+{
+ const char *sc1,*sc2;
+
+ for( sc1 = cs; *sc1 != '\0'; ++sc1) {
+ for( sc2 = ct; *sc2 != '\0'; ++sc2) {
+ if (*sc1 == *sc2)
+ return (char *) sc1;
+ }
+ }
+ return NULL;
+}
+#endif
+
+#ifndef __HAVE_ARCH_STRTOK
+char * strtok(char * s,const char * ct)
+{
+ char *sbegin, *send;
+
+ sbegin = s ? s : ___strtok;
+ if (!sbegin) {
+ return NULL;
+ }
+ sbegin += strspn(sbegin,ct);
+ if (*sbegin == '\0') {
+ ___strtok = NULL;
+ return( NULL );
+ }
+ send = strpbrk( sbegin, ct);
+ if (send && *send != '\0')
+ *send++ = '\0';
+ ___strtok = send;
+ return (sbegin);
+}
+#endif
+
+struct proc_dir_entry proc_scsi;
+struct inode_operations proc_scsi_inode_operations;
+struct proc_dir_entry proc_net;
+struct inode_operations proc_net_inode_operations;
+
+int
+proc_register (struct proc_dir_entry *xxx1, struct proc_dir_entry *xxx2)
+{
+ return 0;
+}
+
+int
+proc_unregister (struct proc_dir_entry *xxx1, int xxx2)
+{
+ return 0;
+}
+
+void
+add_blkdev_randomness (int major)
+{
+}
+
+void
+do_gettimeofday (struct timeval *tv)
+{
+ host_get_time (1, tv);
+}
+
+int
+dev_get_info (char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ return 0;
+}
diff --git a/i386/i386at/gpl/linux/linux_net.c b/i386/i386at/gpl/linux/linux_net.c
new file mode 100644
index 00000000..6a83a98f
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_net.c
@@ -0,0 +1,520 @@
+/*
+ * Linux network driver support.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Ethernet-type device handling.
+ *
+ * Version: @(#)eth.c 1.0.7 05/25/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ * Florian La Roche, <rzsfl@rz.uni-sb.de>
+ * Alan Cox, <gw4pts@gw4pts.ampr.org>
+ *
+ * Fixes:
+ * Mr Linux : Arp problems
+ * Alan Cox : Generic queue tidyup (very tiny here)
+ * Alan Cox : eth_header ntohs should be htons
+ * Alan Cox : eth_rebuild_header missing an htons and
+ * minor other things.
+ * Tegge : Arp bug fixes.
+ * Florian : Removed many unnecessary functions, code cleanup
+ * and changes for new arp and skbuff.
+ * Alan Cox : Redid header building to reflect new format.
+ * Alan Cox : ARP only when compiled with CONFIG_INET
+ * Greg Page : 802.2 and SNAP stuff.
+ * Alan Cox : MAC layer pointers/new format.
+ * Paul Gortmaker : eth_copy_and_sum shouldn't csum padding.
+ * Alan Cox : Protect against forwarding explosions with
+ * older network drivers and IFF_ALLMULTI
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+
+#include <sys/types.h>
+
+#include <mach/mach_types.h>
+#include <mach/kern_return.h>
+#include <mach/mig_errors.h>
+#include <mach/port.h>
+#include <mach/vm_param.h>
+#include <mach/notify.h>
+
+#include <ipc/ipc_port.h>
+#include <ipc/ipc_space.h>
+
+#include <vm/vm_map.h>
+#include <vm/vm_kern.h>
+#include <vm/vm_page.h>
+
+#include <device/device_types.h>
+#include <device/device_port.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/if_hdr.h>
+#include <device/net_io.h>
+#include "device_reply.h"
+
+#include <i386at/dev_hdr.h>
+#include <i386at/device_emul.h>
+
+#include <i386at/gpl/linux/linux_emul.h>
+
+#define MACH_INCLUDE
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+/* One of these is associated with each instance of a device. */
+struct net_data
+{
+ ipc_port_t port; /* device port */
+ struct ifnet ifnet; /* Mach ifnet structure (needed for filters) */
+ struct device device; /* generic device structure */
+ struct linux_device *dev; /* Linux network device structure */
+};
+
+/* List of sk_buffs waiting to be freed. */
+static struct sk_buff_head skb_done_list;
+
+/* Forward declarations. */
+
+extern struct device_emulation_ops linux_net_emulation_ops;
+
+static int print_packet_size = 0;
+
+/* Linux kernel network support routines. */
+
+/* Requeue packet SKB for transmission after the interface DEV
+ has timed out. The priority of the packet is PRI.
+ In Mach, we simply drop the packet like the native drivers. */
+void
+dev_queue_xmit (struct sk_buff *skb, struct linux_device *dev, int pri)
+{
+ dev_kfree_skb (skb, FREE_WRITE);
+}
+
+/* Close the device DEV. */
+int
+dev_close (struct linux_device *dev)
+{
+ return 0;
+}
+
+/* Network software interrupt handler. */
+void
+net_bh (void *xxx)
+{
+ int len;
+ struct sk_buff *skb;
+ struct linux_device *dev;
+
+ /* Start transmission on interfaces. */
+ for (dev = dev_base; dev; dev = dev->next)
+ {
+ if (dev->base_addr && dev->base_addr != 0xffe0)
+ while (1)
+ {
+ skb = skb_dequeue (&dev->buffs[0]);
+ if (skb)
+ {
+ len = skb->len;
+ if ((*dev->hard_start_xmit) (skb, dev))
+ {
+ skb_queue_head (&dev->buffs[0], skb);
+ mark_bh (NET_BH);
+ break;
+ }
+ else if (print_packet_size)
+ printf ("net_bh: length %d\n", len);
+ }
+ else
+ break;
+ }
+ }
+}
+
+/* Free all sk_buffs on the done list.
+ This routine is called by the iodone thread in ds_routines.c. */
+void
+free_skbuffs ()
+{
+ struct sk_buff *skb;
+
+ while (1)
+ {
+ skb = skb_dequeue (&skb_done_list);
+ if (skb)
+ {
+ if (skb->copy)
+ {
+ vm_map_copy_discard (skb->copy);
+ skb->copy = NULL;
+ }
+ if (IP_VALID (skb->reply))
+ {
+ ds_device_write_reply (skb->reply, skb->reply_type, 0, skb->len);
+ skb->reply = IP_NULL;
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+ }
+ else
+ break;
+ }
+}
+
+/* Allocate an sk_buff with SIZE bytes of data space. */
+struct sk_buff *
+alloc_skb (unsigned int size, int priority)
+{
+ return dev_alloc_skb (size);
+}
+
+/* Free SKB. */
+void
+kfree_skb (struct sk_buff *skb, int priority)
+{
+ dev_kfree_skb (skb, priority);
+}
+
+/* Allocate an sk_buff with SIZE bytes of data space. */
+struct sk_buff *
+dev_alloc_skb (unsigned int size)
+{
+ struct sk_buff *skb;
+
+ skb = linux_kmalloc (sizeof (struct sk_buff) + size, GFP_KERNEL);
+ if (skb)
+ {
+ skb->dev = NULL;
+ skb->reply = IP_NULL;
+ skb->copy = NULL;
+ skb->len = size;
+ skb->prev = skb->next = NULL;
+ skb->list = NULL;
+ if (size)
+ {
+ skb->data = (unsigned char *) (skb + 1);
+ skb->tail = skb->data + size;
+ }
+ else
+ skb->data = skb->tail = NULL;
+ skb->head = skb->data;
+ }
+ return skb;
+}
+
+/* Free the sk_buff SKB. */
+void
+dev_kfree_skb (struct sk_buff *skb, int mode)
+{
+ unsigned flags;
+ extern void *io_done_list;
+
+ /* Queue sk_buff on done list if there is a
+ page list attached or we need to send a reply.
+ Wakeup the iodone thread to process the list. */
+ if (skb->copy || IP_VALID (skb->reply))
+ {
+ skb_queue_tail (&skb_done_list, skb);
+ save_flags (flags);
+ thread_wakeup ((event_t) &io_done_list);
+ restore_flags (flags);
+ return;
+ }
+ linux_kfree (skb);
+}
+
+/* Accept packet SKB received on an interface. */
+void
+netif_rx (struct sk_buff *skb)
+{
+ ipc_kmsg_t kmsg;
+ struct ether_header *eh;
+ struct packet_header *ph;
+ struct linux_device *dev = skb->dev;
+
+ assert (skb != NULL);
+
+ if (print_packet_size)
+ printf ("netif_rx: length %d\n", skb->len);
+
+ /* Allocate a kernel message buffer. */
+ kmsg = net_kmsg_get ();
+ if (! kmsg)
+ {
+ dev_kfree_skb (skb, FREE_READ);
+ return;
+ }
+
+ /* Copy packet into message buffer. */
+ eh = (struct ether_header *) (net_kmsg (kmsg)->header);
+ ph = (struct packet_header *) (net_kmsg (kmsg)->packet);
+ memcpy (eh, skb->data, sizeof (struct ether_header));
+ memcpy (ph + 1, skb->data + sizeof (struct ether_header),
+ skb->len - sizeof (struct ether_header));
+ ph->type = eh->ether_type;
+ ph->length = (skb->len - sizeof (struct ether_header)
+ + sizeof (struct packet_header));
+
+ dev_kfree_skb (skb, FREE_READ);
+
+ /* Pass packet up to the microkernel. */
+ net_packet (&dev->net_data->ifnet, kmsg,
+ ph->length, ethernet_priority (kmsg));
+}
+
+/* Mach device interface routines. */
+
+/* Return a send right associated with network device ND. */
+static ipc_port_t
+dev_to_port (void *nd)
+{
+ return (nd
+ ? ipc_port_make_send (((struct net_data *) nd)->port)
+ : IP_NULL);
+}
+
+static io_return_t
+device_open (ipc_port_t reply_port, mach_msg_type_name_t reply_port_type,
+ dev_mode_t mode, char *name, device_t *devp)
+{
+ io_return_t err = D_SUCCESS;
+ ipc_port_t notify;
+ struct ifnet *ifp;
+ struct linux_device *dev;
+ struct net_data *nd;
+
+ /* Search for the device. */
+ for (dev = dev_base; dev; dev = dev->next)
+ if (dev->base_addr
+ && dev->base_addr != 0xffe0
+ && ! strcmp (name, dev->name))
+ break;
+ if (! dev)
+ return D_NO_SUCH_DEVICE;
+
+ /* Allocate and initialize device data if this is the first open. */
+ nd = dev->net_data;
+ if (! nd)
+ {
+ dev->net_data = nd = ((struct net_data *)
+ kalloc (sizeof (struct net_data)));
+ if (! nd)
+ {
+ err = D_NO_MEMORY;
+ goto out;
+ }
+ nd->dev = dev;
+ nd->device.emul_data = nd;
+ nd->device.emul_ops = &linux_net_emulation_ops;
+ nd->port = ipc_port_alloc_kernel ();
+ if (nd->port == IP_NULL)
+ {
+ err = KERN_RESOURCE_SHORTAGE;
+ goto out;
+ }
+ ipc_kobject_set (nd->port, (ipc_kobject_t) &nd->device, IKOT_DEVICE);
+ notify = ipc_port_make_sonce (nd->port);
+ ip_lock (nd->port);
+ ipc_port_nsrequest (nd->port, 1, notify, &notify);
+ assert (notify == IP_NULL);
+
+ ifp = &nd->ifnet;
+ ifp->if_unit = dev->name[strlen (dev->name) - 1] - '0';
+ ifp->if_flags = IFF_UP|IFF_RUNNING;
+ ifp->if_mtu = dev->mtu;
+ ifp->if_header_size = dev->hard_header_len;
+ ifp->if_header_format = dev->type;
+ ifp->if_address_size = dev->addr_len;
+ ifp->if_address = dev->dev_addr;
+ if_init_queues (ifp);
+
+ if (dev->open)
+ {
+ linux_intr_pri = SPL6;
+ if ((*dev->open) (dev))
+ err = D_NO_SUCH_DEVICE;
+ }
+
+ out:
+ if (err)
+ {
+ if (nd)
+ {
+ if (nd->port != IP_NULL)
+ {
+ ipc_kobject_set (nd->port, IKO_NULL, IKOT_NONE);
+ ipc_port_dealloc_kernel (nd->port);
+ }
+ kfree ((vm_offset_t) nd, sizeof (struct net_data));
+ nd = NULL;
+ dev->net_data = NULL;
+ }
+ }
+ else
+ {
+ dev->flags |= LINUX_IFF_UP|LINUX_IFF_RUNNING;
+ skb_queue_head_init (&dev->buffs[0]);
+ }
+ if (IP_VALID (reply_port))
+ ds_device_open_reply (reply_port, reply_port_type,
+ err, dev_to_port (nd));
+ return MIG_NO_REPLY;
+ }
+
+ *devp = &nd->device;
+ return D_SUCCESS;
+}
+
+static io_return_t
+device_write (void *d, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ recnum_t bn, io_buf_ptr_t data, unsigned int count,
+ int *bytes_written)
+{
+ unsigned char *p;
+ int i, amt, skblen, s;
+ io_return_t err = 0;
+ vm_map_copy_t copy = (vm_map_copy_t) data;
+ struct net_data *nd = d;
+ struct linux_device *dev = nd->dev;
+ struct sk_buff *skb;
+
+ if (count == 0 || count > dev->mtu + dev->hard_header_len)
+ return D_INVALID_SIZE;
+
+ /* Allocate a sk_buff. */
+ amt = PAGE_SIZE - (copy->offset & PAGE_MASK);
+ skblen = (amt >= count) ? 0 : count;
+ skb = dev_alloc_skb (skblen);
+ if (! skb)
+ return D_NO_MEMORY;
+
+ /* Copy user data. This is only required if it spans multiple pages. */
+ if (skblen == 0)
+ {
+ assert (copy->cpy_npages == 1);
+
+ skb->copy = copy;
+ skb->data = ((void *) copy->cpy_page_list[0]->phys_addr
+ + (copy->offset & PAGE_MASK));
+ skb->len = count;
+ skb->head = skb->data;
+ skb->tail = skb->data + skb->len;
+ }
+ else
+ {
+ memcpy (skb->data,
+ ((void *) copy->cpy_page_list[0]->phys_addr
+ + (copy->offset & PAGE_MASK)),
+ amt);
+ count -= amt;
+ p = skb->data + amt;
+ for (i = 1; count > 0 && i < copy->cpy_npages; i++)
+ {
+ amt = PAGE_SIZE;
+ if (amt > count)
+ amt = count;
+ memcpy (p, (void *) copy->cpy_page_list[i]->phys_addr, amt);
+ count -= amt;
+ p += amt;
+ }
+
+ assert (count == 0);
+
+ vm_map_copy_discard (copy);
+ }
+
+ skb->dev = dev;
+ skb->reply = reply_port;
+ skb->reply_type = reply_port_type;
+
+ /* Queue packet for transmission and schedule a software interrupt. */
+ s = splimp ();
+ if (dev->buffs[0].next != (struct sk_buff *) &dev->buffs[0]
+ || (*dev->hard_start_xmit) (skb, dev))
+ {
+ __skb_queue_tail (&dev->buffs[0], skb);
+ mark_bh (NET_BH);
+ }
+ splx (s);
+
+ return MIG_NO_REPLY;
+}
+
+static io_return_t
+device_get_status (void *d, dev_flavor_t flavor, dev_status_t status,
+ mach_msg_type_number_t *count)
+{
+ return net_getstat (&((struct net_data *) d)->ifnet, flavor, status, count);
+}
+
+static io_return_t
+device_set_filter (void *d, ipc_port_t port, int priority,
+ filter_t *filter, unsigned filter_count)
+{
+ return net_set_filter (&((struct net_data *) d)->ifnet,
+ port, priority, filter, filter_count);
+}
+
+struct device_emulation_ops linux_net_emulation_ops =
+{
+ NULL,
+ NULL,
+ dev_to_port,
+ device_open,
+ NULL,
+ device_write,
+ NULL,
+ NULL,
+ NULL,
+ NULL,
+ device_get_status,
+ device_set_filter,
+ NULL,
+ NULL,
+ NULL,
+ NULL
+};
+
+/* Do any initialization required for network devices. */
+void
+linux_net_emulation_init ()
+{
+ skb_queue_head_init (&skb_done_list);
+}
diff --git a/i386/i386at/gpl/linux/linux_port.c b/i386/i386at/gpl/linux/linux_port.c
new file mode 100644
index 00000000..4a79c499
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_port.c
@@ -0,0 +1,79 @@
+/*
+ * Linux I/O port management.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <linux/ioport.h>
+
+#define NPORTS 65536
+#define BITS_PER_WORD 32
+#define NWORDS (NPORTS / BITS_PER_WORD)
+
+/*
+ * This bitmap keeps track of all allocated ports.
+ * A bit is set if the port has been allocated.
+ */
+static unsigned port_bitmap[NWORDS];
+
+void snarf_region(unsigned, unsigned);
+
+/*
+ * Check if a region is available for use.
+ */
+int
+check_region(unsigned port, unsigned size)
+{
+ unsigned i;
+
+ for (i = port; i < port + size; i++)
+ if (port_bitmap[i/BITS_PER_WORD] & (1 << (i%BITS_PER_WORD)))
+ return (1);
+ return (0);
+}
+
+/*
+ * Allocate a region.
+ */
+void
+request_region(unsigned port, unsigned size, const char *name)
+{
+ unsigned i;
+
+ for (i = port; i < port + size; i++)
+ port_bitmap[i / BITS_PER_WORD] |= 1 << (i % BITS_PER_WORD);
+}
+
+/*
+ * For compatibility with older kernels.
+ */
+void
+snarf_region(unsigned port, unsigned size)
+{
+ request_region(port, size, 0);
+}
+
+/*
+ * Deallocate a region.
+ */
+void
+release_region(unsigned port, unsigned size)
+{
+ unsigned i;
+
+ for (i = port; i < port + size; i++)
+ port_bitmap[i / BITS_PER_WORD] &= ~(1 << (i % BITS_PER_WORD));
+}
diff --git a/i386/i386at/gpl/linux/linux_printk.c b/i386/i386at/gpl/linux/linux_printk.c
new file mode 100644
index 00000000..c4e489d2
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_printk.c
@@ -0,0 +1,47 @@
+/*
+ * Linux kernel print routine.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * linux/kernel/printk.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <stdarg.h>
+#include <asm/system.h>
+
+static char buf[2048];
+
+void
+printk(char *fmt, ...)
+{
+ va_list args;
+ int i, n, flags;
+ extern void cnputc();
+ extern int linux_vsprintf(char *buf, char *fmt, ...);
+
+ save_flags(flags);
+ cli();
+ va_start(args, fmt);
+ n = linux_vsprintf(buf, fmt, args);
+ va_end(args);
+ for (i = 0; i < n; i++)
+ cnputc(buf[i]);
+ restore_flags(flags);
+}
diff --git a/i386/i386at/gpl/linux/linux_sched.c b/i386/i386at/gpl/linux/linux_sched.c
new file mode 100644
index 00000000..fdb0f693
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_sched.c
@@ -0,0 +1,237 @@
+/*
+ * Linux scheduling support.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+/*
+ * linux/kernel/sched.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <sys/types.h>
+
+#include <mach/boolean.h>
+
+#include <kern/thread.h>
+#include <kern/sched_prim.h>
+
+#include <i386at/gpl/linux/linux_emul.h>
+
+#define MACH_INCLUDE
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/blkdev.h>
+
+#include <asm/system.h>
+
+struct tq_struct tq_last =
+{
+ &tq_last, 0, 0, 0
+};
+
+DECLARE_TASK_QUEUE(tq_timer);
+
+static struct wait_queue **auto_config_queue;
+
+void
+tqueue_bh (void *unused)
+{
+ run_task_queue(&tq_timer);
+}
+
+void
+add_wait_queue (struct wait_queue **q, struct wait_queue *wait)
+{
+ unsigned long flags;
+
+ if (! linux_auto_config)
+ {
+ save_flags (flags);
+ cli ();
+ assert_wait ((event_t) q, FALSE);
+ restore_flags (flags);
+ return;
+ }
+
+ if (auto_config_queue)
+ printf ("add_wait_queue: queue not empty\n");
+ auto_config_queue = q;
+}
+
+void
+remove_wait_queue (struct wait_queue **q, struct wait_queue *wait)
+{
+ unsigned long flags;
+
+ if (! linux_auto_config)
+ {
+ save_flags (flags);
+ thread_wakeup ((event_t) q);
+ restore_flags (flags);
+ return;
+ }
+
+ auto_config_queue = NULL;
+}
+
+void
+__down (struct semaphore *sem)
+{
+ int s;
+ unsigned long flags;
+
+ if (! linux_auto_config)
+ {
+ save_flags (flags);
+ s = splhigh ();
+ while (sem->count <= 0)
+ {
+ assert_wait ((event_t) &sem->wait, FALSE);
+ splx (s);
+ thread_block (0);
+ s = splhigh ();
+ }
+ splx (s);
+ restore_flags (flags);
+ return;
+ }
+
+ while (sem->count <= 0)
+ barrier ();
+}
+
+void
+__sleep_on (struct wait_queue **q, int interruptible)
+{
+ unsigned long flags;
+
+ if (! q)
+ return;
+ save_flags (flags);
+ if (! linux_auto_config)
+ {
+ assert_wait ((event_t) q, interruptible);
+ sti ();
+ thread_block (0);
+ restore_flags (flags);
+ return;
+ }
+
+ add_wait_queue (q, NULL);
+ sti ();
+ while (auto_config_queue)
+ barrier ();
+ restore_flags (flags);
+}
+
+void
+sleep_on (struct wait_queue **q)
+{
+ __sleep_on (q, FALSE);
+}
+
+void
+interruptible_sleep_on (struct wait_queue **q)
+{
+ __sleep_on (q, TRUE);
+}
+
+void
+wake_up (struct wait_queue **q)
+{
+ unsigned long flags;
+
+ if (! linux_auto_config)
+ {
+ if (q != &wait_for_request)
+ {
+ save_flags (flags);
+ thread_wakeup ((event_t) q);
+ restore_flags (flags);
+ }
+ return;
+ }
+
+ if (auto_config_queue == q)
+ auto_config_queue = NULL;
+}
+
+void
+__wait_on_buffer (struct buffer_head *bh)
+{
+ unsigned long flags;
+
+ save_flags (flags);
+ if (! linux_auto_config)
+ {
+ while (1)
+ {
+ cli ();
+ if (! buffer_locked (bh))
+ break;
+ bh->b_wait = (struct wait_queue *) 1;
+ assert_wait ((event_t) bh, FALSE);
+ sti ();
+ thread_block (0);
+ }
+ restore_flags (flags);
+ return;
+ }
+
+ sti ();
+ while (buffer_locked (bh))
+ barrier ();
+ restore_flags (flags);
+}
+
+void
+unlock_buffer (struct buffer_head *bh)
+{
+ unsigned long flags;
+
+ save_flags (flags);
+ cli ();
+ clear_bit (BH_Lock, &bh->b_state);
+ if (bh->b_wait && ! linux_auto_config)
+ {
+ bh->b_wait = NULL;
+ thread_wakeup ((event_t) bh);
+ }
+ restore_flags (flags);
+}
+
+void
+schedule ()
+{
+ if (! linux_auto_config)
+ thread_block (0);
+}
+
+void
+cdrom_sleep (int t)
+{
+ int xxx;
+
+ assert_wait ((event_t) &xxx, TRUE);
+ thread_set_timeout (t);
+ thread_block (0);
+}
diff --git a/i386/i386at/gpl/linux/linux_soft.c b/i386/i386at/gpl/linux/linux_soft.c
new file mode 100644
index 00000000..efcae987
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_soft.c
@@ -0,0 +1,74 @@
+/*
+ * Linux software interrupts.
+ * Copyright (C) 1995 Shantanu Goel.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+/*
+ * linux/kernel/softirq.c
+ *
+ * Copyright (C) 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+
+/*
+ * Mask of pending interrupts.
+ */
+unsigned long bh_active = 0;
+
+/*
+ * Mask of enabled interrupts.
+ */
+unsigned long bh_mask = 0;
+
+/*
+ * List of software interrupt handlers.
+ */
+struct bh_struct bh_base[32];
+
+
+/*
+ * Software interrupt handler.
+ */
+void
+linux_soft_intr()
+{
+ unsigned long active;
+ unsigned long mask, left;
+ struct bh_struct *bh;
+
+ bh = bh_base;
+ active = bh_active & bh_mask;
+ for (mask = 1, left = ~0;
+ left & active; bh++, mask += mask, left += left) {
+ if (mask & active) {
+ void (*fn)(void *);
+
+ bh_active &= ~mask;
+ fn = bh->routine;
+ if (fn == 0)
+ goto bad_bh;
+ (*fn)(bh->data);
+ }
+ }
+ return;
+ bad_bh:
+ printf("linux_soft_intr: bad interrupt handler entry 0x%08lx\n", mask);
+}
diff --git a/i386/i386at/gpl/linux/linux_timer.c b/i386/i386at/gpl/linux/linux_timer.c
new file mode 100644
index 00000000..c1575323
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_timer.c
@@ -0,0 +1,190 @@
+/*
+ * Linux timers.
+ *
+ * Copyright (C) 1996 The University of Utah and the Computer Systems
+ * Laboratory at the University of Utah (CSL)
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+/*
+ * linux/kernel/sched.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/interrupt.h>
+#include <asm/system.h>
+
+unsigned long volatile jiffies = 0;
+
+/*
+ * Mask of active timers.
+ */
+unsigned long timer_active = 0;
+
+/*
+ * List of timeout routines.
+ */
+struct timer_struct timer_table[32];
+
+/*
+ * The head for the timer-list has a "expires" field of MAX_UINT,
+ * and the sorting routine counts on this..
+ */
+static struct timer_list timer_head =
+{
+ &timer_head, &timer_head, ~0, 0, NULL
+};
+
+#define SLOW_BUT_DEBUGGING_TIMERS 0
+
+void
+add_timer(struct timer_list *timer)
+{
+ unsigned long flags;
+ struct timer_list *p;
+
+#if SLOW_BUT_DEBUGGING_TIMERS
+ if (timer->next || timer->prev) {
+ printk("add_timer() called with non-zero list from %p\n",
+ __builtin_return_address(0));
+ return;
+ }
+#endif
+ p = &timer_head;
+ save_flags(flags);
+ cli();
+ do {
+ p = p->next;
+ } while (timer->expires > p->expires);
+ timer->next = p;
+ timer->prev = p->prev;
+ p->prev = timer;
+ timer->prev->next = timer;
+ restore_flags(flags);
+}
+
+int
+del_timer(struct timer_list *timer)
+{
+ unsigned long flags;
+#if SLOW_BUT_DEBUGGING_TIMERS
+ struct timer_list * p;
+
+ p = &timer_head;
+ save_flags(flags);
+ cli();
+ while ((p = p->next) != &timer_head) {
+ if (p == timer) {
+ timer->next->prev = timer->prev;
+ timer->prev->next = timer->next;
+ timer->next = timer->prev = NULL;
+ restore_flags(flags);
+ return 1;
+ }
+ }
+ if (timer->next || timer->prev)
+ printk("del_timer() called from %p with timer not initialized\n",
+ __builtin_return_address(0));
+ restore_flags(flags);
+ return 0;
+#else
+ struct timer_list * next;
+ int ret = 0;
+ save_flags(flags);
+ cli();
+ if ((next = timer->next) != NULL) {
+ (next->prev = timer->prev)->next = next;
+ timer->next = timer->prev = NULL;
+ ret = 1;
+ }
+ restore_flags(flags);
+ return ret;
+#endif
+}
+
+/*
+ * Timer software interrupt handler.
+ */
+void
+timer_bh()
+{
+ unsigned long mask;
+ struct timer_struct *tp;
+ struct timer_list * timer;
+
+ cli();
+ while ((timer = timer_head.next) != &timer_head
+ && timer->expires <= jiffies) {
+ void (*fn)(unsigned long) = timer->function;
+ unsigned long data = timer->data;
+
+ timer->next->prev = timer->prev;
+ timer->prev->next = timer->next;
+ timer->next = timer->prev = NULL;
+ sti();
+ fn(data);
+ cli();
+ }
+ sti();
+
+ for (mask = 1, tp = timer_table; mask; tp++, mask <<= 1) {
+ if (mask > timer_active)
+ break;
+ if ((mask & timer_active)
+ && tp->expires > jiffies) {
+ timer_active &= ~mask;
+ (*tp->fn)();
+ sti();
+ }
+ }
+}
+
+int linux_timer_print = 0;
+
+/*
+ * Timer interrupt handler.
+ */
+void
+linux_timer_intr()
+{
+ unsigned long mask;
+ struct timer_struct *tp;
+ extern int pic_mask[];
+
+ jiffies++;
+
+ for (mask = 1, tp = timer_table; mask; tp++, mask += mask) {
+ if (mask > timer_active)
+ break;
+ if (!(mask & timer_active))
+ continue;
+ if (tp->expires > jiffies)
+ continue;
+ mark_bh(TIMER_BH);
+ }
+ if (timer_head.next->expires <= jiffies)
+ mark_bh(TIMER_BH);
+ if (tq_timer != &tq_last)
+ mark_bh(TQUEUE_BH);
+ if (linux_timer_print)
+ printf ("linux_timer_intr: pic_mask[0] %x\n", pic_mask[0]);
+}
+
diff --git a/i386/i386at/gpl/linux/linux_version.c b/i386/i386at/gpl/linux/linux_version.c
new file mode 100644
index 00000000..0195c42f
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_version.c
@@ -0,0 +1,33 @@
+/*
+ * Copyright (c) 1996 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+#include <linux/utsname.h>
+#include <linux/string.h>
+
+struct new_utsname system_utsname;
+
+void
+linux_version_init ()
+{
+ strcpy (system_utsname.version, "1.3.68");
+}
diff --git a/i386/i386at/gpl/linux/linux_vsprintf.c b/i386/i386at/gpl/linux/linux_vsprintf.c
new file mode 100644
index 00000000..236d38da
--- /dev/null
+++ b/i386/i386at/gpl/linux/linux_vsprintf.c
@@ -0,0 +1,341 @@
+/*
+ * linux/kernel/vsprintf.c
+ *
+ * Copyright (C) 1991, 1992 Linus Torvalds
+ */
+
+/* vsprintf.c -- Lars Wirzenius & Linus Torvalds. */
+/*
+ * Wirzenius wrote this portably, Torvalds fucked it up :-)
+ */
+
+#include <stdarg.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/kernel.h>
+
+static inline
+isxdigit(c)
+ char c;
+{
+ return ((c >= '0' && c <= '9')
+ || (c >= 'a' && c <= 'f') || (c >= 'A' && c <= 'F'));
+}
+
+static inline
+islower(c)
+ char c;
+{
+ return (c >= 'a' && c <= 'z');
+}
+
+static inline
+toupper(c)
+ char c;
+{
+ return (islower(c) ? c - 'a' + 'A' : c);
+}
+
+static inline
+isdigit(c)
+ char c;
+{
+ return (c >= '0' && c <= '9');
+}
+
+unsigned long
+simple_strtoul(const char *cp,char **endp,unsigned int base)
+{
+ unsigned long result = 0,value;
+
+ if (!base) {
+ base = 10;
+ if (*cp == '0') {
+ base = 8;
+ cp++;
+ if ((*cp == 'x') && isxdigit(cp[1])) {
+ cp++;
+ base = 16;
+ }
+ }
+ }
+ while (isxdigit(*cp) && (value = isdigit(*cp) ? *cp-'0' : (islower(*cp)
+ ? toupper(*cp) : *cp)-'A'+10) < base) {
+ result = result*base + value;
+ cp++;
+ }
+ if (endp)
+ *endp = (char *)cp;
+ return result;
+}
+
+/* we use this so that we can do without the ctype library */
+#define is_digit(c) ((c) >= '0' && (c) <= '9')
+
+static int
+skip_atoi(const char **s)
+{
+ int i=0;
+
+ while (is_digit(**s))
+ i = i*10 + *((*s)++) - '0';
+ return i;
+}
+
+#define ZEROPAD 1 /* pad with zero */
+#define SIGN 2 /* unsigned/signed long */
+#define PLUS 4 /* show plus */
+#define SPACE 8 /* space if plus */
+#define LEFT 16 /* left justified */
+#define SPECIAL 32 /* 0x */
+#define LARGE 64 /* use 'ABCDEF' instead of 'abcdef' */
+
+#define do_div(n,base) ({ \
+int __res; \
+__res = ((unsigned long) n) % (unsigned) base; \
+n = ((unsigned long) n) / (unsigned) base; \
+__res; })
+
+static char *
+number(char * str, long num, int base, int size, int precision, int type)
+{
+ char c,sign,tmp[36];
+ const char *digits="0123456789abcdefghijklmnopqrstuvwxyz";
+ int i;
+
+ if (type & LARGE)
+ digits = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
+ if (type & LEFT)
+ type &= ~ZEROPAD;
+ if (base < 2 || base > 36)
+ return 0;
+ c = (type & ZEROPAD) ? '0' : ' ';
+ sign = 0;
+ if (type & SIGN) {
+ if (num < 0) {
+ sign = '-';
+ num = -num;
+ size--;
+ } else if (type & PLUS) {
+ sign = '+';
+ size--;
+ } else if (type & SPACE) {
+ sign = ' ';
+ size--;
+ }
+ }
+ if (type & SPECIAL) {
+ if (base == 16)
+ size -= 2;
+ else if (base == 8)
+ size--;
+ }
+ i = 0;
+ if (num == 0)
+ tmp[i++]='0';
+ else while (num != 0)
+ tmp[i++] = digits[do_div(num,base)];
+ if (i > precision)
+ precision = i;
+ size -= precision;
+ if (!(type&(ZEROPAD+LEFT)))
+ while(size-->0)
+ *str++ = ' ';
+ if (sign)
+ *str++ = sign;
+ if (type & SPECIAL)
+ if (base==8)
+ *str++ = '0';
+ else if (base==16) {
+ *str++ = '0';
+ *str++ = digits[33];
+ }
+ if (!(type & LEFT))
+ while (size-- > 0)
+ *str++ = c;
+ while (i < precision--)
+ *str++ = '0';
+ while (i-- > 0)
+ *str++ = tmp[i];
+ while (size-- > 0)
+ *str++ = ' ';
+ return str;
+}
+
+int
+linux_vsprintf(char *buf, const char *fmt, va_list args)
+{
+ int len;
+ unsigned long num;
+ int i, base;
+ char * str;
+ char *s;
+
+ int flags; /* flags to number() */
+
+ int field_width; /* width of output field */
+ int precision; /* min. # of digits for integers; max
+ number of chars for from string */
+ int qualifier; /* 'h', 'l', or 'L' for integer fields */
+
+ for (str=buf ; *fmt ; ++fmt) {
+ if (*fmt != '%') {
+ *str++ = *fmt;
+ continue;
+ }
+
+ /* process flags */
+ flags = 0;
+ repeat:
+ ++fmt; /* this also skips first '%' */
+ switch (*fmt) {
+ case '-': flags |= LEFT; goto repeat;
+ case '+': flags |= PLUS; goto repeat;
+ case ' ': flags |= SPACE; goto repeat;
+ case '#': flags |= SPECIAL; goto repeat;
+ case '0': flags |= ZEROPAD; goto repeat;
+ }
+
+ /* get field width */
+ field_width = -1;
+ if (is_digit(*fmt))
+ field_width = skip_atoi(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ field_width = va_arg(args, int);
+ if (field_width < 0) {
+ field_width = -field_width;
+ flags |= LEFT;
+ }
+ }
+
+ /* get the precision */
+ precision = -1;
+ if (*fmt == '.') {
+ ++fmt;
+ if (is_digit(*fmt))
+ precision = skip_atoi(&fmt);
+ else if (*fmt == '*') {
+ ++fmt;
+ /* it's the next argument */
+ precision = va_arg(args, int);
+ }
+ if (precision < 0)
+ precision = 0;
+ }
+
+ /* get the conversion qualifier */
+ qualifier = -1;
+ if (*fmt == 'h' || *fmt == 'l' || *fmt == 'L') {
+ qualifier = *fmt;
+ ++fmt;
+ }
+
+ /* default base */
+ base = 10;
+
+ switch (*fmt) {
+ case 'c':
+ if (!(flags & LEFT))
+ while (--field_width > 0)
+ *str++ = ' ';
+ *str++ = (unsigned char) va_arg(args, int);
+ while (--field_width > 0)
+ *str++ = ' ';
+ continue;
+
+ case 's':
+ s = va_arg(args, char *);
+ if (!s)
+ s = "<NULL>";
+ len = strlen(s);
+ if (precision < 0)
+ precision = len;
+ else if (len > precision)
+ len = precision;
+
+ if (!(flags & LEFT))
+ while (len < field_width--)
+ *str++ = ' ';
+ for (i = 0; i < len; ++i)
+ *str++ = *s++;
+ while (len < field_width--)
+ *str++ = ' ';
+ continue;
+
+ case 'p':
+ if (field_width == -1) {
+ field_width = 2*sizeof(void *);
+ flags |= ZEROPAD;
+ }
+ str = number(str,
+ (unsigned long) va_arg(args, void *), 16,
+ field_width, precision, flags);
+ continue;
+
+
+ case 'n':
+ if (qualifier == 'l') {
+ long * ip = va_arg(args, long *);
+ *ip = (str - buf);
+ } else {
+ int * ip = va_arg(args, int *);
+ *ip = (str - buf);
+ }
+ continue;
+
+ /* integer number formats - set up the flags and "break" */
+ case 'o':
+ base = 8;
+ break;
+
+ case 'X':
+ flags |= LARGE;
+ case 'x':
+ base = 16;
+ break;
+
+ case 'd':
+ case 'i':
+ flags |= SIGN;
+ case 'u':
+ break;
+
+ default:
+ if (*fmt != '%')
+ *str++ = '%';
+ if (*fmt)
+ *str++ = *fmt;
+ else
+ --fmt;
+ continue;
+ }
+ if (qualifier == 'l')
+ num = va_arg(args, unsigned long);
+ else if (qualifier == 'h')
+ if (flags & SIGN)
+ num = va_arg(args, short);
+ else
+ num = va_arg(args, unsigned short);
+ else if (flags & SIGN)
+ num = va_arg(args, int);
+ else
+ num = va_arg(args, unsigned int);
+ str = number(str, num, base, field_width, precision, flags);
+ }
+ *str = '\0';
+ return str-buf;
+}
+
+int
+linux_sprintf(char * buf, const char *fmt, ...)
+{
+ va_list args;
+ int i;
+
+ va_start(args, fmt);
+ i = linux_vsprintf(buf, fmt, args);
+ va_end(args);
+ return i;
+}
diff --git a/i386/i386at/gpl/linux/net/3c501.c b/i386/i386at/gpl/linux/net/3c501.c
new file mode 100644
index 00000000..6f8dceb6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c501.c
@@ -0,0 +1,860 @@
+/* 3c501.c: A 3Com 3c501 ethernet driver for linux. */
+/*
+ Written 1992,1993,1994 Donald Becker
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a device driver for the 3Com Etherlink 3c501.
+ Do not purchase this card, even as a joke. It's performance is horrible,
+ and it breaks in many ways.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Fixed (again!) the missing interrupt locking on TX/RX shifting.
+ Alan Cox <Alan.Cox@linux.org>
+
+ Removed calls to init_etherdev since they are no longer needed, and
+ cleaned up modularization just a bit. The driver still allows only
+ the default address for cards when loaded as a module, but that's
+ really less braindead than anyone using a 3c501 board. :)
+ 19950208 (invid@msen.com)
+
+ Added traps for interrupts hitting the window as we clear and TX load
+ the board. Now getting 150K/second FTP with a 3c501 card. Still playing
+ with a TX-TX optimisation to see if we can touch 180-200K/second as seems
+ theoretically maximum.
+ 19950402 Alan Cox <Alan.Cox@linux.org>
+
+ Some notes on this thing if you have to hack it. [Alan]
+
+ 1] Some documentation is available from 3Com. Due to the boards age
+ standard responses when you ask for this will range from 'be serious'
+ to 'give it to a museum'. The documentation is incomplete and mostly
+ of historical interest anyway.
+
+ 2] The basic system is a single buffer which can be used to receive or
+ transmit a packet. A third command mode exists when you are setting
+ things up.
+
+ 3] If it's transmitting it's not receiving and vice versa. In fact the
+ time to get the board back into useful state after an operation is
+ quite large.
+
+ 4] The driver works by keeping the board in receive mode waiting for a
+ packet to arrive. When one arrives it is copied out of the buffer
+ and delivered to the kernel. The card is reloaded and off we go.
+
+ 5] When transmitting dev->tbusy is set and the card is reset (from
+ receive mode) [possibly losing a packet just received] to command
+ mode. A packet is loaded and transmit mode triggered. The interrupt
+ handler runs different code for transmit interrupts and can handle
+ returning to receive mode or retransmissions (yes you have to help
+ out with those too).
+
+ Problems:
+ There are a wide variety of undocumented error returns from the card
+ and you basically have to kick the board and pray if they turn up. Most
+ only occur under extreme load or if you do something the board doesn't
+ like (eg touching a register at the wrong time).
+
+ The driver is less efficient than it could be. It switches through
+ receive mode even if more transmits are queued. If this worries you buy
+ a real ethernet card.
+
+ The combination of slow receive restart and no real multicast
+ filter makes the board unusable with a kernel compiled for IP
+ multicasting in a real multicast environment. Thats down to the board,
+ but even with no multicast programs running a multicast IP kernel is
+ in group 224.0.0.1 and you will therefore be listening to all multicasts.
+ One nv conference running over that ethernet and you can give up.
+
+*/
+
+static const char *version =
+ "3c501.c: 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov).\n";
+
+/*
+ * Braindamage remaining:
+ * The 3c501 board.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/errno.h>
+#include <linux/config.h> /* for CONFIG_IP_MULTICAST */
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#define BLOCKOUT_2
+
+/* A zero-terminated list of I/O addresses to be probed.
+ The 3c501 can be at many locations, but here are the popular ones. */
+static unsigned int netcard_portlist[] =
+ { 0x280, 0x300, 0};
+
+
+/*
+ * Index to functions.
+ */
+
+int el1_probe(struct device *dev);
+static int el1_probe1(struct device *dev, int ioaddr);
+static int el_open(struct device *dev);
+static int el_start_xmit(struct sk_buff *skb, struct device *dev);
+static void el_interrupt(int irq, struct pt_regs *regs);
+static void el_receive(struct device *dev);
+static void el_reset(struct device *dev);
+static int el1_close(struct device *dev);
+static struct enet_statistics *el1_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+#define EL1_IO_EXTENT 16
+
+#ifndef EL_DEBUG
+#define EL_DEBUG 0 /* use 0 for production, 1 for devel., >2 for debug */
+#endif /* Anything above 5 is wordy death! */
+static int el_debug = EL_DEBUG;
+
+/*
+ * Board-specific info in dev->priv.
+ */
+
+struct net_local
+{
+ struct enet_statistics stats;
+ int tx_pkt_start; /* The length of the current Tx packet. */
+ int collisions; /* Tx collisions this packet */
+ int loading; /* Spot buffer load collisions */
+};
+
+
+#define RX_STATUS (ioaddr + 0x06)
+#define RX_CMD RX_STATUS
+#define TX_STATUS (ioaddr + 0x07)
+#define TX_CMD TX_STATUS
+#define GP_LOW (ioaddr + 0x08)
+#define GP_HIGH (ioaddr + 0x09)
+#define RX_BUF_CLR (ioaddr + 0x0A)
+#define RX_LOW (ioaddr + 0x0A)
+#define RX_HIGH (ioaddr + 0x0B)
+#define SAPROM (ioaddr + 0x0C)
+#define AX_STATUS (ioaddr + 0x0E)
+#define AX_CMD AX_STATUS
+#define DATAPORT (ioaddr + 0x0F)
+#define TX_RDY 0x08 /* In TX_STATUS */
+
+#define EL1_DATAPTR 0x08
+#define EL1_RXPTR 0x0A
+#define EL1_SAPROM 0x0C
+#define EL1_DATAPORT 0x0f
+
+/*
+ * Writes to the ax command register.
+ */
+
+#define AX_OFF 0x00 /* Irq off, buffer access on */
+#define AX_SYS 0x40 /* Load the buffer */
+#define AX_XMIT 0x44 /* Transmit a packet */
+#define AX_RX 0x48 /* Receive a packet */
+#define AX_LOOP 0x0C /* Loopback mode */
+#define AX_RESET 0x80
+
+/*
+ * Normal receive mode written to RX_STATUS. We must intr on short packets
+ * to avoid bogus rx lockups.
+ */
+
+#define RX_NORM 0xA8 /* 0x68 == all addrs, 0xA8 only to me. */
+#define RX_PROM 0x68 /* Senior Prom, uhmm promiscuous mode. */
+#define RX_MULT 0xE8 /* Accept multicast packets. */
+#define TX_NORM 0x0A /* Interrupt on everything that might hang the chip */
+
+/*
+ * TX_STATUS register.
+ */
+
+#define TX_COLLISION 0x02
+#define TX_16COLLISIONS 0x04
+#define TX_READY 0x08
+
+#define RX_RUNT 0x08
+#define RX_MISSED 0x01 /* Missed a packet due to 3c501 braindamage. */
+#define RX_GOOD 0x30 /* Good packet 0x20, or simple overflow 0x10. */
+
+
+/*
+ * The boilerplate probe code.
+ */
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry el1_drv = {"3c501", el1_probe1, EL1_IO_EXTENT, netcard_portlist};
+#else
+
+int el1_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el1_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; netcard_portlist[i]; i++)
+ {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, EL1_IO_EXTENT))
+ continue;
+ if (el1_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/*
+ * The actual probe.
+ */
+
+static int el1_probe1(struct device *dev, int ioaddr)
+{
+#ifndef MODULE
+
+ const char *mname; /* Vendor name */
+ unsigned char station_addr[6];
+ int autoirq = 0;
+ int i;
+
+ /*
+ * Read the station address PROM data from the special port.
+ */
+
+ for (i = 0; i < 6; i++)
+ {
+ outw(i, ioaddr + EL1_DATAPTR);
+ station_addr[i] = inb(ioaddr + EL1_SAPROM);
+ }
+ /*
+ * Check the first three octets of the S.A. for 3Com's prefix, or
+ * for the Sager NP943 prefix.
+ */
+
+ if (station_addr[0] == 0x02 && station_addr[1] == 0x60
+ && station_addr[2] == 0x8c)
+ {
+ mname = "3c501";
+ } else if (station_addr[0] == 0x00 && station_addr[1] == 0x80
+ && station_addr[2] == 0xC8)
+ {
+ mname = "NP943";
+ }
+ else
+ return ENODEV;
+
+ /*
+ * Grab the region so we can find the another board if autoIRQ fails.
+ */
+
+ request_region(ioaddr, EL1_IO_EXTENT,"3c501");
+
+ /*
+ * We auto-IRQ by shutting off the interrupt line and letting it float
+ * high.
+ */
+
+ if (dev->irq < 2)
+ {
+ autoirq_setup(2);
+ inb(RX_STATUS); /* Clear pending interrupts. */
+ inb(TX_STATUS);
+ outb(AX_LOOP + 1, AX_CMD);
+
+ outb(0x00, AX_CMD);
+
+ autoirq = autoirq_report(1);
+
+ if (autoirq == 0)
+ {
+ printk("%s probe at %#x failed to detect IRQ line.\n",
+ mname, ioaddr);
+ return EAGAIN;
+ }
+ }
+
+ outb(AX_RESET+AX_LOOP, AX_CMD); /* Loopback mode. */
+ dev->base_addr = ioaddr;
+ memcpy(dev->dev_addr, station_addr, ETH_ALEN);
+
+ if (dev->mem_start & 0xf)
+ el_debug = dev->mem_start & 0x7;
+ if (autoirq)
+ dev->irq = autoirq;
+
+ printk("%s: %s EtherLink at %#lx, using %sIRQ %d.\n", dev->name, mname, dev->base_addr,
+ autoirq ? "auto":"assigned ", dev->irq);
+
+#ifdef CONFIG_IP_MULTICAST
+ printk("WARNING: Use of the 3c501 in a multicast kernel is NOT recommended.\n");
+#endif
+
+ if (el_debug)
+ printk("%s", version);
+
+ /*
+ * Initialize the device structure.
+ */
+
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ /*
+ * The EL1-specific entries in the device structure.
+ */
+
+ dev->open = &el_open;
+ dev->hard_start_xmit = &el_start_xmit;
+ dev->stop = &el1_close;
+ dev->get_stats = &el1_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /*
+ * Setup the generic properties
+ */
+
+ ether_setup(dev);
+
+#endif /* !MODULE */
+
+ return 0;
+}
+
+/*
+ * Open/initialize the board.
+ */
+
+static int el_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el_debug > 2)
+ printk("%s: Doing el_open()...", dev->name);
+
+ if (request_irq(dev->irq, &el_interrupt, 0, "3c501"))
+ return -EAGAIN;
+
+ irq2dev_map[dev->irq] = dev;
+ el_reset(dev);
+
+ dev->start = 1;
+
+ outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int el_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+
+ if(dev->interrupt) /* May be unloading, don't stamp on */
+ return 1; /* the packet buffer this time */
+
+ if (dev->tbusy)
+ {
+ if (jiffies - dev->trans_start < 20)
+ {
+ if (el_debug > 2)
+ printk(" transmitter busy, deferred.\n");
+ return 1;
+ }
+ if (el_debug)
+ printk ("%s: transmit timed out, txsr %#2x axsr=%02x rxsr=%02x.\n",
+ dev->name, inb(TX_STATUS), inb(AX_STATUS), inb(RX_STATUS));
+ lp->stats.tx_errors++;
+ outb(TX_NORM, TX_CMD);
+ outb(RX_NORM, RX_CMD);
+ outb(AX_OFF, AX_CMD); /* Just trigger a false interrupt. */
+ outb(AX_RX, AX_CMD); /* Aux control, irq and receive enabled */
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+
+ if (skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ save_flags(flags);
+
+ /*
+ * Avoid incoming interrupts between us flipping tbusy and flipping
+ * mode as the driver assumes tbusy is a faithful indicator of card
+ * state
+ */
+
+ cli();
+
+ /*
+ * Avoid timer-based retransmission conflicts.
+ */
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ {
+ restore_flags(flags);
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ }
+ else
+ {
+ int gp_start = 0x800 - (ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+ unsigned char *buf = skb->data;
+
+load_it_again_sam:
+ lp->tx_pkt_start = gp_start;
+ lp->collisions = 0;
+
+ /*
+ * Command mode with status cleared should [in theory]
+ * mean no more interrupts can be pending on the card.
+ */
+
+#ifdef BLOCKOUT_1
+ disable_irq(dev->irq);
+#endif
+ outb_p(AX_SYS, AX_CMD);
+ inb_p(RX_STATUS);
+ inb_p(TX_STATUS);
+
+ lp->loading=1;
+
+ /*
+ * Turn interrupts back on while we spend a pleasant afternoon
+ * loading bytes into the board
+ */
+
+ restore_flags(flags);
+ outw(0x00, RX_BUF_CLR); /* Set rx packet area to 0. */
+ outw(gp_start, GP_LOW); /* aim - packet will be loaded into buffer start */
+ outsb(DATAPORT,buf,skb->len); /* load buffer (usual thing each byte increments the pointer) */
+ outw(gp_start, GP_LOW); /* the board reuses the same register */
+#ifndef BLOCKOUT_1
+ if(lp->loading==2) /* A receive upset our load, despite our best efforts */
+ {
+ if(el_debug>2)
+ printk("%s: burped during tx load.\n", dev->name);
+ goto load_it_again_sam; /* Sigh... */
+ }
+#endif
+ outb(AX_XMIT, AX_CMD); /* fire ... Trigger xmit. */
+ lp->loading=0;
+#ifdef BLOCKOUT_1
+ enable_irq(dev->irq);
+#endif
+ dev->trans_start = jiffies;
+ }
+
+ if (el_debug > 2)
+ printk(" queued xmit.\n");
+ dev_kfree_skb (skb, FREE_WRITE);
+ return 0;
+}
+
+
+/*
+ * The typical workload of the driver:
+ * Handle the ether interface interrupts.
+ */
+
+static void el_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr;
+ int axsr; /* Aux. status reg. */
+
+ if (dev == NULL || dev->irq != irq)
+ {
+ printk ("3c501 driver: irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ /*
+ * What happened ?
+ */
+
+ axsr = inb(AX_STATUS);
+
+ /*
+ * Log it
+ */
+
+ if (el_debug > 3)
+ printk("%s: el_interrupt() aux=%#02x", dev->name, axsr);
+ if (dev->interrupt)
+ printk("%s: Reentering the interrupt driver!\n", dev->name);
+ dev->interrupt = 1;
+#ifndef BLOCKOUT_1
+ if(lp->loading==1 && !dev->tbusy)
+ printk("%s: Inconsistent state loading while not in tx\n",
+ dev->name);
+#endif
+#ifdef BLOCKOUT_3
+ lp->loading=2; /* So we can spot loading interruptions */
+#endif
+
+ if (dev->tbusy)
+ {
+
+ /*
+ * Board in transmit mode. May be loading. If we are
+ * loading we shouldn't have got this.
+ */
+
+ int txsr = inb(TX_STATUS);
+#ifdef BLOCKOUT_2
+ if(lp->loading==1)
+ {
+ if(el_debug > 2)
+ {
+ printk("%s: Interrupt while loading [", dev->name);
+ printk(" txsr=%02x gp=%04x rp=%04x]\n", txsr, inw(GP_LOW),inw(RX_LOW));
+ }
+ lp->loading=2; /* Force a reload */
+ dev->interrupt = 0;
+ return;
+ }
+#endif
+ if (el_debug > 6)
+ printk(" txsr=%02x gp=%04x rp=%04x", txsr, inw(GP_LOW),inw(RX_LOW));
+
+ if ((axsr & 0x80) && (txsr & TX_READY) == 0)
+ {
+ /*
+ * FIXME: is there a logic to whether to keep on trying or
+ * reset immediately ?
+ */
+ printk("%s: Unusual interrupt during Tx, txsr=%02x axsr=%02x"
+ " gp=%03x rp=%03x.\n", dev->name, txsr, axsr,
+ inw(ioaddr + EL1_DATAPTR), inw(ioaddr + EL1_RXPTR));
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ else if (txsr & TX_16COLLISIONS)
+ {
+ /*
+ * Timed out
+ */
+ if (el_debug)
+ printk("%s: Transmit failed 16 times, ethernet jammed?\n",dev->name);
+ outb(AX_SYS, AX_CMD);
+ lp->stats.tx_aborted_errors++;
+ }
+ else if (txsr & TX_COLLISION)
+ {
+ /*
+ * Retrigger xmit.
+ */
+
+ if (el_debug > 6)
+ printk(" retransmitting after a collision.\n");
+ /*
+ * Poor little chip can't reset its own start pointer
+ */
+
+ outb(AX_SYS, AX_CMD);
+ outw(lp->tx_pkt_start, GP_LOW);
+ outb(AX_XMIT, AX_CMD);
+ lp->stats.collisions++;
+ dev->interrupt = 0;
+ return;
+ }
+ else
+ {
+ /*
+ * It worked.. we will now fall through and receive
+ */
+ lp->stats.tx_packets++;
+ if (el_debug > 6)
+ printk(" Tx succeeded %s\n",
+ (txsr & TX_RDY) ? "." : "but tx is busy!");
+ /*
+ * This is safe the interrupt is atomic WRT itself.
+ */
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* In case more to transmit */
+ }
+ }
+ else
+ {
+ /*
+ * In receive mode.
+ */
+
+ int rxsr = inb(RX_STATUS);
+ if (el_debug > 5)
+ printk(" rxsr=%02x txsr=%02x rp=%04x", rxsr, inb(TX_STATUS),inw(RX_LOW));
+ /*
+ * Just reading rx_status fixes most errors.
+ */
+ if (rxsr & RX_MISSED)
+ lp->stats.rx_missed_errors++;
+ else if (rxsr & RX_RUNT)
+ { /* Handled to avoid board lock-up. */
+ lp->stats.rx_length_errors++;
+ if (el_debug > 5)
+ printk(" runt.\n");
+ }
+ else if (rxsr & RX_GOOD)
+ {
+ /*
+ * Receive worked.
+ */
+ el_receive(dev);
+ }
+ else
+ {
+ /*
+ * Nothing? Something is broken!
+ */
+ if (el_debug > 2)
+ printk("%s: No packet seen, rxsr=%02x **resetting 3c501***\n",
+ dev->name, rxsr);
+ el_reset(dev);
+ }
+ if (el_debug > 3)
+ printk(".\n");
+ }
+
+ /*
+ * Move into receive mode
+ */
+
+ outb(AX_RX, AX_CMD);
+ outw(0x00, RX_BUF_CLR);
+ inb(RX_STATUS); /* Be certain that interrupts are cleared. */
+ inb(TX_STATUS);
+ dev->interrupt = 0;
+ return;
+}
+
+
+/*
+ * We have a good packet. Well, not really "good", just mostly not broken.
+ * We must check everything to see if it is good.
+ */
+
+static void el_receive(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int pkt_len;
+ struct sk_buff *skb;
+
+ pkt_len = inw(RX_LOW);
+
+ if (el_debug > 4)
+ printk(" el_receive %d.\n", pkt_len);
+
+ if ((pkt_len < 60) || (pkt_len > 1536))
+ {
+ if (el_debug)
+ printk("%s: bogus packet, length=%d\n", dev->name, pkt_len);
+ lp->stats.rx_over_errors++;
+ return;
+ }
+
+ /*
+ * Command mode so we can empty the buffer
+ */
+
+ outb(AX_SYS, AX_CMD);
+ skb = dev_alloc_skb(pkt_len+2);
+
+ /*
+ * Start of frame
+ */
+
+ outw(0x00, GP_LOW);
+ if (skb == NULL)
+ {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ return;
+ }
+ else
+ {
+ skb_reserve(skb,2); /* Force 16 byte alignment */
+ skb->dev = dev;
+ /*
+ * The read increments through the bytes. The interrupt
+ * handler will fix the pointer when it returns to
+ * receive mode.
+ */
+ insb(DATAPORT, skb_put(skb,pkt_len), pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ return;
+}
+
+static void el_reset(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el_debug> 2)
+ printk("3c501 reset...");
+ outb(AX_RESET, AX_CMD); /* Reset the chip */
+ outb(AX_LOOP, AX_CMD); /* Aux control, irq and loopback enabled */
+ {
+ int i;
+ for (i = 0; i < 6; i++) /* Set the station address. */
+ outb(dev->dev_addr[i], ioaddr + i);
+ }
+
+ outw(0, RX_BUF_CLR); /* Set rx packet area to 0. */
+ cli(); /* Avoid glitch on writes to CMD regs */
+ outb(TX_NORM, TX_CMD); /* tx irq on done, collision */
+ outb(RX_NORM, RX_CMD); /* Set Rx commands. */
+ inb(RX_STATUS); /* Clear status. */
+ inb(TX_STATUS);
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ sti();
+}
+
+static int el1_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el_debug > 2)
+ printk("%s: Shutting down ethercard at %#x.\n", dev->name, ioaddr);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /*
+ * Free and disable the IRQ.
+ */
+
+ free_irq(dev->irq);
+ outb(AX_RESET, AX_CMD); /* Reset the chip */
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct enet_statistics *el1_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ * best-effort filtering.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ outb(RX_PROM, RX_CMD);
+ inb(RX_STATUS);
+ }
+ else if (dev->mc_list || dev->flags&IFF_ALLMULTI)
+ {
+ outb(RX_MULT, RX_CMD); /* Multicast or all multicast is the same */
+ inb(RX_STATUS); /* Clear status. */
+ }
+ else
+ {
+ outb(RX_NORM, RX_CMD);
+ inb(RX_STATUS);
+ }
+}
+
+#ifdef MODULE
+
+static char devicename[9] = { 0, };
+
+static struct device dev_3c501 =
+{
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x280, 5,
+ 0, 0, 0, NULL, el1_probe
+};
+
+static int io=0x280;
+static int irq=5;
+
+int init_module(void)
+{
+ dev_3c501.irq=irq;
+ dev_3c501.base_addr=io;
+ if (register_netdev(&dev_3c501) != 0)
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ /*
+ * No need to check MOD_IN_USE, as sys_delete_module() checks.
+ */
+
+ unregister_netdev(&dev_3c501);
+
+ /*
+ * Free up the private structure, or leak memory :-)
+ */
+
+ kfree(dev_3c501.priv);
+ dev_3c501.priv = NULL; /* gets re-allocated by el1_probe1 */
+
+ /*
+ * If we don't do this, we can't re-insmod it later.
+ */
+ release_region(dev_3c501.base_addr, EL1_IO_EXTENT);
+}
+
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer -m486 -c -o 3c501.o 3c501.c"
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c503.c b/i386/i386at/gpl/linux/net/3c503.c
new file mode 100644
index 00000000..9a0c0b9e
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c503.c
@@ -0,0 +1,627 @@
+/* 3c503.c: A shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This driver should work with the 3c503 and 3c503/16. It should be used
+ in shared memory mode for best performance, although it may also work
+ in programmed-I/O mode.
+
+ Sources:
+ EtherLink II Technical Reference Manual,
+ EtherLink II/16 Technical Reference Manual Supplement,
+ 3Com Corporation, 5400 Bayfront Plaza, Santa Clara CA 95052-8145
+
+ The Crynwr 3c503 packet driver.
+
+ Changelog:
+
+ Paul Gortmaker : add support for the 2nd 8kB of RAM on 16 bit cards.
+ Paul Gortmaker : multiple card support for module users.
+
+*/
+
+static const char *version =
+ "3c503.c:v1.10 9/23/93 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+#include "3c503.h"
+
+
+int el2_probe(struct device *dev);
+int el2_pio_probe(struct device *dev);
+int el2_probe1(struct device *dev, int ioaddr);
+
+/* A zero-terminated list of I/O addresses to be probed in PIO mode. */
+static unsigned int netcard_portlist[] =
+ { 0x300,0x310,0x330,0x350,0x250,0x280,0x2a0,0x2e0,0};
+
+#define EL2_IO_EXTENT 16
+
+#ifdef HAVE_DEVLIST
+/* The 3c503 uses two entries, one for the safe memory-mapped probe and
+ the other for the typical I/O probe. */
+struct netdev_entry el2_drv =
+{"3c503", el2_probe, EL1_IO_EXTENT, 0};
+struct netdev_entry el2pio_drv =
+{"3c503pio", el2_pioprobe1, EL1_IO_EXTENT, netcard_portlist};
+#endif
+
+static int el2_open(struct device *dev);
+static int el2_close(struct device *dev);
+static void el2_reset_8390(struct device *dev);
+static void el2_init_card(struct device *dev);
+static void el2_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void el2_block_input(struct device *dev, int count, struct sk_buff *skb,
+ int ring_offset);
+static void el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+
+/* This routine probes for a memory-mapped 3c503 board by looking for
+ the "location register" at the end of the jumpered boot PROM space.
+ This works even if a PROM isn't there.
+
+ If the ethercard isn't found there is an optional probe for
+ ethercard jumpered to programmed-I/O mode.
+ */
+int
+el2_probe(struct device *dev)
+{
+ int *addr, addrs[] = { 0xddffe, 0xd9ffe, 0xcdffe, 0xc9ffe, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el2_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (addr = addrs; *addr; addr++) {
+ int i;
+ unsigned int base_bits = readb(*addr);
+ /* Find first set bit. */
+ for(i = 7; i >= 0; i--, base_bits >>= 1)
+ if (base_bits & 0x1)
+ break;
+ if (base_bits != 1)
+ continue;
+ if (check_region(netcard_portlist[i], EL2_IO_EXTENT))
+ continue;
+ if (el2_probe1(dev, netcard_portlist[i]) == 0)
+ return 0;
+ }
+#if ! defined(no_probe_nonshared_memory) && ! defined (HAVE_DEVLIST)
+ return el2_pio_probe(dev);
+#else
+ return ENODEV;
+#endif
+}
+
+#ifndef HAVE_DEVLIST
+/* Try all of the locations that aren't obviously empty. This touches
+ a lot of locations, and is much riskier than the code above. */
+int
+el2_pio_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el2_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; netcard_portlist[i]; i++) {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, EL2_IO_EXTENT))
+ continue;
+ if (el2_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* Probe for the Etherlink II card at I/O port base IOADDR,
+ returning non-zero on success. If found, set the station
+ address and memory parameters in DEVICE. */
+int
+el2_probe1(struct device *dev, int ioaddr)
+{
+ int i, iobase_reg, membase_reg, saved_406, wordlength;
+ static unsigned version_printed = 0;
+ unsigned long vendor_id;
+
+ /* Reset and/or avoid any lurking NE2000 */
+ if (inb(ioaddr + 0x408) == 0xff) {
+ udelay(1000);
+ return ENODEV;
+ }
+
+ /* We verify that it's a 3C503 board by checking the first three octets
+ of its ethernet address. */
+ iobase_reg = inb(ioaddr+0x403);
+ membase_reg = inb(ioaddr+0x404);
+ /* ASIC location registers should be 0 or have only a single bit set. */
+ if ( (iobase_reg & (iobase_reg - 1))
+ || (membase_reg & (membase_reg - 1))) {
+ return ENODEV;
+ }
+ saved_406 = inb_p(ioaddr + 0x406);
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, ioaddr + 0x406); /* Reset it... */
+ outb_p(ECNTRL_THIN, ioaddr + 0x406);
+ /* Map the station addr PROM into the lower I/O ports. We now check
+ for both the old and new 3Com prefix */
+ outb(ECNTRL_SAPROM|ECNTRL_THIN, ioaddr + 0x406);
+ vendor_id = inb(ioaddr)*0x10000 + inb(ioaddr + 1)*0x100 + inb(ioaddr + 2);
+ if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID)) {
+ /* Restore the register we frobbed. */
+ outb(saved_406, ioaddr + 0x406);
+ return ENODEV;
+ }
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("3c503.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ dev->base_addr = ioaddr;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk ("3c503: unable to allocate memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ printk("%s: 3c503 at i/o base %#3x, node ", dev->name, ioaddr);
+
+ /* Retrieve and print the ethernet address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ /* Map the 8390 back into the window. */
+ outb(ECNTRL_THIN, ioaddr + 0x406);
+
+ /* Check for EL2/16 as described in tech. man. */
+ outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+ outb_p(0, ioaddr + EN0_DCFG);
+ outb_p(E8390_PAGE2, ioaddr + E8390_CMD);
+ wordlength = inb_p(ioaddr + EN0_DCFG) & ENDCFG_WTS;
+ outb_p(E8390_PAGE0, ioaddr + E8390_CMD);
+
+ /* Probe for, turn on and clear the board's shared memory. */
+ if (ei_debug > 2) printk(" memory jumpers %2.2x ", membase_reg);
+ outb(EGACFR_NORM, ioaddr + 0x405); /* Enable RAM */
+
+ /* This should be probed for (or set via an ioctl()) at run-time.
+ Right now we use a sleazy hack to pass in the interface number
+ at boot-time via the low bits of the mem_end field. That value is
+ unused, and the low bits would be discarded even if it was used. */
+#if defined(EI8390_THICK) || defined(EL2_AUI)
+ ei_status.interface_num = 1;
+#else
+ ei_status.interface_num = dev->mem_end & 0xf;
+#endif
+ printk(", using %sternal xcvr.\n", ei_status.interface_num == 0 ? "in" : "ex");
+
+ if ((membase_reg & 0xf0) == 0) {
+ dev->mem_start = 0;
+ ei_status.name = "3c503-PIO";
+ } else {
+ dev->mem_start = ((membase_reg & 0xc0) ? 0xD8000 : 0xC8000) +
+ ((membase_reg & 0xA0) ? 0x4000 : 0);
+
+#define EL2_MEMSIZE (EL2_MB1_STOP_PG - EL2_MB1_START_PG)*256
+#ifdef EL2MEMTEST
+ /* This has never found an error, but someone might care.
+ Note that it only tests the 2nd 8kB on 16kB 3c503/16
+ cards between card addr. 0x2000 and 0x3fff. */
+ { /* Check the card's memory. */
+ unsigned long mem_base = dev->mem_start;
+ unsigned int test_val = 0xbbadf00d;
+ writel(0xba5eba5e, mem_base);
+ for (i = sizeof(test_val); i < EL2_MEMSIZE; i+=sizeof(test_val)) {
+ writel(test_val, mem_base + i);
+ if (readl(mem_base) != 0xba5eba5e
+ || readl(mem_base + i) != test_val) {
+ printk("3c503.c: memory failure or memory address conflict.\n");
+ dev->mem_start = 0;
+ ei_status.name = "3c503-PIO";
+ break;
+ }
+ test_val += 0x55555555;
+ writel(0, mem_base + i);
+ }
+ }
+#endif /* EL2MEMTEST */
+
+ dev->mem_end = dev->rmem_end = dev->mem_start + EL2_MEMSIZE;
+
+ if (wordlength) { /* No Tx pages to skip over to get to Rx */
+ dev->rmem_start = dev->mem_start;
+ ei_status.name = "3c503/16";
+ } else {
+ dev->rmem_start = TX_PAGES*256 + dev->mem_start;
+ ei_status.name = "3c503";
+ }
+ }
+
+ /*
+ Divide up the memory on the card. This is the same regardless of
+ whether shared-mem or PIO is used. For 16 bit cards (16kB RAM),
+ we use the entire 8k of bank1 for an Rx ring. We only use 3k
+ of the bank0 for 2 full size Tx packet slots. For 8 bit cards,
+ (8kB RAM) we use 3kB of bank1 for two Tx slots, and the remaining
+ 5kB for an Rx ring. */
+
+ if (wordlength) {
+ ei_status.tx_start_page = EL2_MB0_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG;
+ } else {
+ ei_status.tx_start_page = EL2_MB1_START_PG;
+ ei_status.rx_start_page = EL2_MB1_START_PG + TX_PAGES;
+ }
+
+ /* Finish setting the board's parameters. */
+ ei_status.stop_page = EL2_MB1_STOP_PG;
+ ei_status.word16 = wordlength;
+ ei_status.reset_8390 = &el2_reset_8390;
+ ei_status.get_8390_hdr = &el2_get_8390_hdr;
+ ei_status.block_input = &el2_block_input;
+ ei_status.block_output = &el2_block_output;
+
+ request_region(ioaddr, EL2_IO_EXTENT, ei_status.name);
+
+ if (dev->irq == 2)
+ dev->irq = 9;
+ else if (dev->irq > 5 && dev->irq != 9) {
+ printk("3c503: configured interrupt %d invalid, will use autoIRQ.\n",
+ dev->irq);
+ dev->irq = 0;
+ }
+
+ ei_status.saved_irq = dev->irq;
+
+ dev->start = 0;
+ dev->open = &el2_open;
+ dev->stop = &el2_close;
+
+ if (dev->mem_start)
+ printk("%s: %s - %dkB RAM, 8kB shared mem window at %#6lx-%#6lx.\n",
+ dev->name, ei_status.name, (wordlength+1)<<3,
+ dev->mem_start, dev->mem_end-1);
+
+ else
+ printk("\n%s: %s, %dkB RAM, using programmed I/O (REJUMPER for SHARED MEMORY).\n",
+ dev->name, ei_status.name, (wordlength+1)<<3);
+
+ return 0;
+}
+
+static int
+el2_open(struct device *dev)
+{
+
+ if (dev->irq < 2) {
+ int irqlist[] = {5, 9, 3, 4, 0};
+ int *irqp = irqlist;
+
+ outb(EGACFR_NORM, E33G_GACFR); /* Enable RAM and interrupts. */
+ do {
+ if (request_irq (*irqp, NULL, 0, "bogus") != -EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen. */
+ autoirq_setup(0);
+ outb_p(0x04 << ((*irqp == 9) ? 2 : *irqp), E33G_IDCFR);
+ outb_p(0x00, E33G_IDCFR);
+ if (*irqp == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (dev->irq = *irqp, &ei_interrupt, 0, ei_status.name) == 0)
+ break;
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+ return -EAGAIN;
+ }
+ } else {
+ if (request_irq(dev->irq, &ei_interrupt, 0, ei_status.name)) {
+ return -EAGAIN;
+ }
+ }
+
+ el2_init_card(dev);
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+el2_close(struct device *dev)
+{
+ free_irq(dev->irq);
+ dev->irq = ei_status.saved_irq;
+ irq2dev_map[dev->irq] = NULL;
+ outb(EGACFR_IRQOFF, E33G_GACFR); /* disable interrupts. */
+
+ ei_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* This is called whenever we have a unrecoverable failure:
+ transmit timeout
+ Bad ring buffer packet header
+ */
+static void
+el2_reset_8390(struct device *dev)
+{
+ if (ei_debug > 1) {
+ printk("%s: Resetting the 3c503 board...", dev->name);
+ printk("%#lx=%#02x %#lx=%#02x %#lx=%#02x...", E33G_IDCFR, inb(E33G_IDCFR),
+ E33G_CNTRL, inb(E33G_CNTRL), E33G_GACFR, inb(E33G_GACFR));
+ }
+ outb_p(ECNTRL_RESET|ECNTRL_THIN, E33G_CNTRL);
+ ei_status.txing = 0;
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ el2_init_card(dev);
+ if (ei_debug > 1) printk("done\n");
+}
+
+/* Initialize the 3c503 GA registers after a reset. */
+static void
+el2_init_card(struct device *dev)
+{
+ /* Unmap the station PROM and select the DIX or BNC connector. */
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+
+ /* Set ASIC copy of rx's first and last+1 buffer pages */
+ /* These must be the same as in the 8390. */
+ outb(ei_status.rx_start_page, E33G_STARTPG);
+ outb(ei_status.stop_page, E33G_STOPPG);
+
+ /* Point the vector pointer registers somewhere ?harmless?. */
+ outb(0xff, E33G_VP2); /* Point at the ROM restart location 0xffff0 */
+ outb(0xff, E33G_VP1);
+ outb(0x00, E33G_VP0);
+ /* Turn off all interrupts until we're opened. */
+ outb_p(0x00, dev->base_addr + EN0_IMR);
+ /* Enable IRQs iff started. */
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ /* Set the interrupt line. */
+ outb_p((0x04 << (dev->irq == 9 ? 2 : dev->irq)), E33G_IDCFR);
+ outb_p(8, E33G_DRQCNT); /* Set burst size to 8 */
+ outb_p(0x20, E33G_DMAAH); /* Put a valid addr in the GA DMA */
+ outb_p(0x00, E33G_DMAAL);
+ return; /* We always succeed */
+}
+
+/* Either use the shared memory (if enabled on the board) or put the packet
+ out through the ASIC FIFO. The latter is probably much slower. */
+static void
+el2_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int i; /* Buffer index */
+ int boguscount = 0; /* timeout counter */
+
+ if (ei_status.word16) /* Tx packets go into bank 0 on EL2/16 card */
+ outb(EGACFR_RSEL|EGACFR_TCM, E33G_GACFR);
+ else
+ outb(EGACFR_NORM, E33G_GACFR);
+
+ if (dev->mem_start) { /* Shared memory transfer */
+ unsigned long dest_addr = dev->mem_start +
+ ((start_page - ei_status.tx_start_page) << 8);
+ memcpy_toio(dest_addr, buf, count);
+ outb(EGACFR_NORM, E33G_GACFR); /* Back to bank1 in case on bank0 */
+ return;
+ }
+ /* No shared memory, put the packet out the slow way. */
+ /* Set up then start the internal memory transfer to Tx Start Page */
+ outb(0x00, E33G_DMAAL);
+ outb_p(start_page, E33G_DMAAH);
+ outb_p((ei_status.interface_num ? ECNTRL_AUI : ECNTRL_THIN ) | ECNTRL_OUTPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* This is the byte copy loop: it should probably be tuned for
+ speed once everything is working. I think it is possible
+ to output 8 bytes between each check of the status bit. */
+ for(i = 0; i < count; i++) {
+ if (i % 8 == 0)
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ if (++boguscount > (i<<3) + 32) {
+ printk("%s: FIFO blocked in el2_block_output (at %d of %d, bc=%d).\n",
+ dev->name, i, count, boguscount);
+ outb(EGACFR_NORM, E33G_GACFR); /* To MB1 for EL2/16 */
+ return;
+ }
+ outb(buf[i], E33G_FIFOH);
+ }
+ outb_p(ei_status.interface_num==0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+ outb(EGACFR_NORM, E33G_GACFR); /* Back to bank1 in case on bank0 */
+ return;
+}
+
+/* Read the 4 byte, page aligned 8390 specific header. */
+static void
+el2_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned int i;
+ unsigned long hdr_start = dev->mem_start + ((ring_page - EL2_MB1_START_PG)<<8);
+ unsigned long fifo_watchdog;
+
+ if (dev->mem_start) { /* Use the shared memory. */
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+ return;
+ }
+
+ /* No shared memory, use programmed I/O. Ugh. */
+ outb(0, E33G_DMAAL);
+ outb_p(ring_page & 0xff, E33G_DMAAH);
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* Header is < 8 bytes, so only check the FIFO at the beginning. */
+ fifo_watchdog = jiffies;
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0) {
+ if (jiffies - fifo_watchdog > 2*HZ/100) {
+ printk("%s: FIFO blocked in el2_get_8390_hdr.\n", dev->name);
+ break;
+ }
+ }
+
+ for(i = 0; i < sizeof(struct e8390_pkt_hdr); i++)
+ ((char *)(hdr))[i] = inb_p(E33G_FIFOH);
+
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+/* Returns the new ring pointer. */
+static void
+el2_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int boguscount = 0;
+ int end_of_ring = dev->rmem_end;
+ unsigned int i;
+
+ /* Maybe enable shared memory just be to be safe... nahh.*/
+ if (dev->mem_start) { /* Use the shared memory. */
+ ring_offset -= (EL2_MB1_START_PG<<8);
+ if (dev->mem_start + ring_offset + count > end_of_ring) {
+ /* We must wrap the input move. */
+ int semi_count = end_of_ring - (dev->mem_start + ring_offset);
+ memcpy_fromio(skb->data, dev->mem_start + ring_offset, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, dev->mem_start + ring_offset, count, 0);
+ }
+ return;
+ }
+ /* No shared memory, use programmed I/O. */
+ outb(ring_offset & 0xff, E33G_DMAAL);
+ outb_p((ring_offset >> 8) & 0xff, E33G_DMAAH);
+ outb_p((ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI) | ECNTRL_INPUT
+ | ECNTRL_START, E33G_CNTRL);
+
+ /* This is the byte copy loop: it should probably be tuned for
+ speed once everything is working. */
+ for(i = 0; i < count; i++) {
+ if (i % 8 == 0)
+ while ((inb(E33G_STATUS) & ESTAT_DPRDY) == 0)
+ if (++boguscount > (i<<3) + 32) {
+ printk("%s: FIFO blocked in el2_block_input() (at %d of %d, bc=%d).\n",
+ dev->name, i, count, boguscount);
+ boguscount = 0;
+ break;
+ }
+ (skb->data)[i] = inb_p(E33G_FIFOH);
+ }
+ outb_p(ei_status.interface_num == 0 ? ECNTRL_THIN : ECNTRL_AUI, E33G_CNTRL);
+}
+
+
+#ifdef MODULE
+#define MAX_EL2_CARDS 4 /* Max number of EL2 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_EL2_CARDS] = { 0, };
+static struct device dev_el2[MAX_EL2_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_EL2_CARDS] = { 0, };
+static int irq[MAX_EL2_CARDS] = { 0, };
+static int xcvr[MAX_EL2_CARDS] = { 0, }; /* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+ struct device *dev = &dev_el2[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
+ dev->init = el2_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "3c503.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "3c503.c: No 3c503 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_EL2_CARDS; this_dev++) {
+ struct device *dev = &dev_el2[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: el2_close() handles free_irq + irq2dev map */
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(dev->base_addr, EL2_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c503.h b/i386/i386at/gpl/linux/net/3c503.h
new file mode 100644
index 00000000..b9f8a46f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c503.h
@@ -0,0 +1,91 @@
+/* Definitions for the 3Com 3c503 Etherlink 2. */
+/* This file is distributed under the GPL.
+ Many of these names and comments are directly from the Crynwr packet
+ drivers, which are released under the GPL. */
+
+#define EL2H (dev->base_addr + 0x400)
+#define EL2L (dev->base_addr)
+
+/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran
+ out of available addresses on the first one... */
+
+#define OLD_3COM_ID 0x02608c
+#define NEW_3COM_ID 0x0020af
+
+/* Shared memory management parameters. NB: The 8 bit cards have only
+ one bank (MB1) which serves both Tx and Rx packet space. The 16bit
+ cards have 2 banks, MB0 for Tx packets, and MB1 for Rx packets.
+ You choose which bank appears in the sh. mem window with EGACFR_MBSn */
+
+#define EL2_MB0_START_PG (0x00) /* EL2/16 Tx packets go in bank 0 */
+#define EL2_MB1_START_PG (0x20) /* First page of bank 1 */
+#define EL2_MB1_STOP_PG (0x40) /* Last page +1 of bank 1 */
+
+/* 3Com 3c503 ASIC registers */
+#define E33G_STARTPG (EL2H+0) /* Start page, matching EN0_STARTPG */
+#define E33G_STOPPG (EL2H+1) /* Stop page, must match EN0_STOPPG */
+#define E33G_DRQCNT (EL2H+2) /* DMA burst count */
+#define E33G_IOBASE (EL2H+3) /* Read of I/O base jumpers. */
+ /* (non-useful, but it also appears at the end of EPROM space) */
+#define E33G_ROMBASE (EL2H+4) /* Read of memory base jumpers. */
+#define E33G_GACFR (EL2H+5) /* Config/setup bits for the ASIC GA */
+#define E33G_CNTRL (EL2H+6) /* Board's main control register */
+#define E33G_STATUS (EL2H+7) /* Status on completions. */
+#define E33G_IDCFR (EL2H+8) /* Interrupt/DMA config register */
+ /* (Which IRQ to assert, DMA chan to use) */
+#define E33G_DMAAH (EL2H+9) /* High byte of DMA address reg */
+#define E33G_DMAAL (EL2H+10) /* Low byte of DMA address reg */
+/* "Vector pointer" - if this address matches a read, the EPROM (rather than
+ shared RAM) is mapped into memory space. */
+#define E33G_VP2 (EL2H+11)
+#define E33G_VP1 (EL2H+12)
+#define E33G_VP0 (EL2H+13)
+#define E33G_FIFOH (EL2H+14) /* FIFO for programmed I/O moves */
+#define E33G_FIFOL (EL2H+15) /* ... low byte of above. */
+
+/* Bits in E33G_CNTRL register: */
+
+#define ECNTRL_RESET (0x01) /* Software reset of the ASIC and 8390 */
+#define ECNTRL_THIN (0x02) /* Onboard xcvr enable, AUI disable */
+#define ECNTRL_AUI (0x00) /* Onboard xcvr disable, AUI enable */
+#define ECNTRL_SAPROM (0x04) /* Map the station address prom */
+#define ECNTRL_DBLBFR (0x20) /* FIFO configuration bit */
+#define ECNTRL_OUTPUT (0x40) /* PC-to-3C503 direction if 1 */
+#define ECNTRL_INPUT (0x00) /* 3C503-to-PC direction if 0 */
+#define ECNTRL_START (0x80) /* Start the DMA logic */
+
+/* Bits in E33G_STATUS register: */
+
+#define ESTAT_DPRDY (0x80) /* Data port (of FIFO) ready */
+#define ESTAT_UFLW (0x40) /* Tried to read FIFO when it was empty */
+#define ESTAT_OFLW (0x20) /* Tried to write FIFO when it was full */
+#define ESTAT_DTC (0x10) /* Terminal Count from PC bus DMA logic */
+#define ESTAT_DIP (0x08) /* DMA In Progress */
+
+/* Bits in E33G_GACFR register: */
+
+#define EGACFR_NIM (0x80) /* NIC interrupt mask */
+#define EGACFR_TCM (0x40) /* DMA term. count interrupt mask */
+#define EGACFR_RSEL (0x08) /* Map a bank of card mem into system mem */
+#define EGACFR_MBS2 (0x04) /* Memory bank select, bit 2. */
+#define EGACFR_MBS1 (0x02) /* Memory bank select, bit 1. */
+#define EGACFR_MBS0 (0x01) /* Memory bank select, bit 0. */
+
+#define EGACFR_NORM (0x49) /* TCM | RSEL | MBS0 */
+#define EGACFR_IRQOFF (0xc9) /* TCM | RSEL | MBS0 | NIM */
+
+/*
+ MBS2 MBS1 MBS0 Sh. mem windows card mem at:
+ ---- ---- ---- -----------------------------
+ 0 0 0 0x0000 -- bank 0
+ 0 0 1 0x2000 -- bank 1 (only choice for 8bit card)
+ 0 1 0 0x4000 -- bank 2, not used
+ 0 1 1 0x6000 -- bank 3, not used
+
+There was going to be a 32k card that used bank 2 and 3, but it
+never got produced.
+
+*/
+
+
+/* End of 3C503 parameter definitions */
diff --git a/i386/i386at/gpl/linux/net/3c505.c b/i386/i386at/gpl/linux/net/3c505.c
new file mode 100644
index 00000000..63ccc9cf
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c505.c
@@ -0,0 +1,1518 @@
+/*
+ * Linux ethernet device driver for the 3Com Etherlink Plus (3C505)
+ * By Craig Southeren and Juha Laiho
+ *
+ * 3c505.c This module implements an interface to the 3Com
+ * Etherlink Plus (3c505) ethernet card. Linux device
+ * driver interface reverse engineered from the Linux 3C509
+ * device drivers. Some 3C505 information gleaned from
+ * the Crynwr packet driver. Still this driver would not
+ * be here without 3C505 technical reference provided by
+ * 3Com.
+ *
+ * Version: @(#)3c505.c 0.8.4 17-Dec-95
+ *
+ * Authors: Linux 3c505 device driver by
+ * Craig Southeren, <craigs@ineluki.apana.org.au>
+ * Final debugging by
+ * Andrew Tridgell, <tridge@nimbus.anu.edu.au>
+ * Auto irq/address, tuning, cleanup and v1.1.4+ kernel mods by
+ * Juha Laiho, <jlaiho@ichaos.nullnet.fi>
+ * Linux 3C509 driver by
+ * Donald Becker, <becker@super.org>
+ * Crynwr packet driver by
+ * Krishnan Gopalan and Gregg Stefancik,
+ * Clemson University Engineering Computer Operations.
+ * Portions of the code have been adapted from the 3c505
+ * driver for NCSA Telnet by Bruce Orchard and later
+ * modified by Warren Van Houten and krus@diku.dk.
+ * 3C505 technical information provided by
+ * Terry Murphy, of 3Com Network Adapter Division
+ * Linux 1.3.0 changes by
+ * Alan Cox <Alan.Cox@linux.org>
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "3c505.h"
+
+/*********************************************************
+ *
+ * define debug messages here as common strings to reduce space
+ *
+ *********************************************************/
+
+static const char * filename = __FILE__;
+
+static const char * null_msg = "*** NULL at %s:%s (line %d) ***\n";
+#define CHECK_NULL(p) \
+ if (!p) printk(null_msg, filename,__FUNCTION__,__LINE__)
+
+static const char * timeout_msg = "*** timeout at %s:%s (line %d) ***\n";
+#define TIMEOUT_MSG(lineno) \
+ printk(timeout_msg, filename,__FUNCTION__,(lineno))
+
+static const char * invalid_pcb_msg =
+ "*** invalid pcb length %d at %s:%s (line %d) ***\n";
+#define INVALID_PCB_MSG(len) \
+ printk(invalid_pcb_msg, (len),filename,__FUNCTION__,__LINE__)
+
+static const char * search_msg = "%s: Looking for 3c505 adapter at address %#x...";
+
+static const char * stilllooking_msg = "still looking...";
+
+static const char * found_msg = "found.\n";
+
+static const char * notfound_msg = "not found (reason = %d)\n";
+
+static const char * couldnot_msg = "%s: 3c505 not found\n";
+
+/*********************************************************
+ *
+ * various other debug stuff
+ *
+ *********************************************************/
+
+#ifdef ELP_DEBUG
+static int elp_debug = ELP_DEBUG;
+#else
+static int elp_debug = 0;
+#endif
+
+/*
+ * 0 = no messages (well, some)
+ * 1 = messages when high level commands performed
+ * 2 = messages when low level commands performed
+ * 3 = messages when interrupts received
+ */
+
+#define ELP_VERSION "0.8.4"
+
+#ifdef MACH
+#define ELP_NEED_HARD_RESET 0
+#endif
+
+/*****************************************************************
+ *
+ * useful macros
+ *
+ *****************************************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+
+/*****************************************************************
+ *
+ * List of I/O-addresses we try to auto-sense
+ * Last element MUST BE 0!
+ *****************************************************************/
+
+const int addr_list[]={0x300,0x280,0x310,0};
+
+/*****************************************************************
+ *
+ * Functions for I/O (note the inline !)
+ *
+ *****************************************************************/
+
+static inline unsigned char
+inb_status (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_STATUS);
+}
+
+static inline unsigned char
+inb_control (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_CONTROL);
+}
+
+static inline int
+inb_command (unsigned int base_addr)
+{
+ return inb(base_addr+PORT_COMMAND);
+}
+
+static inline void
+outb_control (unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr+PORT_CONTROL);
+}
+
+static inline void
+outb_command (unsigned char val, unsigned int base_addr)
+{
+ outb(val, base_addr+PORT_COMMAND);
+}
+
+static inline unsigned int
+inw_data (unsigned int base_addr)
+{
+ return inw(base_addr+PORT_DATA);
+}
+
+static inline void
+outw_data (unsigned int val, unsigned int base_addr)
+{
+ outw(val, base_addr+PORT_DATA);
+}
+
+
+/*****************************************************************
+ *
+ * structure to hold context information for adapter
+ *
+ *****************************************************************/
+
+typedef struct {
+ volatile short got[NUM_TRANSMIT_CMDS]; /* flags for command completion */
+ pcb_struct tx_pcb; /* PCB for foreground sending */
+ pcb_struct rx_pcb; /* PCB for foreground receiving */
+ pcb_struct itx_pcb; /* PCB for background sending */
+ pcb_struct irx_pcb; /* PCB for background receiving */
+ struct enet_statistics stats;
+} elp_device;
+
+static int reset_count=0;
+
+/*****************************************************************
+ *
+ * useful functions for accessing the adapter
+ *
+ *****************************************************************/
+
+/*
+ * use this routine when accessing the ASF bits as they are
+ * changed asynchronously by the adapter
+ */
+
+/* get adapter PCB status */
+#define GET_ASF(addr) \
+ (get_status(addr)&ASF_PCB_MASK)
+
+static inline int
+get_status (unsigned int base_addr)
+{
+ int timeout = jiffies + 10;
+ register int stat1;
+ do {
+ stat1 = inb_status(base_addr);
+ } while (stat1 != inb_status(base_addr) && jiffies < timeout);
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ return stat1;
+}
+
+static inline void
+set_hsf (unsigned int base_addr, int hsf)
+{
+ cli();
+ outb_control((inb_control(base_addr)&~HSF_PCB_MASK)|hsf, base_addr);
+ sti();
+}
+
+#define WAIT_HCRE(addr,toval) wait_hcre((addr),(toval),__LINE__)
+static inline int
+wait_hcre (unsigned int base_addr, int toval, int lineno)
+{
+ int timeout = jiffies + toval;
+ while (((inb_status(base_addr)&HCRE)==0) && (jiffies <= timeout))
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(lineno);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static inline int
+wait_fast_hcre (unsigned int base_addr, int toval, int lineno)
+{
+ int timeout = 0;
+ while (((inb_status(base_addr)&HCRE)==0) && (timeout++ < toval))
+ ;
+ if (timeout >= toval) {
+ sti();
+ TIMEOUT_MSG(lineno);
+ return FALSE;
+ }
+ return TRUE;
+}
+
+static int start_receive (struct device *, pcb_struct *);
+static void adapter_hard_reset (struct device *);
+
+inline static void
+adapter_reset (struct device * dev)
+{
+ int timeout;
+ unsigned char orig_hcr=inb_control(dev->base_addr);
+
+ elp_device * adapter=dev->priv;
+
+ outb_control(0,dev->base_addr);
+
+ if (inb_status(dev->base_addr)&ACRF) {
+ do {
+ inb_command(dev->base_addr);
+ timeout=jiffies+2;
+ while ((jiffies<=timeout) && !(inb_status(dev->base_addr)&ACRF))
+ ;
+ } while (inb_status(dev->base_addr)&ACRF);
+ set_hsf(dev->base_addr,HSF_PCB_NAK);
+ }
+
+ outb_control(inb_control(dev->base_addr)|ATTN|DIR,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)&~ATTN,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)|FLSH,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+ outb_control(inb_control(dev->base_addr)&~FLSH,dev->base_addr);
+ timeout=jiffies+1;
+ while (jiffies<=timeout)
+ ;
+
+ outb_control(orig_hcr, dev->base_addr);
+ if (!start_receive(dev, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
+}
+
+/*****************************************************************
+ *
+ * send_pcb
+ * Send a PCB to the adapter.
+ *
+ * output byte to command reg --<--+
+ * wait until HCRE is non zero |
+ * loop until all bytes sent -->--+
+ * set HSF1 and HSF2 to 1
+ * output pcb length
+ * wait until ASF give ACK or NAK
+ * set HSF1 and HSF2 to 0
+ *
+ *****************************************************************/
+
+static int
+send_pcb (struct device * dev, pcb_struct * pcb)
+{
+ int i;
+ int timeout;
+ int cont;
+
+ /*
+ * load each byte into the command register and
+ * wait for the HCRE bit to indicate the adapter
+ * had read the byte
+ */
+ set_hsf(dev->base_addr,0);
+ if ((cont = WAIT_HCRE(dev->base_addr,5))) {
+ cli();
+ if (pcb->command==CMD_TRANSMIT_PACKET)
+ outb_control(inb_control(dev->base_addr)&~DIR,dev->base_addr);
+ outb_command(pcb->command, dev->base_addr);
+ sti();
+ cont = WAIT_HCRE(dev->base_addr,5);
+ }
+
+ if (cont) {
+ outb_command(pcb->length, dev->base_addr);
+ cont = WAIT_HCRE(dev->base_addr,5);
+ }
+
+ cli();
+ for (i = 0; cont && (i < pcb->length); i++) {
+ outb_command(pcb->data.raw[i], dev->base_addr);
+ cont = wait_fast_hcre(dev->base_addr,20000,__LINE__);
+ } /* if wait_fast_hcre() failed, has already done sti() */
+
+ /* set the host status bits to indicate end of PCB */
+ /* send the total packet length as well */
+ /* wait for the adapter to indicate that it has read the PCB */
+ if (cont) {
+ set_hsf(dev->base_addr,HSF_PCB_END);
+ outb_command(2+pcb->length, dev->base_addr);
+ sti();
+ timeout = jiffies + 7;
+ while (jiffies < timeout) {
+ i = GET_ASF(dev->base_addr);
+ if ((i == ASF_PCB_ACK) || (i == ASF_PCB_NAK))
+ break;
+ }
+
+ if (i == ASF_PCB_ACK) {
+ reset_count=0;
+ return TRUE;
+ }
+ else if (i == ASF_PCB_NAK) {
+ printk("%s: PCB send was NAKed\n", dev->name);
+ } else {
+ printk("%s: timeout after sending PCB\n", dev->name);
+ }
+ } else {
+ sti();
+ printk("%s: timeout in middle of sending PCB\n", dev->name);
+ }
+
+ adapter_reset(dev);
+ return FALSE;
+}
+
+/*****************************************************************
+ *
+ * receive_pcb
+ * Read a PCB to the adapter
+ *
+ * wait for ACRF to be non-zero ---<---+
+ * input a byte |
+ * if ASF1 and ASF2 were not both one |
+ * before byte was read, loop --->---+
+ * set HSF1 and HSF2 for ack
+ *
+ *****************************************************************/
+
+static int
+receive_pcb (struct device * dev, pcb_struct * pcb)
+{
+ int i, j;
+ int total_length;
+ int stat;
+ int timeout;
+
+ CHECK_NULL(pcb);
+ CHECK_NULL(dev);
+
+ set_hsf(dev->base_addr,0);
+
+ /* get the command code */
+ timeout = jiffies + 2;
+ while (((stat = get_status(dev->base_addr))&ACRF) == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+
+ pcb->command = inb_command(dev->base_addr);
+
+ /* read the data length */
+ timeout = jiffies + 3;
+ while (((stat = get_status(dev->base_addr)) & ACRF) == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+ pcb->length = inb_command(dev->base_addr);
+
+ if (pcb->length > MAX_PCB_DATA) {
+ INVALID_PCB_MSG(pcb->length);
+ adapter_reset(dev);
+ return FALSE;
+ }
+
+ /* read the data */
+ cli();
+ i = 0;
+ do {
+ j = 0;
+ while (((stat = get_status(dev->base_addr))&ACRF) == 0 && j++ < 20000)
+ ;
+ pcb->data.raw[i++] = inb_command(dev->base_addr);
+ if (i > MAX_PCB_DATA)
+ INVALID_PCB_MSG(i);
+ } while ((stat & ASF_PCB_MASK) != ASF_PCB_END && j < 20000);
+ sti();
+ if (j >= 20000) {
+ TIMEOUT_MSG(__LINE__);
+ return FALSE;
+ }
+
+ /* woops, the last "data" byte was really the length! */
+ total_length = pcb->data.raw[--i];
+
+ /* safety check total length vs data length */
+ if (total_length != (pcb->length + 2)) {
+ if (elp_debug >= 2)
+ printk("%s: mangled PCB received\n", dev->name);
+ set_hsf(dev->base_addr,HSF_PCB_NAK);
+ return FALSE;
+ }
+
+ set_hsf(dev->base_addr,HSF_PCB_ACK);
+ reset_count=0;
+ return TRUE;
+}
+
+static void
+adapter_hard_reset (struct device * dev)
+{
+ int timeout;
+ long flags;
+
+ CHECK_NULL(dev);
+
+ save_flags(flags);
+ sti();
+
+ if (elp_debug > 0)
+ printk("%s: Resetting the adapter, please wait (approx 20 s)\n",
+ dev->name);
+ /*
+ * take FLSH and ATTN high
+ */
+ outb_control(ATTN|FLSH, dev->base_addr);
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now take them low
+ */
+ outb_control(0, dev->base_addr);
+
+ /*
+ * wait for a little bit
+ */
+ for (timeout = jiffies + 20; jiffies <= timeout; )
+ ;
+
+ /*
+ * now hang around until the board gets it's act together
+ */
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF(dev->base_addr) != ASF_PCB_END)
+ break;
+ restore_flags(flags);
+}
+
+/******************************************************
+ *
+ * queue a receive command on the adapter so we will get an
+ * interrupt when a packet is received.
+ *
+ ******************************************************/
+
+static int
+start_receive (struct device * dev, pcb_struct * tx_pcb)
+{
+ CHECK_NULL(dev);
+ CHECK_NULL(tx_pcb);
+
+ if (elp_debug >= 3)
+ printk("%s: restarting receiver\n", dev->name);
+ tx_pcb->command = CMD_RECEIVE_PACKET;
+ tx_pcb->length = sizeof(struct Rcv_pkt);
+ tx_pcb->data.rcv_pkt.buf_seg
+ = tx_pcb->data.rcv_pkt.buf_ofs = 0; /* Unused */
+ tx_pcb->data.rcv_pkt.buf_len = 1600;
+ tx_pcb->data.rcv_pkt.timeout = 0; /* set timeout to zero */
+ return send_pcb(dev, tx_pcb);
+}
+
+/******************************************************
+ *
+ * extract a packet from the adapter
+ * this routine is only called from within the interrupt
+ * service routine, so no cli/sti calls are needed
+ * note that the length is always assumed to be even
+ *
+ ******************************************************/
+
+static void
+receive_packet (struct device * dev, int len)
+{
+ register int i;
+ unsigned short * ptr;
+ int timeout;
+ int rlen;
+ struct sk_buff *skb;
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+ adapter=dev->priv;
+
+ if (len <= 0 || ((len & ~1) != len))
+ if (elp_debug >= 3) {
+ sti();
+ printk("*** bad packet len %d at %s(%d)\n",len,filename,__LINE__);
+ cli();
+ }
+
+ rlen = (len+1) & ~1;
+
+ skb = dev_alloc_skb(rlen+2);
+
+ /*
+ * make sure the data register is going the right way
+ */
+
+ outb_control(inb_control(dev->base_addr)|DIR, dev->base_addr);
+
+ /*
+ * if buffer could not be allocated, swallow it
+ */
+ if (skb == NULL) {
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = 0;
+ while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+ ;
+ if (timeout >= 20000) {
+ sti();
+ TIMEOUT_MSG(__LINE__);
+ break;
+ }
+
+ inw_data(dev->base_addr);
+ }
+ adapter->stats.rx_dropped++;
+
+ } else {
+ skb_reserve(skb,2); /* 16 byte alignment */
+ skb->dev = dev;
+
+ /*
+ * now read the data from the adapter
+ */
+ ptr = (unsigned short *)skb_put(skb,len);
+ for (i = 0; i < (rlen/2); i++) {
+ timeout = 0;
+ while ((inb_status(dev->base_addr)&HRDY) == 0 && timeout++ < 20000)
+ ;
+ if (timeout >= 20000) {
+ sti();
+ printk("*** timeout at %s(%d) reading word %d of %d ***\n",
+ filename,__LINE__, i, rlen/2);
+ kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+
+ *ptr = inw_data(dev->base_addr);
+ ptr++;
+ }
+
+ sti();
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ }
+
+ outb_control(inb_control(dev->base_addr)&~DIR, dev->base_addr);
+}
+
+
+/******************************************************
+ *
+ * interrupt handler
+ *
+ ******************************************************/
+
+static void
+elp_interrupt (int irq, struct pt_regs *reg_ptr)
+{
+ int len;
+ int dlen;
+ struct device *dev;
+ elp_device * adapter;
+ int timeout;
+
+ if (irq < 0 || irq > 15) {
+ printk ("elp_interrupt(): illegal IRQ number found in interrupt routine (%i)\n", irq);
+ return;
+ }
+
+ dev = irq2dev_map[irq];
+
+ if (dev == NULL) {
+ printk ("elp_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ adapter = (elp_device *) dev->priv;
+
+ CHECK_NULL(adapter);
+
+ if (dev->interrupt)
+ if (elp_debug >= 2)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ /*
+ * allow interrupts (we need timers!)
+ */
+ sti();
+
+ /*
+ * receive a PCB from the adapter
+ */
+ timeout = jiffies + 3;
+ while ((inb_status(dev->base_addr)&ACRF) != 0 && jiffies < timeout) {
+
+ if (receive_pcb(dev, &adapter->irx_pcb)) {
+
+ switch (adapter->irx_pcb.command) {
+
+ /*
+ * received a packet - this must be handled fast
+ */
+ case CMD_RECEIVE_PACKET_COMPLETE:
+ /* if the device isn't open, don't pass packets up the stack */
+ if (dev->start == 0)
+ break;
+ cli();
+ /* Set direction of adapter FIFO */
+ outb_control(inb_control(dev->base_addr)|DIR,
+ dev->base_addr);
+ len = adapter->irx_pcb.data.rcv_resp.pkt_len;
+ dlen = adapter->irx_pcb.data.rcv_resp.buf_len;
+ if (adapter->irx_pcb.data.rcv_resp.timeout != 0) {
+ printk("%s: interrupt - packet not received correctly\n", dev->name);
+ sti();
+ } else {
+ if (elp_debug >= 3) {
+ sti();
+ printk("%s: interrupt - packet received of length %i (%i)\n", dev->name, len, dlen);
+ cli();
+ }
+ receive_packet(dev, dlen);
+ sti();
+ if (elp_debug >= 3)
+ printk("%s: packet received\n", dev->name);
+ }
+ if (dev->start && !start_receive(dev, &adapter->itx_pcb))
+ if (elp_debug >= 2)
+ printk("%s: interrupt - failed to send receive start PCB\n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: receive procedure complete\n", dev->name);
+
+ break;
+
+ /*
+ * 82586 configured correctly
+ */
+ case CMD_CONFIGURE_82586_RESPONSE:
+ adapter->got[CMD_CONFIGURE_82586] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - configure response received\n", dev->name);
+ break;
+
+ /*
+ * Adapter memory configuration
+ */
+ case CMD_CONFIGURE_ADAPTER_RESPONSE:
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Adapter memory configuration %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Multicast list loading
+ */
+ case CMD_LOAD_MULTICAST_RESPONSE:
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Multicast address list loading %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+ /*
+ * Station address setting
+ */
+ case CMD_SET_ADDRESS_RESPONSE:
+ adapter->got[CMD_SET_STATION_ADDRESS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: Ethernet address setting %s.\n",dev->name,
+ adapter->irx_pcb.data.failed?"failed":"succeeded");
+ break;
+
+
+ /*
+ * received board statistics
+ */
+ case CMD_NETWORK_STATISTICS_RESPONSE:
+ adapter->stats.rx_packets += adapter->irx_pcb.data.netstat.tot_recv;
+ adapter->stats.tx_packets += adapter->irx_pcb.data.netstat.tot_xmit;
+ adapter->stats.rx_crc_errors += adapter->irx_pcb.data.netstat.err_CRC;
+ adapter->stats.rx_frame_errors += adapter->irx_pcb.data.netstat.err_align;
+ adapter->stats.rx_fifo_errors += adapter->irx_pcb.data.netstat.err_ovrrun;
+ adapter->got[CMD_NETWORK_STATISTICS] = 1;
+ if (elp_debug >= 3)
+ printk("%s: interrupt - statistics response received\n", dev->name);
+ break;
+
+ /*
+ * sent a packet
+ */
+ case CMD_TRANSMIT_PACKET_COMPLETE:
+ if (elp_debug >= 3)
+ printk("%s: interrupt - packet sent\n", dev->name);
+ if (dev->start == 0)
+ break;
+ if (adapter->irx_pcb.data.xmit_resp.c_stat != 0)
+ if (elp_debug >= 2)
+ printk("%s: interrupt - error sending packet %4.4x\n",
+ dev->name, adapter->irx_pcb.data.xmit_resp.c_stat);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ break;
+
+ /*
+ * some unknown PCB
+ */
+ default:
+ printk("%s: unknown PCB received - %2.2x\n", dev->name, adapter->irx_pcb.command);
+ break;
+ }
+ } else {
+ printk("%s: failed to read PCB on interrupt\n", dev->name);
+ adapter_reset(dev);
+ }
+ }
+
+ /*
+ * indicate no longer in interrupt routine
+ */
+ dev->interrupt = 0;
+}
+
+
+/******************************************************
+ *
+ * open the board
+ *
+ ******************************************************/
+
+static int
+elp_open (struct device *dev)
+{
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+
+ adapter = dev->priv;
+
+ if (elp_debug >= 3)
+ printk("%s: request to open device\n", dev->name);
+
+ /*
+ * make sure we actually found the device
+ */
+ if (adapter == NULL) {
+ printk("%s: Opening a non-existent physical device\n", dev->name);
+ return -EAGAIN;
+ }
+
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0x00, dev->base_addr);
+
+ /*
+ * clear any pending interrupts
+ */
+ inb_command(dev->base_addr);
+ adapter_reset(dev);
+
+ /*
+ * interrupt routine not entered
+ */
+ dev->interrupt = 0;
+
+ /*
+ * transmitter not busy
+ */
+ dev->tbusy = 0;
+
+ /*
+ * make sure we can find the device header given the interrupt number
+ */
+ irq2dev_map[dev->irq] = dev;
+
+ /*
+ * install our interrupt service routine
+ */
+ if (request_irq(dev->irq, &elp_interrupt, 0, "3c505")) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
+
+ /*
+ * enable interrupts on the board
+ */
+ outb_control(CMDE, dev->base_addr);
+
+ /*
+ * device is now officially open!
+ */
+ dev->start = 1;
+
+ /*
+ * configure adapter memory: we need 10 multicast addresses, default==0
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 3c505 memory configuration command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_ADAPTER_MEMORY;
+ adapter->tx_pcb.data.memconf.cmd_q = 10;
+ adapter->tx_pcb.data.memconf.rcv_q = 20;
+ adapter->tx_pcb.data.memconf.mcast = 10;
+ adapter->tx_pcb.data.memconf.frame = 20;
+ adapter->tx_pcb.data.memconf.rcv_b = 20;
+ adapter->tx_pcb.data.memconf.progs = 0;
+ adapter->tx_pcb.length = sizeof(struct Memconf);
+ adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send memory configuration command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_ADAPTER_MEMORY] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
+
+
+ /*
+ * configure adapter to receive broadcast messages and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
+
+ /*
+ * queue receive commands to provide buffering
+ */
+ if (!start_receive(dev, &adapter->tx_pcb))
+ printk("%s: start receive command failed \n", dev->name);
+ if (elp_debug >= 3)
+ printk("%s: start receive command sent\n", dev->name);
+
+ MOD_INC_USE_COUNT;
+
+ return 0; /* Always succeed */
+}
+
+
+/******************************************************
+ *
+ * send a packet to the adapter
+ *
+ ******************************************************/
+
+static int
+send_packet (struct device * dev, unsigned char * ptr, int len)
+{
+ int i;
+ int timeout = 0;
+ elp_device * adapter;
+
+ /*
+ * make sure the length is even and no shorter than 60 bytes
+ */
+ unsigned int nlen = (((len < 60) ? 60 : len) + 1) & (~1);
+
+ CHECK_NULL(dev);
+ CHECK_NULL(ptr);
+
+ adapter = dev->priv;
+
+ if (nlen < len)
+ printk("Warning, bad length nlen=%d len=%d %s(%d)\n",nlen,len,filename,__LINE__);
+
+ /*
+ * send the adapter a transmit packet command. Ignore segment and offset
+ * and make sure the length is even
+ */
+ adapter->tx_pcb.command = CMD_TRANSMIT_PACKET;
+ adapter->tx_pcb.length = sizeof(struct Xmit_pkt);
+ adapter->tx_pcb.data.xmit_pkt.buf_ofs
+ = adapter->tx_pcb.data.xmit_pkt.buf_seg = 0; /* Unused */
+ adapter->tx_pcb.data.xmit_pkt.pkt_len = nlen;
+ if (!send_pcb(dev, &adapter->tx_pcb)) {
+ return FALSE;
+ }
+
+ /*
+ * write data to the adapter
+ */
+ cli();
+ for (i = 0; i < (nlen/2);i++) {
+ while (((inb_status(dev->base_addr)&HRDY) == 0)
+ && (timeout++ < 20000))
+ ;
+ if (timeout >= 20000) {
+ sti();
+ printk("%s: timeout at %s(%d) writing word %d of %d ***\n",
+ dev->name,filename,__LINE__, i, nlen/2);
+ return FALSE;
+ }
+
+ outw_data(*(short *)ptr, dev->base_addr);
+ ptr +=2;
+ }
+ sti();
+
+ return TRUE;
+}
+
+/******************************************************
+ *
+ * start the transmitter
+ * return 0 if sent OK, else return 1
+ *
+ ******************************************************/
+
+static int
+elp_start_xmit (struct sk_buff *skb, struct device *dev)
+{
+ CHECK_NULL(dev);
+
+ /*
+ * not sure what this does, but the 3c509 driver does it, so...
+ */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * if we ended up with a munged length, don't send it
+ */
+ if (skb->len <= 0)
+ return 0;
+
+ if (elp_debug >= 3)
+ printk("%s: request to send packet of length %d\n", dev->name, (int)skb->len);
+
+ /*
+ * if the transmitter is still busy, we have a transmit timeout...
+ */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int stat;
+ if (tickssofar < 50) /* was 500, AJT */
+ return 1;
+ printk("%s: transmit timed out, not resetting adapter\n", dev->name);
+ if (((stat=inb_status(dev->base_addr))&ACRF) != 0)
+ printk("%s: hmmm...seemed to have missed an interrupt!\n", dev->name);
+ printk("%s: status %#02x\n", dev->name, stat);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ }
+
+ /*
+ * send the packet at skb->data for skb->len
+ */
+ if (!send_packet(dev, skb->data, skb->len)) {
+ printk("%s: send packet PCB failed\n", dev->name);
+ return 1;
+ }
+
+ if (elp_debug >= 3)
+ printk("%s: packet of length %d sent\n", dev->name, (int)skb->len);
+
+
+ /*
+ * start the transmit timeout
+ */
+ dev->trans_start = jiffies;
+
+ /*
+ * the transmitter is now busy
+ */
+ dev->tbusy = 1;
+
+ /*
+ * free the buffer
+ */
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+/******************************************************
+ *
+ * return statistics on the board
+ *
+ ******************************************************/
+
+static struct enet_statistics *
+elp_get_stats (struct device *dev)
+{
+ elp_device *adapter = (elp_device *) dev->priv;
+
+ if (elp_debug >= 3)
+ printk("%s: request for stats\n", dev->name);
+
+ /* If the device is closed, just return the latest stats we have,
+ - we cannot ask from the adapter without interrupts */
+ if (!dev->start)
+ return &adapter->stats;
+
+ /* send a get statistics command to the board */
+ adapter->tx_pcb.command = CMD_NETWORK_STATISTICS;
+ adapter->tx_pcb.length = 0;
+ adapter->got[CMD_NETWORK_STATISTICS] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send get statistics command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_NETWORK_STATISTICS] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ return &adapter->stats;
+ }
+ }
+
+ /* statistics are now up to date */
+ return &adapter->stats;
+}
+
+/******************************************************
+ *
+ * close the board
+ *
+ ******************************************************/
+
+static int
+elp_close (struct device *dev)
+{
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+ adapter = dev->priv;
+ CHECK_NULL(adapter);
+
+ if (elp_debug >= 3)
+ printk("%s: request to close device\n", dev->name);
+
+ /* Someone may request the device statistic information even when
+ * the interface is closed. The following will update the statistics
+ * structure in the driver, so we'll be able to give current statistics.
+ */
+ (void) elp_get_stats(dev);
+
+ /*
+ * disable interrupts on the board
+ */
+ outb_control(0x00, dev->base_addr);
+
+ /*
+ * flag transmitter as busy (i.e. not available)
+ */
+ dev->tbusy = 1;
+
+ /*
+ * indicate device is closed
+ */
+ dev->start = 0;
+
+ /*
+ * release the IRQ
+ */
+ free_irq(dev->irq);
+
+ /*
+ * and we no longer have to map irq to dev either
+ */
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+/************************************************************
+ *
+ * Set multicast list
+ * num_addrs==0: clear mc_list
+ * num_addrs==-1: set promiscuous mode
+ * num_addrs>0: set mc_list
+ *
+ ************************************************************/
+
+static void
+elp_set_mc_list (struct device *dev)
+{
+ elp_device *adapter = (elp_device *) dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ int i;
+
+ if (elp_debug >= 3)
+ printk("%s: request to set multicast list\n", dev->name);
+
+ if (!(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
+ {
+ /* send a "load multicast list" command to the board, max 10 addrs/cmd */
+ /* if num_addrs==0 the list will be cleared */
+ adapter->tx_pcb.command = CMD_LOAD_MULTICAST_LIST;
+ adapter->tx_pcb.length = 6*dev->mc_count;
+ for (i=0;i<dev->mc_count;i++)
+ {
+ memcpy(adapter->tx_pcb.data.multicast[i], dmi->dmi_addr,6);
+ dmi=dmi->next;
+ }
+ adapter->got[CMD_LOAD_MULTICAST_LIST] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send set_multicast command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_LOAD_MULTICAST_LIST] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout) {
+ TIMEOUT_MSG(__LINE__);
+ }
+ }
+ if (dev->mc_count)
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD | RECV_MULTI;
+ else /* num_addrs == 0 */
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_BROAD;
+ }
+ else
+ adapter->tx_pcb.data.configure = NO_LOOPBACK | RECV_PROMISC;
+ /*
+ * configure adapter to receive messages (as specified above)
+ * and wait for response
+ */
+ if (elp_debug >= 3)
+ printk("%s: sending 82586 configure command\n", dev->name);
+ adapter->tx_pcb.command = CMD_CONFIGURE_82586;
+ adapter->tx_pcb.length = 2;
+ adapter->got[CMD_CONFIGURE_82586] = 0;
+ if (!send_pcb(dev, &adapter->tx_pcb))
+ printk("%s: couldn't send 82586 configure command\n", dev->name);
+ else {
+ int timeout = jiffies + TIMEOUT;
+ while (adapter->got[CMD_CONFIGURE_82586] == 0 && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ TIMEOUT_MSG(__LINE__);
+ }
+}
+
+/******************************************************
+ *
+ * initialise Etherlink Plus board
+ *
+ ******************************************************/
+
+static void
+elp_init (struct device *dev)
+{
+ elp_device * adapter;
+
+ CHECK_NULL(dev);
+
+ /*
+ * set ptrs to various functions
+ */
+ dev->open = elp_open; /* local */
+ dev->stop = elp_close; /* local */
+ dev->get_stats = elp_get_stats; /* local */
+ dev->hard_start_xmit = elp_start_xmit; /* local */
+ dev->set_multicast_list = elp_set_mc_list; /* local */
+
+ /* Setup the generic properties */
+ ether_setup(dev);
+
+ /*
+ * setup ptr to adapter specific information
+ */
+ adapter = (elp_device *)(dev->priv = kmalloc(sizeof(elp_device), GFP_KERNEL));
+ CHECK_NULL(adapter);
+ if (adapter == NULL)
+ return;
+ memset(&(adapter->stats), 0, sizeof(struct enet_statistics));
+
+ /*
+ * memory information
+ */
+ dev->mem_start = dev->mem_end = dev->rmem_end = dev->rmem_start = 0;
+}
+
+/************************************************************
+ *
+ * A couple of tests to see if there's 3C505 or not
+ * Called only by elp_autodetect
+ ************************************************************/
+
+static int
+elp_sense (struct device * dev)
+{
+ int timeout;
+ int addr=dev->base_addr;
+ const char *name=dev->name;
+ long flags;
+ byte orig_HCR, orig_HSR;
+
+ if (check_region(addr, 0xf))
+ return -1;
+
+ orig_HCR=inb_control(addr);
+ orig_HSR=inb_status(addr);
+
+ if (elp_debug > 0)
+ printk(search_msg, name, addr);
+
+ if (((orig_HCR==0xff) && (orig_HSR==0xff)) ||
+ ((orig_HCR & DIR) != (orig_HSR & DIR))) {
+ if (elp_debug > 0)
+ printk(notfound_msg, 1);
+ return -1; /* It can't be 3c505 if HCR.DIR != HSR.DIR */
+ }
+
+ /* Enable interrupts - we need timers! */
+ save_flags(flags);
+ sti();
+
+ /* Wait for a while; the adapter may still be booting up */
+ if (elp_debug > 0)
+ printk(stilllooking_msg);
+ for (timeout = jiffies + (100 * 15); jiffies <= timeout; )
+ if (GET_ASF(addr) != ASF_PCB_END)
+ break;
+
+ if (orig_HCR & DIR) {
+ /* If HCR.DIR is up, we pull it down. HSR.DIR should follow. */
+ outb_control(orig_HCR & ~DIR,addr);
+ timeout = jiffies+30;
+ while (jiffies < timeout)
+ ;
+ restore_flags(flags);
+ if (inb_status(addr) & DIR) {
+ outb_control(orig_HCR,addr);
+ if (elp_debug > 0)
+ printk(notfound_msg, 2);
+ return -1;
+ }
+ } else {
+ /* If HCR.DIR is down, we pull it up. HSR.DIR should follow. */
+ outb_control(orig_HCR | DIR,addr);
+ timeout = jiffies+300;
+ while (jiffies < timeout)
+ ;
+ restore_flags(flags);
+ if (!(inb_status(addr) & DIR)) {
+ outb_control(orig_HCR,addr);
+ if (elp_debug > 0)
+ printk(notfound_msg, 3);
+ return -1;
+ }
+ }
+ /*
+ * It certainly looks like a 3c505. If it has DMA enabled, it needs
+ * a hard reset. Also, do a hard reset if selected at the compile time.
+ */
+ if (elp_debug > 0)
+ printk(found_msg);
+
+ if (((orig_HCR==0x35) && (orig_HSR==0x5b)) || ELP_NEED_HARD_RESET)
+ adapter_hard_reset(dev);
+ return 0;
+}
+
+/*************************************************************
+ *
+ * Search through addr_list[] and try to find a 3C505
+ * Called only by eplus_probe
+ *************************************************************/
+
+static int
+elp_autodetect (struct device * dev)
+{
+ int idx=0;
+
+ /* if base address set, then only check that address
+ otherwise, run through the table */
+ if (dev->base_addr != 0) { /* dev->base_addr == 0 ==> plain autodetect */
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ } else while ( (dev->base_addr=addr_list[idx++]) ) {
+ if (elp_sense(dev) == 0)
+ return dev->base_addr;
+ }
+
+ /* could not find an adapter */
+ if (elp_debug > 0)
+ printk(couldnot_msg, dev->name);
+
+ return 0; /* Because of this, the layer above will return -ENODEV */
+}
+
+/******************************************************
+ *
+ * probe for an Etherlink Plus board at the specified address
+ *
+ ******************************************************/
+
+int
+elplus_probe (struct device *dev)
+{
+ elp_device adapter;
+ int i;
+
+ CHECK_NULL(dev);
+
+ /*
+ * setup adapter structure
+ */
+
+ dev->base_addr = elp_autodetect(dev);
+ if ( !(dev->base_addr) )
+ return -ENODEV;
+
+ /*
+ * As we enter here from bootup, the adapter should have IRQs enabled,
+ * but we can as well enable them anyway.
+ */
+ outb_control(inb_control(dev->base_addr) | CMDE, dev->base_addr);
+ autoirq_setup(0);
+
+ /*
+ * use ethernet address command to probe for board in polled mode
+ * (this also makes us the IRQ that we need for automatic detection)
+ */
+ adapter.tx_pcb.command = CMD_STATION_ADDRESS;
+ adapter.tx_pcb.length = 0;
+ if (!send_pcb (dev, &adapter.tx_pcb) ||
+ !receive_pcb(dev, &adapter.rx_pcb) ||
+ (adapter.rx_pcb.command != CMD_ADDRESS_RESPONSE) ||
+ (adapter.rx_pcb.length != 6)) {
+ printk("%s: not responding to first PCB\n", dev->name);
+ return -ENODEV;
+ }
+
+ if (dev->irq) { /* Is there a preset IRQ? */
+ if (dev->irq != autoirq_report(0)) {
+ printk("%s: Detected IRQ doesn't match user-defined one.\n",dev->name);
+ return -ENODEV;
+ }
+ /* if dev->irq == autoirq_report(0), all is well */
+ } else /* No preset IRQ; just use what we can detect */
+ dev->irq=autoirq_report(0);
+ switch (dev->irq) { /* Legal, sane? */
+ case 0:
+ printk("%s: No IRQ reported by autoirq_report().\n",dev->name);
+ printk("%s: Check the jumpers of your 3c505 board.\n",dev->name);
+ return -ENODEV;
+ case 1:
+ case 6:
+ case 8:
+ case 13:
+ printk("%s: Impossible IRQ %d reported by autoirq_report().\n",
+ dev->name, dev->irq);
+ return -ENODEV;
+ }
+ /*
+ * Now we have the IRQ number so we can disable the interrupts from
+ * the board until the board is opened.
+ */
+ outb_control(inb_control(dev->base_addr) & ~CMDE, dev->base_addr);
+
+ /*
+ * copy ethernet address into structure
+ */
+ for (i = 0; i < 6; i++)
+ dev->dev_addr[i] = adapter.rx_pcb.data.eth_addr[i];
+
+ /*
+ * print remainder of startup message
+ */
+ printk("%s: 3c505 card found at I/O %#lx using IRQ%d"
+ " has address %02x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name, dev->base_addr, dev->irq,
+ dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /*
+ * and reserve the address region
+ */
+ request_region(dev->base_addr, ELP_IO_EXTENT, "3c505");
+
+ /*
+ * initialise the device
+ */
+ elp_init(dev);
+ return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_3c505 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, elplus_probe };
+
+int io = 0x300;
+int irq = 0;
+
+int init_module(void)
+{
+ if (io == 0)
+ printk("3c505: You should not use auto-probing with insmod!\n");
+ dev_3c505.base_addr = io;
+ dev_3c505.irq = irq;
+ if (register_netdev(&dev_3c505) != 0) {
+ printk("3c505: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_3c505);
+ kfree(dev_3c505.priv);
+ dev_3c505.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_3c505.base_addr, ELP_IO_EXTENT);
+}
+#endif /* MODULE */
diff --git a/i386/i386at/gpl/linux/net/3c505.h b/i386/i386at/gpl/linux/net/3c505.h
new file mode 100644
index 00000000..f7d28368
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c505.h
@@ -0,0 +1,245 @@
+/*****************************************************************
+ *
+ * defines for 3Com Etherlink Plus adapter
+ *
+ *****************************************************************/
+
+/*
+ * I/O register offsets
+ */
+#define PORT_COMMAND 0x00 /* read/write, 8-bit */
+#define PORT_STATUS 0x02 /* read only, 8-bit */
+#define PORT_AUXDMA 0x02 /* write only, 8-bit */
+#define PORT_DATA 0x04 /* read/write, 16-bit */
+#define PORT_CONTROL 0x06 /* read/write, 8-bit */
+
+#define ELP_IO_EXTENT 0x10 /* size of used IO registers */
+
+/*
+ * host control registers bits
+ */
+#define ATTN 0x80 /* attention */
+#define FLSH 0x40 /* flush data register */
+#define DMAE 0x20 /* DMA enable */
+#define DIR 0x10 /* direction */
+#define TCEN 0x08 /* terminal count interrupt enable */
+#define CMDE 0x04 /* command register interrupt enable */
+#define HSF2 0x02 /* host status flag 2 */
+#define HSF1 0x01 /* host status flag 1 */
+
+/*
+ * combinations of HSF flags used for PCB transmission
+ */
+#define HSF_PCB_ACK HSF1
+#define HSF_PCB_NAK HSF2
+#define HSF_PCB_END (HSF2|HSF1)
+#define HSF_PCB_MASK (HSF2|HSF1)
+
+/*
+ * host status register bits
+ */
+#define HRDY 0x80 /* data register ready */
+#define HCRE 0x40 /* command register empty */
+#define ACRF 0x20 /* adapter command register full */
+/* #define DIR 0x10 direction - same as in control register */
+#define DONE 0x08 /* DMA done */
+#define ASF3 0x04 /* adapter status flag 3 */
+#define ASF2 0x02 /* adapter status flag 2 */
+#define ASF1 0x01 /* adapter status flag 1 */
+
+/*
+ * combinations of ASF flags used for PCB reception
+ */
+#define ASF_PCB_ACK ASF1
+#define ASF_PCB_NAK ASF2
+#define ASF_PCB_END (ASF2|ASF1)
+#define ASF_PCB_MASK (ASF2|ASF1)
+
+/*
+ * host aux DMA register bits
+ */
+#define DMA_BRST 0x01 /* DMA burst */
+
+/*
+ * maximum amount of data data allowed in a PCB
+ */
+#define MAX_PCB_DATA 62
+
+/*****************************************************************
+ *
+ * timeout value
+ * this is a rough value used for loops to stop them from
+ * locking up the whole machine in the case of failure or
+ * error conditions
+ *
+ *****************************************************************/
+
+#define TIMEOUT 300
+
+/*****************************************************************
+ *
+ * PCB commands
+ *
+ *****************************************************************/
+
+enum {
+ /*
+ * host PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_MEMORY = 0x01,
+ CMD_CONFIGURE_82586 = 0x02,
+ CMD_STATION_ADDRESS = 0x03,
+ CMD_DMA_DOWNLOAD = 0x04,
+ CMD_DMA_UPLOAD = 0x05,
+ CMD_PIO_DOWNLOAD = 0x06,
+ CMD_PIO_UPLOAD = 0x07,
+ CMD_RECEIVE_PACKET = 0x08,
+ CMD_TRANSMIT_PACKET = 0x09,
+ CMD_NETWORK_STATISTICS = 0x0a,
+ CMD_LOAD_MULTICAST_LIST = 0x0b,
+ CMD_CLEAR_PROGRAM = 0x0c,
+ CMD_DOWNLOAD_PROGRAM = 0x0d,
+ CMD_EXECUTE_PROGRAM = 0x0e,
+ CMD_SELF_TEST = 0x0f,
+ CMD_SET_STATION_ADDRESS = 0x10,
+ CMD_ADAPTER_INFO = 0x11,
+ NUM_TRANSMIT_CMDS,
+
+ /*
+ * adapter PCB commands
+ */
+ CMD_CONFIGURE_ADAPTER_RESPONSE = 0x31,
+ CMD_CONFIGURE_82586_RESPONSE = 0x32,
+ CMD_ADDRESS_RESPONSE = 0x33,
+ CMD_DOWNLOAD_DATA_REQUEST = 0x34,
+ CMD_UPLOAD_DATA_REQUEST = 0x35,
+ CMD_RECEIVE_PACKET_COMPLETE = 0x38,
+ CMD_TRANSMIT_PACKET_COMPLETE = 0x39,
+ CMD_NETWORK_STATISTICS_RESPONSE = 0x3a,
+ CMD_LOAD_MULTICAST_RESPONSE = 0x3b,
+ CMD_CLEAR_PROGRAM_RESPONSE = 0x3c,
+ CMD_DOWNLOAD_PROGRAM_RESPONSE = 0x3d,
+ CMD_EXECUTE_RESPONSE = 0x3e,
+ CMD_SELF_TEST_RESPONSE = 0x3f,
+ CMD_SET_ADDRESS_RESPONSE = 0x40,
+ CMD_ADAPTER_INFO_RESPONSE = 0x41
+};
+
+/* Definitions for the PCB data structure */
+
+/* Data units */
+typedef unsigned char byte;
+typedef unsigned short int word;
+typedef unsigned long int dword;
+
+/* Data structures */
+struct Memconf {
+ word cmd_q,
+ rcv_q,
+ mcast,
+ frame,
+ rcv_b,
+ progs;
+};
+
+struct Rcv_pkt {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ timeout;
+};
+
+struct Xmit_pkt {
+ word buf_ofs,
+ buf_seg,
+ pkt_len;
+};
+
+struct Rcv_resp {
+ word buf_ofs,
+ buf_seg,
+ buf_len,
+ pkt_len,
+ timeout,
+ status;
+ dword timetag;
+};
+
+struct Xmit_resp {
+ word buf_ofs,
+ buf_seg,
+ c_stat,
+ status;
+};
+
+
+struct Netstat {
+ dword tot_recv,
+ tot_xmit;
+ word err_CRC,
+ err_align,
+ err_res,
+ err_ovrrun;
+};
+
+
+struct Selftest {
+ word error;
+ union {
+ word ROM_cksum;
+ struct {
+ word ofs, seg;
+ } RAM;
+ word i82586;
+ } failure;
+};
+
+struct Info {
+ byte minor_vers,
+ major_vers;
+ word ROM_cksum,
+ RAM_sz,
+ free_ofs,
+ free_seg;
+};
+
+struct Memdump {
+ word size,
+ off,
+ seg;
+};
+
+/*
+Primary Command Block. The most important data structure. All communication
+between the host and the adapter is done with these. (Except for the actual
+ethernet data, which has different packaging.)
+*/
+typedef struct {
+ byte command;
+ byte length;
+ union {
+ struct Memconf memconf;
+ word configure;
+ struct Rcv_pkt rcv_pkt;
+ struct Xmit_pkt xmit_pkt;
+ byte multicast[10][6];
+ byte eth_addr[6];
+ byte failed;
+ struct Rcv_resp rcv_resp;
+ struct Xmit_resp xmit_resp;
+ struct Netstat netstat;
+ struct Selftest selftest;
+ struct Info info;
+ struct Memdump memdump;
+ byte raw[62];
+ } data;
+} pcb_struct;
+
+/* These defines for 'configure' */
+#define RECV_STATION 0x00
+#define RECV_BROAD 0x01
+#define RECV_MULTI 0x02
+#define RECV_PROMISC 0x04
+#define NO_LOOPBACK 0x00
+#define INT_LOOPBACK 0x08
+#define EXT_LOOPBACK 0x10
diff --git a/i386/i386at/gpl/linux/net/3c507.c b/i386/i386at/gpl/linux/net/3c507.c
new file mode 100644
index 00000000..f18bc0a3
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c507.c
@@ -0,0 +1,923 @@
+/* 3c507.c: An EtherLink16 device driver for Linux. */
+/*
+ Written 1993,1994 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Thanks go to jennings@Montrouge.SMR.slb.com ( Patrick Jennings)
+ and jrs@world.std.com (Rick Sladkey) for testing and bugfixes.
+ Mark Salazar <leslie@access.digex.net> made the changes for cards with
+ only 16K packet buffers.
+
+ Things remaining to do:
+ Verify that the tx and rx buffers don't have fencepost errors.
+ Move the theory of operation and memory map documentation.
+ The statistics need to be updated correctly.
+*/
+
+static const char *version =
+ "3c507.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+
+/*
+ Sources:
+ This driver wouldn't have been written with the availability of the
+ Crynwr driver source code. It provided a known-working implementation
+ that filled in the gaping holes of the Intel documentation. Three cheers
+ for Russ Nelson.
+
+ Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ info that the casual reader might think that it documents the i82586 :-<.
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/malloc.h>
+
+
+/* use 0 for production, 1 for verification, 2..7 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* A zero-terminated list of common I/O addresses to be probed. */
+static unsigned int netcard_portlist[] =
+ { 0x300, 0x320, 0x340, 0x280, 0};
+
+/*
+ Details of the i82586.
+
+ You'll really need the databook to understand the details of this part,
+ but the outline is that the i82586 has two separate processing units.
+ Both are started from a list of three configuration tables, of which only
+ the last, the System Control Block (SCB), is used after reset-time. The SCB
+ has the following fields:
+ Status word
+ Command word
+ Tx/Command block addr.
+ Rx block addr.
+ The command word accepts the following controls for the Tx and Rx units:
+ */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+
+/* The Rx unit uses a list of frame descriptors and a list of data buffer
+ descriptors. We use full-sized (1518 byte) data buffers, so there is
+ a one-to-one pairing of frame descriptors to buffer descriptors.
+
+ The Tx ("command") unit executes a list of commands that look like:
+ Status word Written by the 82586 when the command is done.
+ Command word Command in lower 3 bits, post-command action in upper 3
+ Link word The address of the next command.
+ Parameters (as needed).
+
+ Some definitions related to the Command Word are:
+ */
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ int last_restart;
+ ushort rx_head;
+ ushort rx_tail;
+ ushort tx_head;
+ ushort tx_cmd_link;
+ ushort tx_reap;
+};
+
+/*
+ Details of the EtherLink16 Implementation
+ The 3c507 is a generic shared-memory i82586 implementation.
+ The host can map 16K, 32K, 48K, or 64K of the 64K memory into
+ 0x0[CD][08]0000, or all 64K into 0xF[02468]0000.
+ */
+
+/* Offsets from the base I/O address. */
+#define SA_DATA 0 /* Station address data, or 3Com signature. */
+#define MISC_CTRL 6 /* Switch the SA_DATA banks, and bus config bits. */
+#define RESET_IRQ 10 /* Reset the latched IRQ line. */
+#define SIGNAL_CA 11 /* Frob the 82586 Channel Attention line. */
+#define ROM_CONFIG 13
+#define MEM_CONFIG 14
+#define IRQ_CONFIG 15
+#define EL16_IO_EXTENT 16
+
+/* The ID port is used at boot-time to locate the ethercard. */
+#define ID_PORT 0x100
+
+/* Offsets to registers in the mailbox (SCB). */
+#define iSCB_STATUS 0x8
+#define iSCB_CMD 0xA
+#define iSCB_CBL 0xC /* Command BLock offset. */
+#define iSCB_RFA 0xE /* Rx Frame Area offset. */
+
+/* Since the 3c507 maps the shared memory window so that the last byte is
+ at 82586 address FFFF, the first byte is at 82586 address 0, 16K, 32K, or
+ 48K corresponding to window sizes of 64K, 48K, 32K and 16K respectively.
+ We can account for this be setting the 'SBC Base' entry in the ISCP table
+ below for all the 16 bit offset addresses, and also adding the 'SCB Base'
+ value to all 24 bit physical addresses (in the SCP table and the TX and RX
+ Buffer Descriptors).
+ -Mark
+ */
+#define SCB_BASE ((unsigned)64*1024 - (dev->mem_end - dev->mem_start))
+
+/*
+ What follows in 'init_words[]' is the "program" that is downloaded to the
+ 82586 memory. It's mostly tables and command blocks, and starts at the
+ reset address 0xfffff6. This is designed to be similar to the EtherExpress,
+ thus the unusual location of the SCB at 0x0008.
+
+ Even with the additional "don't care" values, doing it this way takes less
+ program space than initializing the individual tables, and I feel it's much
+ cleaner.
+
+ The databook is particularly useless for the first two structures, I had
+ to use the Crynwr driver as an example.
+
+ The memory setup is as follows:
+ */
+
+#define CONFIG_CMD 0x0018
+#define SET_SA_CMD 0x0024
+#define SA_OFFSET 0x002A
+#define IDLELOOP 0x30
+#define TDR_CMD 0x38
+#define TDR_TIME 0x3C
+#define DUMP_CMD 0x40
+#define DIAG_CMD 0x48
+#define SET_MC_CMD 0x4E
+#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
+
+#define TX_BUF_START 0x0100
+#define NUM_TX_BUFS 4
+#define TX_BUF_SIZE (1518+14+20+16) /* packet+header+TBD */
+
+#define RX_BUF_START 0x2000
+#define RX_BUF_SIZE (1518+14+18) /* packet+header+RBD */
+#define RX_BUF_END (dev->mem_end - dev->mem_start)
+
+/*
+ That's it: only 86 bytes to set up the beast, including every extra
+ command available. The 170 byte buffer at DUMP_DATA is shared between the
+ Dump command (called only by the diagnostic program) and the SetMulticastList
+ command.
+
+ To complete the memory setup you only have to write the station address at
+ SA_OFFSET and create the Tx & Rx buffer lists.
+
+ The Tx command chain and buffer list is setup as follows:
+ A Tx command table, with the data buffer pointing to...
+ A Tx data buffer descriptor. The packet is in a single buffer, rather than
+ chaining together several smaller buffers.
+ A NoOp command, which initially points to itself,
+ And the packet data.
+
+ A transmit is done by filling in the Tx command table and data buffer,
+ re-writing the NoOp command, and finally changing the offset of the last
+ command to point to the current Tx command. When the Tx command is finished,
+ it jumps to the NoOp, when it loops until the next Tx command changes the
+ "link offset" in the NoOp. This way the 82586 never has to go through the
+ slow restart sequence.
+
+ The Rx buffer list is set up in the obvious ring structure. We have enough
+ memory (and low enough interrupt latency) that we can avoid the complicated
+ Rx buffer linked lists by alway associating a full-size Rx data buffer with
+ each Rx data frame.
+
+ I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+ use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+
+ */
+
+unsigned short init_words[] = {
+ /* System Configuration Pointer (SCP). */
+ 0x0000, /* Set bus size to 16 bits. */
+ 0,0, /* pad words. */
+ 0x0000,0x0000, /* ISCP phys addr, set in init_82586_mem(). */
+
+ /* Intermediate System Configuration Pointer (ISCP). */
+ 0x0001, /* Status word that's cleared when init is done. */
+ 0x0008,0,0, /* SCB offset, (skip, skip) */
+
+ /* System Control Block (SCB). */
+ 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
+ CONFIG_CMD, /* Command list pointer, points to Configure. */
+ RX_BUF_START, /* Rx block list. */
+ 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
+
+ /* 0x0018: Configure command. Change to put MAC data with packet. */
+ 0, CmdConfigure, /* Status, command. */
+ SET_SA_CMD, /* Next command is Set Station Addr. */
+ 0x0804, /* "4" bytes of config data, 8 byte FIFO. */
+ 0x2e40, /* Magic values, including MAC data location. */
+ 0, /* Unused pad word. */
+
+ /* 0x0024: Setup station address command. */
+ 0, CmdSASetup,
+ SET_MC_CMD, /* Next command. */
+ 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
+
+ /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
+ 0, CmdNOp, IDLELOOP, 0 /* pad */,
+
+ /* 0x0038: A unused Time-Domain Reflectometer command. */
+ 0, CmdTDR, IDLELOOP, 0,
+
+ /* 0x0040: An unused Dump State command. */
+ 0, CmdDump, IDLELOOP, DUMP_DATA,
+
+ /* 0x0048: An unused Diagnose command. */
+ 0, CmdDiagnose, IDLELOOP,
+
+ /* 0x004E: An empty set-multicast-list command. */
+ 0, CmdMulticastList, IDLELOOP, 0,
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int el16_probe(struct device *dev); /* Called from Space.c */
+
+static int el16_probe1(struct device *dev, int ioaddr);
+static int el16_open(struct device *dev);
+static int el16_send_packet(struct sk_buff *skb, struct device *dev);
+static void el16_interrupt(int irq, struct pt_regs *regs);
+static void el16_rx(struct device *dev);
+static int el16_close(struct device *dev);
+static struct enet_statistics *el16_get_stats(struct device *dev);
+
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+void init_82586_mem(struct device *dev);
+
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"3c507", el16_probe1, EL16_IO_EXTENT, netcard_portlist};
+#endif
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, (detachable devices only) allocate space for the
+ device and return success.
+ */
+int
+el16_probe(struct device *dev)
+{
+ int base_addr = dev ? dev->base_addr : 0;
+ int i;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return el16_probe1(dev, base_addr);
+ else if (base_addr != 0)
+ return ENXIO; /* Don't probe at all. */
+
+ for (i = 0; netcard_portlist[i]; i++) {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, EL16_IO_EXTENT))
+ continue;
+ if (el16_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int el16_probe1(struct device *dev, int ioaddr)
+{
+ static unsigned char init_ID_done = 0, version_printed = 0;
+ int i, irq, irqval;
+
+ if (init_ID_done == 0) {
+ ushort lrs_state = 0xff;
+ /* Send the ID sequence to the ID_PORT to enable the board(s). */
+ outb(0x00, ID_PORT);
+ for(i = 0; i < 255; i++) {
+ outb(lrs_state, ID_PORT);
+ lrs_state <<= 1;
+ if (lrs_state & 0x100)
+ lrs_state ^= 0xe7;
+ }
+ outb(0x00, ID_PORT);
+ init_ID_done = 1;
+ }
+
+ if (inb(ioaddr) == '*' && inb(ioaddr+1) == '3'
+ && inb(ioaddr+2) == 'C' && inb(ioaddr+3) == 'O')
+ ;
+ else
+ return ENODEV;
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ if (net_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: 3c507 at %#x,", dev->name, ioaddr);
+
+ /* We should make a few more checks here, like the first three octets of
+ the S.A. for the manufacturer's code. */
+
+ irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+ irqval = request_irq(irq, &el16_interrupt, 0, "3c507");
+ if (irqval) {
+ printk ("unable to get IRQ %d (irqval=%d).\n", irq, irqval);
+ return EAGAIN;
+ }
+
+ /* We've committed to using the board, and can start filling in *dev. */
+ request_region(ioaddr, EL16_IO_EXTENT, "3c507");
+ dev->base_addr = ioaddr;
+
+ outb(0x01, ioaddr + MISC_CTRL);
+ for (i = 0; i < 6; i++) {
+ dev->dev_addr[i] = inb(ioaddr + i);
+ printk(" %02x", dev->dev_addr[i]);
+ }
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+#ifdef MEM_BASE
+ dev->mem_start = MEM_BASE;
+ dev->mem_end = dev->mem_start + 0x10000;
+#else
+ {
+ int base;
+ int size;
+ char mem_config = inb(ioaddr + MEM_CONFIG);
+ if (mem_config & 0x20) {
+ size = 64*1024;
+ base = 0xf00000 + (mem_config & 0x08 ? 0x080000
+ : ((mem_config & 3) << 17));
+ } else {
+ size = ((mem_config & 3) + 1) << 14;
+ base = 0x0c0000 + ( (mem_config & 0x18) << 12);
+ }
+ dev->mem_start = base;
+ dev->mem_end = base + size;
+ }
+#endif
+
+ dev->if_port = (inb(ioaddr + ROM_CONFIG) & 0x80) ? 1 : 0;
+ dev->irq = inb(ioaddr + IRQ_CONFIG) & 0x0f;
+
+ printk(", IRQ %d, %sternal xcvr, memory %#lx-%#lx.\n", dev->irq,
+ dev->if_port ? "ex" : "in", dev->mem_start, dev->mem_end-1);
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = el16_open;
+ dev->stop = el16_close;
+ dev->hard_start_xmit = el16_send_packet;
+ dev->get_stats = el16_get_stats;
+
+ ether_setup(dev); /* Generic ethernet behaviour */
+
+ dev->flags&=~IFF_MULTICAST; /* Multicast doesn't work */
+
+ return 0;
+}
+
+
+
+static int
+el16_open(struct device *dev)
+{
+ irq2dev_map[dev->irq] = dev;
+
+ /* Initialize the 82586 memory and start it. */
+ init_82586_mem(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+el16_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short *shmem = (short*)dev->mem_start;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s? ", dev->name,
+ shmem[iSCB_STATUS>>1] & 0x8000 ? "IRQ conflict" :
+ "network cable problem");
+ /* Try to restart the adaptor. */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (net_debug > 1) printk("Resetting board.\n");
+ /* Completely reset the adaptor. */
+ init_82586_mem(dev);
+ } else {
+ /* Issue the channel attention signal and hope it "gets better". */
+ if (net_debug > 1) printk("Kicking board.\n");
+ shmem[iSCB_CMD>>1] = 0xf000|CUC_START|RX_START;
+ outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ /* Enable the 82586 interrupt input. */
+ outb(0x84, ioaddr + MISC_CTRL);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+el16_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+ ushort ack_cmd = 0;
+ ushort *shmem;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+ shmem = ((ushort*)dev->mem_start);
+
+ status = shmem[iSCB_STATUS>>1];
+
+ if (net_debug > 4) {
+ printk("%s: 3c507 interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+
+ /* Reap the Tx packet buffers. */
+ while (lp->tx_reap != lp->tx_head) {
+ unsigned short tx_status = shmem[lp->tx_reap>>1];
+
+ if (tx_status == 0) {
+ if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
+ break;
+ }
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->stats.tx_errors++;
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ }
+ if (net_debug > 5)
+ printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
+ lp->tx_reap += TX_BUF_SIZE;
+ if (lp->tx_reap > RX_BUF_START - TX_BUF_SIZE)
+ lp->tx_reap = TX_BUF_START;
+ if (++boguscount > 4)
+ break;
+ }
+
+ if (status & 0x4000) { /* Packet received. */
+ if (net_debug > 5)
+ printk("Received packet, rx_head %04x.\n", lp->rx_head);
+ el16_rx(dev);
+ }
+
+ /* Acknowledge the interrupt sources. */
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x0700) != 0x0200 && dev->start) {
+ if (net_debug)
+ printk("%s: Command unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ /* If this ever occurs we should really re-write the idle loop, reset
+ the Tx list, and do a complete restart of the command unit.
+ For now we rely on the Tx timeout if the resume doesn't work. */
+ ack_cmd |= CUC_RESUME;
+ }
+
+ if ((status & 0x0070) != 0x0040 && dev->start) {
+ static void init_rx_bufs(struct device *);
+ /* The Rx unit is not ready, it must be hung. Restart the receiver by
+ initializing the rx buffers, and issuing an Rx start command. */
+ if (net_debug)
+ printk("%s: Rx unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ init_rx_bufs(dev);
+ shmem[iSCB_RFA >> 1] = RX_BUF_START;
+ ack_cmd |= RX_START;
+ }
+
+ shmem[iSCB_CMD>>1] = ack_cmd;
+ outb(0, ioaddr + SIGNAL_CA); /* Issue channel-attn. */
+
+ /* Clear the latched interrupt. */
+ outb(0, ioaddr + RESET_IRQ);
+
+ /* Enable the 82586's interrupt input. */
+ outb(0x84, ioaddr + MISC_CTRL);
+
+ return;
+}
+
+static int
+el16_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ ushort *shmem = (short*)dev->mem_start;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx. */
+ shmem[iSCB_CMD >> 1] = RX_SUSPEND | CUC_SUSPEND;
+ outb(0, ioaddr + SIGNAL_CA);
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(0x80, ioaddr + MISC_CTRL);
+
+ /* We always physically use the IRQ line, so we don't do free_irq().
+ We do remove ourselves from the map. */
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. */
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+el16_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ /* ToDo: decide if there are any useful statistics from the SCB. */
+
+ return &lp->stats;
+}
+
+/* Initialize the Rx-block list. */
+static void
+init_rx_bufs(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ unsigned short *write_ptr;
+ unsigned short SCB_base = SCB_BASE;
+
+ int cur_rxbuf = lp->rx_head = RX_BUF_START;
+
+ /* Initialize each Rx frame + data buffer. */
+ do { /* While there is room for one more. */
+
+ write_ptr = (unsigned short *)(dev->mem_start + cur_rxbuf);
+
+ *write_ptr++ = 0x0000; /* Status */
+ *write_ptr++ = 0x0000; /* Command */
+ *write_ptr++ = cur_rxbuf + RX_BUF_SIZE; /* Link */
+ *write_ptr++ = cur_rxbuf + 22; /* Buffer offset */
+ *write_ptr++ = 0x0000; /* Pad for dest addr. */
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000; /* Pad for source addr. */
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000;
+ *write_ptr++ = 0x0000; /* Pad for protocol. */
+
+ *write_ptr++ = 0x0000; /* Buffer: Actual count */
+ *write_ptr++ = -1; /* Buffer: Next (none). */
+ *write_ptr++ = cur_rxbuf + 0x20 + SCB_base; /* Buffer: Address low */
+ *write_ptr++ = 0x0000;
+ /* Finally, the number of bytes in the buffer. */
+ *write_ptr++ = 0x8000 + RX_BUF_SIZE-0x20;
+
+ lp->rx_tail = cur_rxbuf;
+ cur_rxbuf += RX_BUF_SIZE;
+ } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+
+ /* Terminate the list by setting the EOL bit, and wrap the pointer to make
+ the list a ring. */
+ write_ptr = (unsigned short *)
+ (dev->mem_start + lp->rx_tail + 2);
+ *write_ptr++ = 0xC000; /* Command, mark as last. */
+ *write_ptr++ = lp->rx_head; /* Link */
+
+}
+
+void
+init_82586_mem(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ ushort *shmem = (short*)dev->mem_start;
+
+ /* Enable loopback to protect the wire while starting up,
+ and hold the 586 in reset during the memory initialization. */
+ outb(0x20, ioaddr + MISC_CTRL);
+
+ /* Fix the ISCP address and base. */
+ init_words[3] = SCB_BASE;
+ init_words[7] = SCB_BASE;
+
+ /* Write the words at 0xfff6 (address-aliased to 0xfffff6). */
+ memcpy((void*)dev->mem_end-10, init_words, 10);
+
+ /* Write the words at 0x0000. */
+ memcpy((char*)dev->mem_start, init_words + 5, sizeof(init_words) - 10);
+
+ /* Fill in the station address. */
+ memcpy((char*)dev->mem_start+SA_OFFSET, dev->dev_addr,
+ sizeof(dev->dev_addr));
+
+ /* The Tx-block list is written as needed. We just set up the values. */
+ lp->tx_cmd_link = IDLELOOP + 4;
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+
+ init_rx_bufs(dev);
+
+ /* Start the 586 by releasing the reset line, but leave loopback. */
+ outb(0xA0, ioaddr + MISC_CTRL);
+
+ /* This was time consuming to track down: you need to give two channel
+ attention signals to reliably start up the i82586. */
+ outb(0, ioaddr + SIGNAL_CA);
+
+ {
+ int boguscnt = 50;
+ while (shmem[iSCB_STATUS>>1] == 0)
+ if (--boguscnt == 0) {
+ printk("%s: i82586 initialization timed out with status %04x,"
+ "cmd %04x.\n", dev->name,
+ shmem[iSCB_STATUS>>1], shmem[iSCB_CMD>>1]);
+ break;
+ }
+ /* Issue channel-attn -- the 82586 won't start. */
+ outb(0, ioaddr + SIGNAL_CA);
+ }
+
+ /* Disable loopback and enable interrupts. */
+ outb(0x84, ioaddr + MISC_CTRL);
+ if (net_debug > 4)
+ printk("%s: Initialized 82586, status %04x.\n", dev->name,
+ shmem[iSCB_STATUS>>1]);
+ return;
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ ushort tx_block = lp->tx_head;
+ ushort *write_ptr = (ushort *)(dev->mem_start + tx_block);
+
+ /* Set the write pointer to the Tx block, and put out the header. */
+ *write_ptr++ = 0x0000; /* Tx status */
+ *write_ptr++ = CMD_INTR|CmdTx; /* Tx command */
+ *write_ptr++ = tx_block+16; /* Next command is a NoOp. */
+ *write_ptr++ = tx_block+8; /* Data Buffer offset. */
+
+ /* Output the data buffer descriptor. */
+ *write_ptr++ = length | 0x8000; /* Byte count parameter. */
+ *write_ptr++ = -1; /* No next data buffer. */
+ *write_ptr++ = tx_block+22+SCB_BASE;/* Buffer follows the NoOp command. */
+ *write_ptr++ = 0x0000; /* Buffer address high bits (always zero). */
+
+ /* Output the Loop-back NoOp command. */
+ *write_ptr++ = 0x0000; /* Tx status */
+ *write_ptr++ = CmdNOp; /* Tx command */
+ *write_ptr++ = tx_block+16; /* Next is myself. */
+
+ /* Output the packet at the write pointer. */
+ memcpy(write_ptr, buf, length);
+
+ /* Set the old command link pointing to this send packet. */
+ *(ushort*)(dev->mem_start + lp->tx_cmd_link) = tx_block;
+ lp->tx_cmd_link = tx_block + 20;
+
+ /* Set the next free tx region. */
+ lp->tx_head = tx_block + TX_BUF_SIZE;
+ if (lp->tx_head > RX_BUF_START - TX_BUF_SIZE)
+ lp->tx_head = TX_BUF_START;
+
+ if (net_debug > 4) {
+ printk("%s: 3c507 @%x send length = %d, tx_block %3x, next %3x.\n",
+ dev->name, ioaddr, length, tx_block, lp->tx_head);
+ }
+
+ if (lp->tx_head != lp->tx_reap)
+ dev->tbusy = 0;
+}
+
+static void
+el16_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short *shmem = (short*)dev->mem_start;
+ ushort rx_head = lp->rx_head;
+ ushort rx_tail = lp->rx_tail;
+ ushort boguscount = 10;
+ short frame_status;
+
+ while ((frame_status = shmem[rx_head>>1]) < 0) { /* Command complete */
+ ushort *read_frame = (short *)(dev->mem_start + rx_head);
+ ushort rfd_cmd = read_frame[1];
+ ushort next_rx_frame = read_frame[2];
+ ushort data_buffer_addr = read_frame[3];
+ ushort *data_frame = (short *)(dev->mem_start + data_buffer_addr);
+ ushort pkt_len = data_frame[0];
+
+ if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
+ || (pkt_len & 0xC000) != 0xC000) {
+ printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
+ "next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
+ frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
+ pkt_len);
+ } else if ((frame_status & 0x2000) == 0) {
+ /* Frame Rxed, but with error. */
+ lp->stats.rx_errors++;
+ if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
+ if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
+ if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
+ if (frame_status & 0x0100) lp->stats.rx_over_errors++;
+ if (frame_status & 0x0080) lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ pkt_len &= 0x3fff;
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb_reserve(skb,2);
+ skb->dev = dev;
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ memcpy(skb_put(skb,pkt_len), data_frame + 5, pkt_len);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+
+ /* Clear the status word and set End-of-List on the rx frame. */
+ read_frame[0] = 0;
+ read_frame[1] = 0xC000;
+ /* Clear the end-of-list on the prev. RFD. */
+ *(short*)(dev->mem_start + rx_tail + 2) = 0x0000;
+
+ rx_tail = rx_head;
+ rx_head = next_rx_frame;
+ if (--boguscount == 0)
+ break;
+ }
+
+ lp->rx_head = rx_head;
+ lp->rx_tail = rx_tail;
+}
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_3c507 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, el16_probe
+};
+
+static int io = 0x300;
+static int irq = 0;
+
+int init_module(void)
+{
+ if (io == 0)
+ printk("3c507: You should not use auto-probing with insmod!\n");
+ dev_3c507.base_addr = io;
+ dev_3c507.irq = irq;
+ if (register_netdev(&dev_3c507) != 0) {
+ printk("3c507: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_3c507);
+ kfree(dev_3c507.priv);
+ dev_3c507.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ free_irq(dev_3c507.irq);
+ release_region(dev_3c507.base_addr, EL16_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c 3c507.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c509.c b/i386/i386at/gpl/linux/net/3c509.c
new file mode 100644
index 00000000..5e7dce4f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c509.c
@@ -0,0 +1,739 @@
+/* 3c509.c: A 3c509 EtherLink3 ethernet driver for linux. */
+/*
+ Written 1993,1994 by Donald Becker.
+
+ Copyright 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This driver is for the 3Com EtherLinkIII series.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov or
+ C/O Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ Known limitations:
+ Because of the way 3c509 ISA detection works it's difficult to predict
+ a priori which of several ISA-mode cards will be detected first.
+
+ This driver does not use predictive interrupt mode, resulting in higher
+ packet latency but lower overhead. If interrupts are disabled for an
+ unusually long time it could also result in missed packets, but in
+ practice this rarely happens.
+*/
+
+static const char *version = "3c509.c:1.03 10/8/94 becker@cesdis.gsfc.nasa.gov\n";
+
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/config.h> /* for CONFIG_MCA */
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+
+#ifdef EL3_DEBUG
+int el3_debug = EL3_DEBUG;
+#else
+int el3_debug = 2;
+#endif
+
+/* To minimize the size of the driver source I only define operating
+ constants if they are used several times. You'll need the manual
+ if you want to understand driver details. */
+/* Offsets from base I/O address. */
+#define EL3_DATA 0x00
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+#define ID_PORT 0x100
+#define EEPROM_READ 0x80
+
+#define EL3_IO_EXTENT 16
+
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable. */
+enum c509cmd {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrMask = 14<<11,
+ SetReadZero = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Register window 1 offsets, the window used in normal operation. */
+#define TX_FIFO 0x00
+#define RX_FIFO 0x00
+#define RX_STATUS 0x08
+#define TX_STATUS 0x0B
+#define TX_FREE 0x0C /* Remaining free bytes in Tx buffer. */
+
+#define WN0_IRQ 0x08 /* Window 0: Set IRQ line in bits 12-15. */
+#define WN4_MEDIA 0x0A /* Window 4: Various transcvr/media bits. */
+#define MEDIA_TP 0x00C0 /* Enable link beat and jabber for 10baseT. */
+
+struct el3_private {
+ struct enet_statistics stats;
+};
+
+static ushort id_read_eeprom(int index);
+static ushort read_eeprom(short ioaddr, int index);
+static int el3_open(struct device *dev);
+static int el3_start_xmit(struct sk_buff *skb, struct device *dev);
+static void el3_interrupt(int irq, struct pt_regs *regs);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *el3_get_stats(struct device *dev);
+static int el3_rx(struct device *dev);
+static int el3_close(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+
+int el3_probe(struct device *dev)
+{
+ short lrs_state = 0xff, i;
+ ushort ioaddr, irq, if_port;
+ short *phys_addr = (short *)dev->dev_addr;
+ static int current_tag = 0;
+
+ /* First check all slots of the EISA bus. The next slot address to
+ probe is kept in 'eisa_addr' to support multiple probe() calls. */
+ if (EISA_bus) {
+ static int eisa_addr = 0x1000;
+ while (eisa_addr < 0x9000) {
+ ioaddr = eisa_addr;
+ eisa_addr += 0x1000;
+
+ /* Check the standard EISA ID register for an encoded '3Com'. */
+ if (inw(ioaddr + 0xC80) != 0x6d50)
+ continue;
+
+ /* Change the register set to the configuration window 0. */
+ outw(SelectWindow | 0, ioaddr + 0xC80 + EL3_CMD);
+
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
+ if_port = inw(ioaddr + 6)>>14;
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+
+ /* Restore the "Product ID" to the EEPROM read register. */
+ read_eeprom(ioaddr, 3);
+
+ /* Was the EISA code an add-on hack? Nahhhhh... */
+ goto found;
+ }
+ }
+
+#ifdef CONFIG_MCA
+ if (MCA_bus) {
+ mca_adaptor_select_mode(1);
+ for (i = 0; i < 8; i++)
+ if ((mca_adaptor_id(i) | 1) == 0x627c) {
+ ioaddr = mca_pos_base_addr(i);
+ irq = inw(ioaddr + WN0_IRQ) >> 12;
+ if_port = inw(ioaddr + 6)>>14;
+ for (i = 0; i < 3; i++)
+ phys_addr[i] = htons(read_eeprom(ioaddr, i));
+
+ mca_adaptor_select_mode(0);
+ goto found;
+ }
+ mca_adaptor_select_mode(0);
+
+ }
+#endif
+
+ /* Next check for all ISA bus boards by sending the ID sequence to the
+ ID_PORT. We find cards past the first by setting the 'current_tag'
+ on cards as they are found. Cards with their tag set will not
+ respond to subsequent ID sequences. */
+
+ if (check_region(ID_PORT,1)) {
+ static int once = 1;
+ if (once) printk("3c509: Somebody has reserved 0x%x, can't do ID_PORT lookup, nor card auto-probing\n",ID_PORT);
+ once = 0;
+ return -ENODEV;
+ }
+
+ outb(0x00, ID_PORT);
+ outb(0x00, ID_PORT);
+ for(i = 0; i < 255; i++) {
+ outb(lrs_state, ID_PORT);
+ lrs_state <<= 1;
+ lrs_state = lrs_state & 0x100 ? lrs_state ^ 0xcf : lrs_state;
+ }
+
+ /* For the first probe, clear all board's tag registers. */
+ if (current_tag == 0)
+ outb(0xd0, ID_PORT);
+ else /* Otherwise kill off already-found boards. */
+ outb(0xd8, ID_PORT);
+
+ if (id_read_eeprom(7) != 0x6d50) {
+ return -ENODEV;
+ }
+
+ /* Read in EEPROM data, which does contention-select.
+ Only the lowest address board will stay "on-line".
+ 3Com got the byte order backwards. */
+ for (i = 0; i < 3; i++) {
+ phys_addr[i] = htons(id_read_eeprom(i));
+ }
+
+ {
+ unsigned short iobase = id_read_eeprom(8);
+ if_port = iobase >> 14;
+ ioaddr = 0x200 + ((iobase & 0x1f) << 4);
+ }
+ irq = id_read_eeprom(9) >> 12;
+
+ if (dev->base_addr != 0
+ && dev->base_addr != (unsigned short)ioaddr) {
+ return -ENODEV;
+ }
+
+ /* Set the adaptor tag so that the next card can be found. */
+ outb(0xd0 + ++current_tag, ID_PORT);
+
+ /* Activate the adaptor at the EEPROM location. */
+ outb(0xff, ID_PORT);
+
+ EL3WINDOW(0);
+ if (inw(ioaddr) != 0x6d50)
+ return -ENODEV;
+
+ /* Free the interrupt so that some other card can use it. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+ found:
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->if_port = if_port;
+ request_region(dev->base_addr, EL3_IO_EXTENT, "3c509");
+
+ {
+ const char *if_names[] = {"10baseT", "AUI", "undefined", "BNC"};
+ printk("%s: 3c509 at %#3.3lx tag %d, %s port, address ",
+ dev->name, dev->base_addr, current_tag, if_names[dev->if_port]);
+ }
+
+ /* Read in the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i]);
+ printk(", IRQ %d.\n", dev->irq);
+
+ /* Make up a EL3-specific-data structure. */
+ dev->priv = kmalloc(sizeof(struct el3_private), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct el3_private));
+
+ if (el3_debug > 0)
+ printk(version);
+
+ /* The EL3-specific entries in the device structure. */
+ dev->open = &el3_open;
+ dev->hard_start_xmit = &el3_start_xmit;
+ dev->stop = &el3_close;
+ dev->get_stats = &el3_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the generic fields of the device structure. */
+ ether_setup(dev);
+ return 0;
+}
+
+/* Read a word from the EEPROM using the regular EEPROM access register.
+ Assume that we are in register window zero.
+ */
+static ushort read_eeprom(short ioaddr, int index)
+{
+ int timer;
+
+ outw(EEPROM_READ + index, ioaddr + 10);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 0; timer < 162*4 + 400; timer++)
+ SLOW_DOWN_IO;
+ return inw(ioaddr + 12);
+}
+
+/* Read a word from the EEPROM when in the ISA ID probe state. */
+static ushort id_read_eeprom(int index)
+{
+ int timer, bit, word = 0;
+
+ /* Issue read command, and pause for at least 162 us. for it to complete.
+ Assume extra-fast 16Mhz bus. */
+ outb(EEPROM_READ + index, ID_PORT);
+
+ /* This should really be done by looking at one of the timer channels. */
+ for (timer = 0; timer < 162*4 + 400; timer++)
+ SLOW_DOWN_IO;
+
+ for (bit = 15; bit >= 0; bit--)
+ word = (word << 1) + (inb(ID_PORT) & 0x01);
+
+ if (el3_debug > 3)
+ printk(" 3c509 EEPROM word %d %#4.4x.\n", index, word);
+
+ return word;
+}
+
+
+
+static int
+el3_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ int i;
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(RxReset, ioaddr + EL3_CMD);
+ outw(SetReadZero | 0x00, ioaddr + EL3_CMD);
+
+ if (request_irq(dev->irq, &el3_interrupt, 0, "3c509")) {
+ return -EAGAIN;
+ }
+
+ EL3WINDOW(0);
+ if (el3_debug > 3)
+ printk("%s: Opening, IRQ %d status@%x %4.4x.\n", dev->name,
+ dev->irq, ioaddr + EL3_STATUS, inw(ioaddr + EL3_STATUS));
+
+ /* Activate board: this is probably unnecessary. */
+ outw(0x0001, ioaddr + 4);
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Set the IRQ line. */
+ outw((dev->irq << 12) | 0x0f00, ioaddr + WN0_IRQ);
+
+ /* Set the station address in window 2 each time opened. */
+ EL3WINDOW(2);
+
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+
+ if (dev->if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ outw(StartCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* 10baseT interface, enabled link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) | MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 9; i++)
+ inb(ioaddr + i);
+ inb(ioaddr + 10);
+ inb(ioaddr + 12);
+
+ /* Switch to register set 1 for normal use. */
+ EL3WINDOW(1);
+
+ /* Accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetReadZero | 0xff, ioaddr + EL3_CMD);
+ outw(AckIntr | 0x69, ioaddr + EL3_CMD); /* Ack IRQ */
+ outw(SetIntrMask | 0x98, ioaddr + EL3_CMD); /* Set interrupt mask. */
+
+ if (el3_debug > 3)
+ printk("%s: Opened 3c509 IRQ %d status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + EL3_STATUS));
+
+ MOD_INC_USE_COUNT;
+ return 0; /* Always succeed */
+}
+
+static int
+el3_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TX_STATUS), inw(ioaddr + EL3_STATUS));
+ dev->trans_start = jiffies;
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+
+ if (el3_debug > 4) {
+ printk("%s: el3_start_xmit(length = %ld) called, status %4.4x.\n",
+ dev->name, skb->len, inw(ioaddr + EL3_STATUS));
+ }
+#ifndef final_version
+ { /* Error-checking code, delete for 1.30. */
+ ushort status = inw(ioaddr + EL3_STATUS);
+ if (status & 0x0001 /* IRQ line active, missed one. */
+ && inw(ioaddr + EL3_STATUS) & 1) { /* Make sure. */
+ printk("%s: Missed interrupt, status then %04x now %04x"
+ " Tx %2.2x Rx %4.4x.\n", dev->name, status,
+ inw(ioaddr + EL3_STATUS), inb(ioaddr + TX_STATUS),
+ inw(ioaddr + RX_STATUS));
+ /* Fake interrupt trigger by masking, acknowledge interrupts. */
+ outw(SetReadZero | 0x00, ioaddr + EL3_CMD);
+ outw(AckIntr | 0x69, ioaddr + EL3_CMD); /* Ack IRQ */
+ outw(SetReadZero | 0xff, ioaddr + EL3_CMD);
+ }
+ }
+#endif
+
+ /* Avoid timer-based retransmission conflicts. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ /* Put out the doubleword header... */
+ outw(skb->len, ioaddr + TX_FIFO);
+ outw(0x00, ioaddr + TX_FIFO);
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+
+ dev->trans_start = jiffies;
+ if (inw(ioaddr + TX_FREE) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TX_STATUS)) > 0) {
+ if (tx_status & 0x38) lp->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) outw(TxReset, ioaddr + EL3_CMD);
+ if (tx_status & 0x3C) outw(TxEnable, ioaddr + EL3_CMD);
+ outb(0x00, ioaddr + TX_STATUS); /* Pop the status stack. */
+ }
+ }
+ return 0;
+}
+
+/* The EL3 interrupt handler. */
+static void
+el3_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int ioaddr, status;
+ int i = 0;
+
+ if (dev == NULL) {
+ printk ("el3_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ status = inw(ioaddr + EL3_STATUS);
+
+ if (el3_debug > 4)
+ printk("%s: interrupt, status %4.4x.\n", dev->name, status);
+
+ while ((status = inw(ioaddr + EL3_STATUS)) & 0x91) {
+
+ if (status & 0x10)
+ el3_rx(dev);
+
+ if (status & 0x08) {
+ if (el3_debug > 5)
+ printk(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | 0x08, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ if (status & 0x80) /* Statistics full. */
+ update_stats(ioaddr, dev);
+
+ if (++i > 10) {
+ printk("%s: Infinite loop in interrupt, status %4.4x.\n",
+ dev->name, status);
+ /* Clear all interrupts. */
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | 0x41, ioaddr + EL3_CMD); /* Ack IRQ */
+
+ }
+
+ if (el3_debug > 4) {
+ printk("%s: exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + EL3_STATUS));
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+
+static struct enet_statistics *
+el3_get_stats(struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ update_stats(dev->base_addr, dev);
+ restore_flags(flags);
+ return &lp->stats;
+}
+
+/* Update statistics. We change to register window 6, so this should be run
+ single-threaded if the device is active. This is expected to be a rare
+ operation, and it's simpler for the rest of the driver to assume that
+ window 1 is always valid rather than use a special window-state variable.
+ */
+static void update_stats(int ioaddr, struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+
+ if (el3_debug > 5)
+ printk(" Updating the statistics.\n");
+ /* Turn off statistics updates while reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ lp->stats.tx_carrier_errors += inb(ioaddr + 0);
+ lp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ lp->stats.collisions += inb(ioaddr + 3);
+ lp->stats.tx_window_errors += inb(ioaddr + 4);
+ lp->stats.rx_fifo_errors += inb(ioaddr + 5);
+ lp->stats.tx_packets += inb(ioaddr + 6);
+ /* Rx packets */ inb(ioaddr + 7);
+ /* Tx deferrals */ inb(ioaddr + 8);
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+
+ /* Back to window 1, and turn statistics back on. */
+ EL3WINDOW(1);
+ outw(StatsEnable, ioaddr + EL3_CMD);
+ return;
+}
+
+static int
+el3_rx(struct device *dev)
+{
+ struct el3_private *lp = (struct el3_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short rx_status;
+
+ if (el3_debug > 5)
+ printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RX_STATUS));
+ while ((rx_status = inw(ioaddr + RX_STATUS)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ short error = rx_status & 0x3800;
+ lp->stats.rx_errors++;
+ switch (error) {
+ case 0x0000: lp->stats.rx_over_errors++; break;
+ case 0x0800: lp->stats.rx_length_errors++; break;
+ case 0x1000: lp->stats.rx_frame_errors++; break;
+ case 0x1800: lp->stats.rx_length_errors++; break;
+ case 0x2000: lp->stats.rx_frame_errors++; break;
+ case 0x2800: lp->stats.rx_crc_errors++; break;
+ }
+ } else {
+ short pkt_len = rx_status & 0x7ff;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+5);
+ if (el3_debug > 4)
+ printk("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align IP on 16 byte boundaries */
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ insl(ioaddr+RX_FIFO, skb_put(skb,pkt_len),
+ (pkt_len + 3) >> 2);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ lp->stats.rx_packets++;
+ continue;
+ } else if (el3_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ }
+ lp->stats.rx_dropped++;
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ while (inw(ioaddr + EL3_STATUS) & 0x1000)
+ printk(" Waiting for 3c509 to discard packet, status %x.\n",
+ inw(ioaddr + EL3_STATUS) );
+ }
+
+ return 0;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ if (el3_debug > 1) {
+ static int old = 0;
+ if (old != dev->mc_count) {
+ old = dev->mc_count;
+ printk("%s: Setting Rx mode to %d addresses.\n", dev->name, dev->mc_count);
+ }
+ }
+ if (dev->flags&IFF_PROMISC)
+ {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ }
+ else if (dev->mc_count || (dev->flags&IFF_ALLMULTI))
+ {
+ outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ }
+ else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+static int
+el3_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (el3_debug > 2)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 3)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* Disable link beat and jabber, if_port may change ere next open(). */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + WN4_MEDIA) & ~MEDIA_TP, ioaddr + WN4_MEDIA);
+ }
+
+ free_irq(dev->irq);
+ /* Switching back to window 0 disables the IRQ. */
+ EL3WINDOW(0);
+ /* But we explicitly zero the IRQ line select anyway. */
+ outw(0x0f00, ioaddr + WN0_IRQ);
+
+
+ irq2dev_map[dev->irq] = 0;
+
+ update_stats(ioaddr, dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_3c509 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, el3_probe };
+
+static int io = 0;
+static int irq = 0;
+
+int
+init_module(void)
+{
+ dev_3c509.base_addr = io;
+ dev_3c509.irq = irq;
+ if (!EISA_bus) {
+ printk("3c509: WARNING! Module load-time probing works reliably only for EISA-bus!\n");
+ }
+ if (register_netdev(&dev_3c509) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_3c509);
+ kfree_s(dev_3c509.priv,sizeof(struct el3_private));
+ dev_3c509.priv=NULL;
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_3c509.base_addr, EL3_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c509.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/3c59x.c b/i386/i386at/gpl/linux/net/3c59x.c
new file mode 100644
index 00000000..b5c4d5b7
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/3c59x.c
@@ -0,0 +1,1066 @@
+/* 3c59x.c: A 3Com 3c590/3c595 "Vortex" ethernet driver for linux. */
+/*
+ Written 1995 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the 3Com "Vortex" series ethercards. Members of
+ the series include the 3c590 PCI EtherLink III and 3c595-Tx PCI Fast
+ EtherLink. It also works with the 10Mbs-only 3c590 PCI EtherLink III.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static char *version = "3c59x.c:v0.13 2/13/96 becker@cesdis.gsfc.nasa.gov\n";
+
+/* "Knobs" that turn on special features. */
+/* Allow the use of bus master transfers instead of programmed-I/O for the
+ Tx process. Bus master transfers are always disabled by default, but
+ iff this is set they may be turned on using 'options'. */
+#define VORTEX_BUS_MASTER
+
+/* Put out somewhat more debugging messages. (0 - no msg, 1 minimal msgs). */
+#define VORTEX_DEBUG 1
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/in.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <linux/timer.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef HAVE_SHARED_IRQ
+#define USE_SHARED_IRQ
+#include <linux/shared_irq.h>
+#endif
+
+/* The total size is twice that of the original EtherLinkIII series: the
+ runtime register window, window 1, is now always mapped in. */
+#define VORTEX_TOTAL_SIZE 0x20
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tc59x_drv =
+{"Vortex", vortex_pci_probe, VORTEX_TOTAL_SIZE, NULL};
+#endif
+
+#ifdef VORTEX_DEBUG
+int vortex_debug = VORTEX_DEBUG;
+#else
+int vortex_debug = 1;
+#endif
+
+static int product_ids[] = {0x5900, 0x5950, 0x5951, 0x5952, 0, 0};
+static const char *product_names[] = {
+ "3c590 Vortex 10Mbps",
+ "3c595 Vortex 100baseTX",
+ "3c595 Vortex 100baseT4",
+ "3c595 Vortex 100base-MII",
+ "EISA Vortex 3c597",
+};
+#define DEMON_INDEX 5 /* Caution! Must be consistent with above! */
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the 3Com FastEtherLink, 3Com's PCI to
+10/100baseT adapter. It also works with the 3c590, a similar product
+with only a 10Mbs interface.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+physically possible to shared PCI interrupt lines, the 1.2.0 kernel doesn't
+support it.
+
+III. Driver operation
+
+The 3c59x series use an interface that's very similar to the previous 3c5x9
+series. The primary interface is two programmed-I/O FIFOs, with an
+alternate single-contiguous-region bus-master transfer (see next).
+
+One extension that is advertised in a very large font is that the adapters
+are capable of being bus masters. Unfortunately this capability is only for
+a single contiguous region making it less useful than the list of transfer
+regions available with the DEC Tulip or AMD PCnet. Given the significant
+performance impact of taking an extra interrupt for each transfer, using
+DMA transfers is a win only with large blocks.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+IV. Notes
+
+Thanks to Cameron Spitzer and Terry Murphy of 3Com for providing both
+3c590 and 3c595 boards.
+The name "Vortex" is the internal 3Com project name for the PCI ASIC, and
+the not-yet-released (3/95) EISA version is called "Demon". According to
+Terry these names come from rides at the local amusement park.
+
+The new chips support both ethernet (1.5K) and FDDI (4.5K) packet sizes!
+This driver only supports ethernet packets because of the skbuff allocation
+limit of 4K.
+*/
+
+#define TCOM_VENDOR_ID 0x10B7 /* 3Com's manufacturer's ID. */
+
+/* Operational defintions.
+ These are not used by other compilation units and thus are not
+ exported in a ".h" file.
+
+ First the windows. There are eight register windows, with the command
+ and status registers available in each.
+ */
+#define EL3WINDOW(win_num) outw(SelectWindow + (win_num), ioaddr + EL3_CMD)
+#define EL3_CMD 0x0e
+#define EL3_STATUS 0x0e
+
+/* The top five bits written to EL3_CMD are a command, the lower
+ 11 bits are the parameter, if applicable.
+ Note that 11 parameters bits was fine for ethernet, but the new chip
+ can handle FDDI lenght frames (~4500 octets) and now parameters count
+ 32-bit 'Dwords' rather than octets. */
+
+enum vortex_cmd {
+ TotalReset = 0<<11, SelectWindow = 1<<11, StartCoax = 2<<11,
+ RxDisable = 3<<11, RxEnable = 4<<11, RxReset = 5<<11, RxDiscard = 8<<11,
+ TxEnable = 9<<11, TxDisable = 10<<11, TxReset = 11<<11,
+ FakeIntr = 12<<11, AckIntr = 13<<11, SetIntrEnb = 14<<11,
+ SetStatusEnb = 15<<11, SetRxFilter = 16<<11, SetRxThreshold = 17<<11,
+ SetTxThreshold = 18<<11, SetTxStart = 19<<11,
+ StartDMAUp = 20<<11, StartDMADown = (20<<11)+1, StatsEnable = 21<<11,
+ StatsDisable = 22<<11, StopCoax = 23<<11,};
+
+/* The SetRxFilter command accepts the following classes: */
+enum RxFilter {
+ RxStation = 1, RxMulticast = 2, RxBroadcast = 4, RxProm = 8 };
+
+/* Bits in the general status register. */
+enum vortex_status {
+ IntLatch = 0x0001, AdapterFailure = 0x0002, TxComplete = 0x0004,
+ TxAvailable = 0x0008, RxComplete = 0x0010, RxEarly = 0x0020,
+ IntReq = 0x0040, StatsFull = 0x0080, DMADone = 1<<8,
+ DMAInProgress = 1<<11, /* DMA controller is still busy.*/
+ CmdInProgress = 1<<12, /* EL3_CMD is still busy.*/
+};
+
+/* Register window 1 offsets, the window used in normal operation.
+ On the Vortex this window is always mapped at offsets 0x10-0x1f. */
+enum Window1 {
+ TX_FIFO = 0x10, RX_FIFO = 0x10, RxErrors = 0x14,
+ RxStatus = 0x18, Timer=0x1A, TxStatus = 0x1B,
+ TxFree = 0x1C, /* Remaining free bytes in Tx buffer. */
+};
+enum Window0 {
+ Wn0EepromCmd = 10, /* Window 0: EEPROM command register. */
+};
+enum Win0_EEPROM_bits {
+ EEPROM_Read = 0x80, EEPROM_WRITE = 0x40, EEPROM_ERASE = 0xC0,
+ EEPROM_EWENB = 0x30, /* Enable erasing/writing for 10 msec. */
+ EEPROM_EWDIS = 0x00, /* Disable EWENB before 10 msec timeout. */
+};
+/* EEPROM locations. */
+enum eeprom_offset {
+ PhysAddr01=0, PhysAddr23=1, PhysAddr45=2, ModelID=3,
+ EtherLink3ID=7, IFXcvrIO=8, IRQLine=9,
+ NodeAddr01=10, NodeAddr23=11, NodeAddr45=12,
+ DriverTune=13, Checksum=15};
+
+enum Window3 { /* Window 3: MAC/config bits. */
+ Wn3_Config=0, Wn3_MAC_Ctrl=6, Wn3_Options=8,
+};
+union wn3_config {
+ int i;
+ struct w3_config_fields {
+ unsigned int ram_size:3, ram_width:1, ram_speed:2, rom_size:2;
+ int pad8:8;
+ unsigned int ram_split:2, pad18:2, xcvr:3, pad21:1, autoselect:1;
+ int pad24:8;
+ } u;
+};
+
+enum Window4 {
+ Wn4_Media = 0x0A, /* Window 4: Various transcvr/media bits. */
+};
+enum Win4_Media_bits {
+ Media_TP = 0x00C0, /* Enable link beat and jabber for 10baseT. */
+};
+enum Window7 { /* Window 7: Bus Master control. */
+ Wn7_MasterAddr = 0, Wn7_MasterLen = 6, Wn7_MasterStatus = 12,
+};
+
+struct vortex_private {
+ char devname[8]; /* "ethN" string, also for kernel debug. */
+ const char *product_name;
+ struct device *next_module;
+ struct enet_statistics stats;
+#ifdef VORTEX_BUS_MASTER
+ struct sk_buff *tx_skb; /* Packet being eaten by bus master ctrl. */
+#endif
+ struct timer_list timer; /* Media selection timer. */
+ int options; /* User-settable driver options (none yet). */
+ unsigned int media_override:3, full_duplex:1, bus_master:1, autoselect:1;
+};
+
+static char *if_names[] = {
+ "10baseT", "10Mbs AUI", "undefined", "10base2",
+ "100baseTX", "100baseFX", "MII", "undefined"};
+
+static int vortex_scan(struct device *dev);
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+ int product_index, int options);
+static int vortex_probe1(struct device *dev);
+static int vortex_open(struct device *dev);
+static void vortex_timer(unsigned long arg);
+static int vortex_start_xmit(struct sk_buff *skb, struct device *dev);
+static int vortex_rx(struct device *dev);
+static void vortex_interrupt(int irq, struct pt_regs *regs);
+static int vortex_close(struct device *dev);
+static void update_stats(int addr, struct device *dev);
+static struct enet_statistics *vortex_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Unlike the other PCI cards the 59x cards don't need a large contiguous
+ memory region, so making the driver a loadable module is feasible.
+
+ Unfortuneately maximizing the shared code between the integrated and
+ module version of the driver results in a complicated set of initialization
+ procedures.
+ init_module() -- modules / tc59x_init() -- built-in
+ The wrappers for vortex_scan()
+ vortex_scan() The common routine that scans for PCI and EISA cards
+ vortex_found_device() Allocate a device structure when we find a card.
+ Different versions exist for modules and built-in.
+ vortex_probe1() Fill in the device structure -- this is seperated
+ so that the modules code can put it in dev->init.
+*/
+/* This driver uses 'options' to pass the media type, full-duplex flag, etc. */
+/* Note: this is the only limit on the number of cards supported!! */
+int options[8] = { -1, -1, -1, -1, -1, -1, -1, -1,};
+
+#ifdef MODULE
+static int debug = -1;
+/* A list of all installed Vortex devices, for removing the driver module. */
+static struct device *root_vortex_dev = NULL;
+
+int
+init_module(void)
+{
+ int cards_found;
+
+ if (debug >= 0)
+ vortex_debug = debug;
+ if (vortex_debug)
+ printk(version);
+
+ root_vortex_dev = NULL;
+ cards_found = vortex_scan(0);
+ return cards_found < 0 ? cards_found : 0;
+}
+
+#else
+unsigned long tc59x_probe(struct device *dev)
+{
+ int cards_found = 0;
+
+ cards_found = vortex_scan(dev);
+
+ if (vortex_debug > 0 && cards_found)
+ printk(version);
+
+ return cards_found ? 0 : -ENODEV;
+}
+#endif /* not MODULE */
+
+static int vortex_scan(struct device *dev)
+{
+ int cards_found = 0;
+
+ if (pcibios_present()) {
+ static int pci_index = 0;
+ for (; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line, pci_latency;
+ unsigned int pci_ioaddr;
+ unsigned short pci_command;
+ int index;
+
+ for (index = 0; product_ids[index]; index++) {
+ if ( ! pcibios_find_device(TCOM_VENDOR_ID, product_ids[index],
+ pci_index, &pci_bus,
+ &pci_device_fn))
+ break;
+ }
+ if ( ! product_ids[index])
+ break;
+
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+
+#ifdef VORTEX_BUS_MASTER
+ /* Get and check the bus-master and latency values.
+ Some PCI BIOSes fail to set the master-enable bit, and
+ the latency timer must be set to the maximum value to avoid
+ data corruption that occurs when the timer expires during
+ a transfer. Yes, it's a bug. */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk(" PCI Master Bit has not been set! Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, &pci_latency);
+ if (pci_latency != 255) {
+ printk(" Overriding PCI latency timer (CFLT) setting of %d, new value is 255.\n", pci_latency);
+ pcibios_write_config_byte(pci_bus, pci_device_fn,
+ PCI_LATENCY_TIMER, 255);
+ }
+#endif /* VORTEX_BUS_MASTER */
+ vortex_found_device(dev, pci_ioaddr, pci_irq_line, index,
+ dev && dev->mem_start ? dev->mem_start
+ : options[cards_found]);
+ dev = 0;
+ cards_found++;
+ }
+ }
+
+ /* Now check all slots of the EISA bus. */
+ if (EISA_bus) {
+ static int ioaddr = 0x1000;
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ /* Check the standard EISA ID register for an encoded '3Com'. */
+ if (inw(ioaddr + 0xC80) != 0x6d50)
+ continue;
+ /* Check for a product that we support. */
+ if ((inw(ioaddr + 0xC82) & 0xFFF0) != 0x5970
+ && (inw(ioaddr + 0xC82) & 0xFFF0) != 0x5920)
+ continue;
+ vortex_found_device(dev, ioaddr, inw(ioaddr + 0xC88) >> 12,
+ DEMON_INDEX, dev && dev->mem_start
+ ? dev->mem_start : options[cards_found]);
+ dev = 0;
+ cards_found++;
+ }
+ }
+
+ return cards_found;
+}
+
+static int vortex_found_device(struct device *dev, int ioaddr, int irq,
+ int product_index, int options)
+{
+ struct vortex_private *vp;
+
+#ifdef MODULE
+ /* Allocate and fill new device structure. */
+ int dev_size = sizeof(struct device) +
+ sizeof(struct vortex_private);
+
+ dev = (struct device *) kmalloc(dev_size, GFP_KERNEL);
+ memset(dev, 0, dev_size);
+ dev->priv = ((void *)dev) + sizeof(struct device);
+ vp = (struct vortex_private *)dev->priv;
+ dev->name = vp->devname; /* An empty string. */
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ dev->init = vortex_probe1;
+ vp->product_name = product_names[product_index];
+ vp->options = options;
+ if (options >= 0) {
+ vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
+ vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->bus_master = (options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+ ether_setup(dev);
+ vp->next_module = root_vortex_dev;
+ root_vortex_dev = dev;
+ if (register_netdev(dev) != 0)
+ return -EIO;
+#else /* not a MODULE */
+ if (dev) {
+ dev->priv = kmalloc(sizeof (struct vortex_private), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof (struct vortex_private));
+ }
+ dev = init_etherdev(dev, sizeof(struct vortex_private));
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ vp = (struct vortex_private *)dev->priv;
+ vp->product_name = product_names[product_index];
+ vp->options = options;
+ if (options >= 0) {
+ vp->media_override = ((options & 7) == 2) ? 0 : options & 7;
+ vp->full_duplex = (options & 8) ? 1 : 0;
+ vp->bus_master = (options & 16) ? 1 : 0;
+ } else {
+ vp->media_override = 7;
+ vp->full_duplex = 0;
+ vp->bus_master = 0;
+ }
+
+ vortex_probe1(dev);
+#endif /* MODULE */
+ return 0;
+}
+
+static int vortex_probe1(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int i;
+
+ printk("%s: 3Com %s at %#3x,", dev->name,
+ vp->product_name, ioaddr);
+
+ /* Read the station address from the EEPROM. */
+ EL3WINDOW(0);
+ for (i = 0; i < 3; i++) {
+ short *phys_addr = (short *)dev->dev_addr;
+ int timer;
+ outw(EEPROM_Read + PhysAddr01 + i, ioaddr + Wn0EepromCmd);
+ /* Pause for at least 162 us. for the read to take place. */
+ for (timer = 0; timer < 162*4 + 400; timer++) {
+ SLOW_DOWN_IO;
+ if ((inw(ioaddr + Wn0EepromCmd) & 0x8000) == 0)
+ break;
+ }
+ phys_addr[i] = htons(inw(ioaddr + 12));
+ }
+ for (i = 0; i < 6; i++)
+ printk("%c%2.2x", i ? ':' : ' ', dev->dev_addr[i]);
+ printk(", IRQ %d\n", dev->irq);
+ /* Tell them about an invalid IRQ. */
+ if (vortex_debug && (dev->irq <= 0 || dev->irq > 15))
+ printk(" *** Warning: this IRQ is unlikely to work!\n");
+
+ {
+ char *ram_split[] = {"5:3", "3:1", "1:1", "invalid"};
+ union wn3_config config;
+ EL3WINDOW(3);
+ config.i = inl(ioaddr + Wn3_Config);
+ if (vortex_debug > 1)
+ printk(" Internal config register is %4.4x, transceivers %#x.\n",
+ config.i, inw(ioaddr + Wn3_Options));
+ printk(" %dK %s-wide RAM %s Rx:Tx split, %s%s interface.\n",
+ 8 << config.u.ram_size,
+ config.u.ram_width ? "word" : "byte",
+ ram_split[config.u.ram_split],
+ config.u.autoselect ? "autoselect/" : "",
+ if_names[config.u.xcvr]);
+ dev->if_port = config.u.xcvr;
+ vp->autoselect = config.u.autoselect;
+ }
+
+ /* We do a request_region() only to register /proc/ioports info. */
+ request_region(ioaddr, VORTEX_TOTAL_SIZE, vp->product_name);
+
+ /* The 3c59x-specific entries in the device structure. */
+ dev->open = &vortex_open;
+ dev->hard_start_xmit = &vortex_start_xmit;
+ dev->stop = &vortex_close;
+ dev->get_stats = &vortex_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+#if defined (HAVE_SET_MAC_ADDR) && 0
+ dev->set_mac_address = &set_mac_address;
+#endif
+
+ return 0;
+}
+
+
+static int
+vortex_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ union wn3_config config;
+ int i;
+
+ /* Before initializing select the active media port. */
+ EL3WINDOW(3);
+ if (vp->full_duplex)
+ outb(0x20, ioaddr + Wn3_MAC_Ctrl); /* Set the full-duplex bit. */
+ config.i = inl(ioaddr + Wn3_Config);
+
+ if (vp->media_override != 7) {
+ if (vortex_debug > 1)
+ printk("%s: Media override to transceiver %d (%s).\n",
+ dev->name, vp->media_override, if_names[vp->media_override]);
+ config.u.xcvr = vp->media_override;
+ dev->if_port = vp->media_override;
+ outl(config.i, ioaddr + Wn3_Config);
+ }
+
+ if (vortex_debug > 1) {
+ printk("%s: vortex_open() InternalConfig %8.8x.\n",
+ dev->name, config.i);
+ }
+
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (i = 20; i >= 0 ; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Wait a few ticks for the RxReset command to complete. */
+ for (i = 20; i >= 0 ; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+
+ outw(SetStatusEnb | 0x00, ioaddr + EL3_CMD);
+
+#ifdef USE_SHARED_IRQ
+ i = request_shared_irq(dev->irq, &vortex_interrupt, dev, vp->product_name);
+ if (i) /* Error */
+ return i;
+#else
+ if (dev->irq == 0 || irq2dev_map[dev->irq] != NULL)
+ return -EAGAIN;
+ irq2dev_map[dev->irq] = dev;
+ if (request_irq(dev->irq, &vortex_interrupt, 0, vp->product_name)) {
+ irq2dev_map[dev->irq] = NULL;
+ return -EAGAIN;
+ }
+#endif
+
+ if (vortex_debug > 1) {
+ EL3WINDOW(4);
+ printk("%s: vortex_open() irq %d media status %4.4x.\n",
+ dev->name, dev->irq, inw(ioaddr + Wn4_Media));
+ }
+
+ /* Set the station address and mask in window 2 each time opened. */
+ EL3WINDOW(2);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + i);
+ for (; i < 12; i+=2)
+ outw(0, ioaddr + i);
+
+ if (dev->if_port == 3)
+ /* Start the thinnet transceiver. We should really wait 50ms...*/
+ outw(StartCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* 10baseT interface, enabled link beat and jabber check. */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + Wn4_Media) | Media_TP, ioaddr + Wn4_Media);
+ }
+
+ /* Switch to the stats window, and clear all stats by reading. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+ EL3WINDOW(6);
+ for (i = 0; i < 10; i++)
+ inb(ioaddr + i);
+ inw(ioaddr + 10);
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+
+ /* Switch to register set 7 for normal use. */
+ EL3WINDOW(7);
+
+ /* Accept b-case and phys addr only. */
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+ outw(StatsEnable, ioaddr + EL3_CMD); /* Turn on statistics. */
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ outw(RxEnable, ioaddr + EL3_CMD); /* Enable the receiver. */
+ outw(TxEnable, ioaddr + EL3_CMD); /* Enable transmitter. */
+ /* Allow status bits to be seen. */
+ outw(SetStatusEnb | 0xff, ioaddr + EL3_CMD);
+ /* Ack all pending events, and set active indicator mask. */
+ outw(AckIntr | IntLatch | TxAvailable | RxEarly | IntReq,
+ ioaddr + EL3_CMD);
+ outw(SetIntrEnb | IntLatch | TxAvailable | RxComplete | StatsFull
+ | DMADone, ioaddr + EL3_CMD);
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ if (vp->autoselect) {
+ init_timer(&vp->timer);
+ vp->timer.expires = (14*HZ)/10; /* 1.4 sec. */
+ vp->timer.data = (unsigned long)dev;
+ vp->timer.function = &vortex_timer; /* timer handler */
+ add_timer(&vp->timer);
+ }
+ return 0;
+}
+
+static void vortex_timer(unsigned long data)
+{
+ struct device *dev = (struct device *)data;
+ if (vortex_debug > 2)
+ printk("%s: Media selection timer tick happened.\n", dev->name);
+ /* ToDo: active media selection here! */
+}
+
+static int
+vortex_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 40)
+ return 1;
+ printk("%s: transmit timed out, tx_status %2.2x status %4.4x.\n",
+ dev->name, inb(ioaddr + TxStatus), inw(ioaddr + EL3_STATUS));
+ vp->stats.tx_errors++;
+ /* Issue TX_RESET and TX_START commands. */
+ outw(TxReset, ioaddr + EL3_CMD);
+ {
+ int i;
+ for (i = 20; i >= 0 ; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ }
+ outw(TxEnable, ioaddr + EL3_CMD);
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ return 0;
+ }
+
+ if (skb == NULL || skb->len <= 0) {
+ printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ If this ever occurs the queue layer is doing something evil! */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ /* Put out the doubleword header... */
+ outl(skb->len, ioaddr + TX_FIFO);
+#ifdef VORTEX_BUS_MASTER
+ if (vp->bus_master) {
+ /* Set the bus-master controller to transfer the packet. */
+ outl((int)(skb->data), ioaddr + Wn7_MasterAddr);
+ outw((skb->len + 3) & ~3, ioaddr + Wn7_MasterLen);
+ vp->tx_skb = skb;
+ outw(StartDMADown, ioaddr + EL3_CMD);
+ } else {
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_kfree_skb (skb, FREE_WRITE);
+ if (inw(ioaddr + TxFree) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+ }
+#else
+ /* ... and the packet rounded to a doubleword. */
+ outsl(ioaddr + TX_FIFO, skb->data, (skb->len + 3) >> 2);
+ dev_kfree_skb (skb, FREE_WRITE);
+ if (inw(ioaddr + TxFree) > 1536) {
+ dev->tbusy = 0;
+ } else
+ /* Interrupt us when the FIFO has room for max-sized packet. */
+ outw(SetTxThreshold + 1536, ioaddr + EL3_CMD);
+#endif /* bus master */
+
+ dev->trans_start = jiffies;
+
+ /* Clear the Tx status stack. */
+ {
+ short tx_status;
+ int i = 4;
+
+ while (--i > 0 && (tx_status = inb(ioaddr + TxStatus)) > 0) {
+ if (tx_status & 0x3C) { /* A Tx-disabling error occured. */
+ if (vortex_debug > 2)
+ printk("%s: Tx error, status %2.2x.\n",
+ dev->name, tx_status);
+ if (tx_status & 0x04) vp->stats.tx_fifo_errors++;
+ if (tx_status & 0x38) vp->stats.tx_aborted_errors++;
+ if (tx_status & 0x30) {
+ int j;
+ outw(TxReset, ioaddr + EL3_CMD);
+ for (j = 20; j >= 0 ; j--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ }
+ outw(TxEnable, ioaddr + EL3_CMD);
+ }
+ outb(0x00, ioaddr + TxStatus); /* Pop the status stack. */
+ }
+ }
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void vortex_interrupt(int irq, struct pt_regs *regs)
+{
+#ifdef USE_SHARED_IRQ
+ struct device *dev = (struct device *)(irq == 0 ? regs : irq2dev_map[irq]);
+#else
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+#endif
+ struct vortex_private *lp;
+ int ioaddr, status;
+ int latency;
+ int i = 0;
+
+ if (dev == NULL) {
+ printk ("vortex_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ latency = inb(ioaddr + Timer);
+ lp = (struct vortex_private *)dev->priv;
+
+ status = inw(ioaddr + EL3_STATUS);
+
+ if (vortex_debug > 4)
+ printk("%s: interrupt, status %4.4x, timer %d.\n", dev->name,
+ status, latency);
+ if ((status & 0xE000) != 0xE000) {
+ static int donedidthis=0;
+ /* Some interrupt controllers store a bogus interrupt from boot-time.
+ Ignore a single early interrupt, but don't hang the machine for
+ other interrupt problems. */
+ if (donedidthis++ > 1) {
+ printk("%s: Bogus interrupt, bailing. Status %4.4x, start=%d.\n",
+ dev->name, status, dev->start);
+ free_irq(dev->irq);
+ }
+ }
+
+ do {
+ if (vortex_debug > 5)
+ printk("%s: In interrupt loop, status %4.4x.\n",
+ dev->name, status);
+ if (status & RxComplete)
+ vortex_rx(dev);
+
+ if (status & TxAvailable) {
+ if (vortex_debug > 5)
+ printk(" TX room bit was handled.\n");
+ /* There's room in the FIFO for a full-sized packet. */
+ outw(AckIntr | TxAvailable, ioaddr + EL3_CMD);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+#ifdef VORTEX_BUS_MASTER
+ if (status & DMADone) {
+ outw(0x1000, ioaddr + Wn7_MasterStatus); /* Ack the event. */
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+#endif
+ if (status & (AdapterFailure | RxEarly | StatsFull)) {
+ /* Handle all uncommon interrupts at once. */
+ if (status & RxEarly) { /* Rx early is unused. */
+ vortex_rx(dev);
+ outw(AckIntr | RxEarly, ioaddr + EL3_CMD);
+ }
+ if (status & StatsFull) { /* Empty statistics. */
+ static int DoneDidThat = 0;
+ if (vortex_debug > 4)
+ printk("%s: Updating stats.\n", dev->name);
+ update_stats(ioaddr, dev);
+ /* DEBUG HACK: Disable statistics as an interrupt source. */
+ /* This occurs when we have the wrong media type! */
+ if (DoneDidThat == 0 &&
+ inw(ioaddr + EL3_STATUS) & StatsFull) {
+ int win, reg;
+ printk("%s: Updating stats failed, disabling stats as an"
+ " interrupt source.\n", dev->name);
+ for (win = 0; win < 8; win++) {
+ EL3WINDOW(win);
+ printk("\n Vortex window %d:", win);
+ for (reg = 0; reg < 16; reg++)
+ printk(" %2.2x", inb(ioaddr+reg));
+ }
+ EL3WINDOW(7);
+ outw(SetIntrEnb | 0x18, ioaddr + EL3_CMD);
+ DoneDidThat++;
+ }
+ }
+ if (status & AdapterFailure) {
+ /* Adapter failure requires Rx reset and reinit. */
+ outw(RxReset, ioaddr + EL3_CMD);
+ /* Set the Rx filter to the current state. */
+ outw(SetRxFilter | RxStation | RxBroadcast
+ | (dev->flags & IFF_ALLMULTI ? RxMulticast : 0)
+ | (dev->flags & IFF_PROMISC ? RxProm : 0),
+ ioaddr + EL3_CMD);
+ outw(RxEnable, ioaddr + EL3_CMD); /* Re-enable the receiver. */
+ outw(AckIntr | AdapterFailure, ioaddr + EL3_CMD);
+ }
+ }
+
+ if (++i > 10) {
+ printk("%s: Infinite loop in interrupt, status %4.4x. "
+ "Disabling functions (%4.4x).\n",
+ dev->name, status, SetStatusEnb | ((~status) & 0xFE));
+ /* Disable all pending interrupts. */
+ outw(SetStatusEnb | ((~status) & 0xFE), ioaddr + EL3_CMD);
+ outw(AckIntr | 0xFF, ioaddr + EL3_CMD);
+ break;
+ }
+ /* Acknowledge the IRQ. */
+ outw(AckIntr | IntReq | IntLatch, ioaddr + EL3_CMD);
+
+ } while ((status = inw(ioaddr + EL3_STATUS)) & (IntLatch | RxComplete));
+
+ if (vortex_debug > 4)
+ printk("%s: exiting interrupt, status %4.4x.\n", dev->name, status);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+vortex_rx(struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+ short rx_status;
+
+ if (vortex_debug > 5)
+ printk(" In rx_packet(), status %4.4x, rx_status %4.4x.\n",
+ inw(ioaddr+EL3_STATUS), inw(ioaddr+RxStatus));
+ while ((rx_status = inw(ioaddr + RxStatus)) > 0) {
+ if (rx_status & 0x4000) { /* Error, update stats. */
+ unsigned char rx_error = inb(ioaddr + RxErrors);
+ if (vortex_debug > 4)
+ printk(" Rx error: status %2.2x.\n", rx_error);
+ vp->stats.rx_errors++;
+ if (rx_error & 0x01) vp->stats.rx_over_errors++;
+ if (rx_error & 0x02) vp->stats.rx_length_errors++;
+ if (rx_error & 0x04) vp->stats.rx_frame_errors++;
+ if (rx_error & 0x08) vp->stats.rx_crc_errors++;
+ if (rx_error & 0x10) vp->stats.rx_length_errors++;
+ } else {
+ /* The packet length: up to 4.5K!. */
+ short pkt_len = rx_status & 0x1fff;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len + 5);
+ if (vortex_debug > 4)
+ printk("Receiving packet size %d status %4.4x.\n",
+ pkt_len, rx_status);
+ if (skb != NULL) {
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* Align IP on 16 byte boundaries */
+ /* 'skb_put()' points to the start of sk_buff data area. */
+ insl(ioaddr + RX_FIFO, skb_put(skb, pkt_len),
+ (pkt_len + 3) >> 2);
+ skb->protocol = eth_type_trans(skb, dev);
+ netif_rx(skb);
+ outw(RxDiscard, ioaddr + EL3_CMD); /* Pop top Rx packet. */
+ /* Wait a limited time to go to next packet. */
+ for (i = 200; i >= 0; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ vp->stats.rx_packets++;
+ continue;
+ } else if (vortex_debug)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ }
+ vp->stats.rx_dropped++;
+ outw(RxDiscard, ioaddr + EL3_CMD);
+ /* Wait a limited time to skip this packet. */
+ for (i = 200; i >= 0; i--)
+ if ( ! inw(ioaddr + EL3_STATUS) & CmdInProgress)
+ break;
+ }
+
+ return 0;
+}
+
+static int
+vortex_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (vortex_debug > 1)
+ printk("%s: vortex_close() status %4.4x, Tx status %2.2x.\n",
+ dev->name, inw(ioaddr + EL3_STATUS), inb(ioaddr + TxStatus));
+
+ /* Turn off statistics ASAP. We update lp->stats below. */
+ outw(StatsDisable, ioaddr + EL3_CMD);
+
+ /* Disable the receiver and transmitter. */
+ outw(RxDisable, ioaddr + EL3_CMD);
+ outw(TxDisable, ioaddr + EL3_CMD);
+
+ if (dev->if_port == 3)
+ /* Turn off thinnet power. Green! */
+ outw(StopCoax, ioaddr + EL3_CMD);
+ else if (dev->if_port == 0) {
+ /* Disable link beat and jabber, if_port may change ere next open(). */
+ EL3WINDOW(4);
+ outw(inw(ioaddr + Wn4_Media) & ~Media_TP, ioaddr + Wn4_Media);
+ }
+
+#ifdef USE_SHARED_IRQ
+ free_shared_irq(dev->irq, dev);
+#else
+ free_irq(dev->irq);
+ /* Mmmm, we should diable all interrupt sources here. */
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ update_stats(ioaddr, dev);
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static struct enet_statistics *
+vortex_get_stats(struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ update_stats(dev->base_addr, dev);
+ restore_flags(flags);
+ return &vp->stats;
+}
+
+/* Update statistics.
+ Unlike with the EL3 we need not worry about interrupts changing
+ the window setting from underneath us, but we must still guard
+ against a race condition with a StatsUpdate interrupt updating the
+ table. This is done by checking that the ASM (!) code generated uses
+ atomic updates with '+='.
+ */
+static void update_stats(int ioaddr, struct device *dev)
+{
+ struct vortex_private *vp = (struct vortex_private *)dev->priv;
+
+ /* Unlike the 3c5x9 we need not turn off stats updates while reading. */
+ /* Switch to the stats window, and read everything. */
+ EL3WINDOW(6);
+ vp->stats.tx_carrier_errors += inb(ioaddr + 0);
+ vp->stats.tx_heartbeat_errors += inb(ioaddr + 1);
+ /* Multiple collisions. */ inb(ioaddr + 2);
+ vp->stats.collisions += inb(ioaddr + 3);
+ vp->stats.tx_window_errors += inb(ioaddr + 4);
+ vp->stats.rx_fifo_errors += inb(ioaddr + 5);
+ vp->stats.tx_packets += inb(ioaddr + 6);
+ vp->stats.tx_packets += (inb(ioaddr + 9)&0x30) << 4;
+ /* Rx packets */ inb(ioaddr + 7); /* Must read to clear */
+ /* Tx deferrals */ inb(ioaddr + 8);
+ /* Don't bother with register 9, an extention of registers 6&7.
+ If we do use the 6&7 values the atomic update assumption above
+ is invalid. */
+ inw(ioaddr + 10); /* Total Rx and Tx octets. */
+ inw(ioaddr + 12);
+ /* New: On the Vortex we must also clear the BadSSD counter. */
+ EL3WINDOW(4);
+ inb(ioaddr + 12);
+
+ /* We change back to window 7 (not 1) with the Vortex. */
+ EL3WINDOW(7);
+ return;
+}
+
+/* There are two version of set_multicast_list() to support both v1.2 and
+ v1.4 kernels. */
+static void
+set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if ((dev->mc_list) || (dev->flags & IFF_ALLMULTI)) {
+ outw(SetRxFilter|RxStation|RxMulticast|RxBroadcast, ioaddr + EL3_CMD);
+ if (vortex_debug > 3) {
+ printk("%s: Setting Rx multicast mode, %d addresses.\n",
+ dev->name, dev->mc_count);
+ }
+ } else if (dev->flags & IFF_PROMISC) {
+ outw(SetRxFilter | RxStation | RxMulticast | RxBroadcast | RxProm,
+ ioaddr + EL3_CMD);
+ } else
+ outw(SetRxFilter | RxStation | RxBroadcast, ioaddr + EL3_CMD);
+}
+
+
+#ifdef MODULE
+void
+cleanup_module(void)
+{
+ struct device *next_dev;
+
+ /* No need to check MOD_IN_USE, as sys_delete_module() checks. */
+ while (root_vortex_dev) {
+ next_dev = ((struct vortex_private *)root_vortex_dev->priv)->next_module;
+ unregister_netdev(root_vortex_dev);
+ release_region(root_vortex_dev->base_addr, VORTEX_TOTAL_SIZE);
+ kfree(root_vortex_dev);
+ root_vortex_dev = next_dev;
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DMODULE -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 3c59x.c -o 3c59x.o"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/8390.c b/i386/i386at/gpl/linux/net/8390.c
new file mode 100644
index 00000000..05ea32f6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/8390.c
@@ -0,0 +1,727 @@
+/* 8390.c: A general NS8390 ethernet driver core for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is the chip-specific code for many 8390-based ethernet adaptors.
+ This is not a complete driver, it must be combined with board-specific
+ code such as ne.c, wd.c, 3c503.c, etc.
+
+ Changelog:
+
+ Paul Gortmaker : remove set_bit lock, other cleanups.
+ Paul Gortmaker : add ei_get_8390_hdr() so we can pass skb's to
+ ei_block_input() for eth_io_copy_and_sum().
+
+ */
+
+static const char *version =
+ "8390.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/*
+ Braindamage remaining:
+ Much of this code should have been cleaned up, but every attempt
+ has broken some clone part.
+
+ Sources:
+ The National Semiconductor LAN Databook, and the 3Com 3c503 databook.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/fs.h>
+#include <linux/types.h>
+#include <linux/ptrace.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <linux/errno.h>
+#include <linux/fcntl.h>
+#include <linux/in.h>
+#include <linux/interrupt.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include "8390.h"
+
+/* These are the operational function interfaces to board-specific
+ routines.
+ void reset_8390(struct device *dev)
+ Resets the board associated with DEV, including a hardware reset of
+ the 8390. This is only called when there is a transmit timeout, and
+ it is always followed by 8390_init().
+ void block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+ Write the COUNT bytes of BUF to the packet buffer at START_PAGE. The
+ "page" value uses the 8390's 256-byte pages.
+ void get_8390_hdr(struct device *dev, struct e8390_hdr *hdr, int ring_page)
+ Read the 4 byte, page aligned 8390 header. *If* there is a
+ subsequent read, it will be of the rest of the packet.
+ void block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+ Read COUNT bytes from the packet buffer into the skb data area. Start
+ reading from RING_OFFSET, the address as the 8390 sees it. This will always
+ follow the read of the 8390 header.
+*/
+#define ei_reset_8390 (ei_local->reset_8390)
+#define ei_block_output (ei_local->block_output)
+#define ei_block_input (ei_local->block_input)
+#define ei_get_8390_hdr (ei_local->get_8390_hdr)
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifdef EI_DEBUG
+int ei_debug = EI_DEBUG;
+#else
+int ei_debug = 1;
+#endif
+#ifdef EI_PINGPONG
+static int ei_pingpong = 1;
+#else
+static int ei_pingpong = 0;
+#endif
+
+/* Max number of packets received at one Intr.
+ Currently this may only be examined by a kernel debugger. */
+static int high_water_mark = 0;
+
+/* Index to functions. */
+static void ei_tx_intr(struct device *dev);
+static void ei_receive(struct device *dev);
+static void ei_rx_overrun(struct device *dev);
+
+/* Routines generic to NS8390-based boards. */
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+ int start_page);
+static void set_multicast_list(struct device *dev);
+
+
+/* Open/initialize the board. This routine goes all-out, setting everything
+ up anew at each open, even though many of these registers should only
+ need to be set once at boot.
+ */
+int ei_open(struct device *dev)
+{
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ /* This can't happen unless somebody forgot to call ethdev_init(). */
+ if (ei_local == NULL) {
+ printk(KERN_EMERG "%s: ei_open passed a non-existent device!\n", dev->name);
+ return -ENXIO;
+ }
+
+ irq2dev_map[dev->irq] = dev;
+ NS8390_init(dev, 1);
+ dev->start = 1;
+ ei_local->irqlock = 0;
+ return 0;
+}
+
+/* Opposite of above. Only used when "ifconfig <devname> down" is done. */
+int ei_close(struct device *dev)
+{
+ NS8390_init(dev, 0);
+ dev->start = 0;
+ return 0;
+}
+
+static int ei_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ int length, send_length;
+
+/*
+ * We normally shouldn't be called if dev->tbusy is set, but the
+ * existing code does anyway. If it has been too long since the
+ * last Tx, we assume the board has died and kick it.
+ */
+
+ if (dev->tbusy) { /* Do timeouts, just like the 8003 driver. */
+ int txsr = inb(e8390_base+EN0_TSR), isr;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < TX_TIMEOUT || (tickssofar < (TX_TIMEOUT+5) && ! (txsr & ENTSR_PTX))) {
+ return 1;
+ }
+ isr = inb(e8390_base+EN0_ISR);
+ if (dev->start == 0) {
+ printk("%s: xmit on stopped card\n", dev->name);
+ return 1;
+ }
+
+ printk(KERN_DEBUG "%s: Tx timed out, %s TSR=%#2x, ISR=%#2x, t=%d.\n",
+ dev->name, (txsr & ENTSR_ABT) ? "excess collisions." :
+ (isr) ? "lost interrupt?" : "cable problem?", txsr, isr, tickssofar);
+
+ if (!isr && !ei_local->stat.tx_packets) {
+ /* The 8390 probably hasn't gotten on the cable yet. */
+ ei_local->interface_num ^= 1; /* Try a different xcvr. */
+ }
+
+ /* Try to restart the card. Perhaps the user has fixed something. */
+ ei_reset_8390(dev);
+ NS8390_init(dev, 1);
+ dev->trans_start = jiffies;
+ }
+
+ /* Sending a NULL skb means some higher layer thinks we've missed an
+ tx-done interrupt. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ length = skb->len;
+ if (skb->len <= 0)
+ return 0;
+
+ /* Mask interrupts from the ethercard. */
+ outb_p(0x00, e8390_base + EN0_IMR);
+ if (dev->interrupt) {
+ printk("%s: Tx request while isr active.\n",dev->name);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ return 1;
+ }
+ ei_local->irqlock = 1;
+
+ send_length = ETH_ZLEN < length ? length : ETH_ZLEN;
+
+ if (ei_local->pingpong) {
+ int output_page;
+ if (ei_local->tx1 == 0) {
+ output_page = ei_local->tx_start_page;
+ ei_local->tx1 = send_length;
+ if (ei_debug && ei_local->tx2 > 0)
+ printk("%s: idle transmitter tx2=%d, lasttx=%d, txing=%d.\n",
+ dev->name, ei_local->tx2, ei_local->lasttx,
+ ei_local->txing);
+ } else if (ei_local->tx2 == 0) {
+ output_page = ei_local->tx_start_page + 6;
+ ei_local->tx2 = send_length;
+ if (ei_debug && ei_local->tx1 > 0)
+ printk("%s: idle transmitter, tx1=%d, lasttx=%d, txing=%d.\n",
+ dev->name, ei_local->tx1, ei_local->lasttx,
+ ei_local->txing);
+ } else { /* We should never get here. */
+ if (ei_debug)
+ printk("%s: No Tx buffers free. irq=%d tx1=%d tx2=%d last=%d\n",
+ dev->name, dev->interrupt, ei_local->tx1,
+ ei_local->tx2, ei_local->lasttx);
+ ei_local->irqlock = 0;
+ dev->tbusy = 1;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ return 1;
+ }
+ ei_block_output(dev, length, skb->data, output_page);
+ if (! ei_local->txing) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, output_page);
+ dev->trans_start = jiffies;
+ if (output_page == ei_local->tx_start_page)
+ ei_local->tx1 = -1, ei_local->lasttx = -1;
+ else
+ ei_local->tx2 = -1, ei_local->lasttx = -2;
+ } else
+ ei_local->txqueue++;
+
+ dev->tbusy = (ei_local->tx1 && ei_local->tx2);
+ } else { /* No pingpong, just a single Tx buffer. */
+ ei_block_output(dev, length, skb->data, ei_local->tx_start_page);
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, send_length, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ dev->tbusy = 1;
+ }
+
+ /* Turn 8390 interrupts back on. */
+ ei_local->irqlock = 0;
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the ether interface interrupts. */
+void ei_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int e8390_base;
+ int interrupts, nr_serviced = 0;
+ struct ei_device *ei_local;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ e8390_base = dev->base_addr;
+ ei_local = (struct ei_device *) dev->priv;
+ if (dev->interrupt || ei_local->irqlock) {
+ /* The "irqlock" check is only for testing. */
+ printk(ei_local->irqlock
+ ? "%s: Interrupted while interrupts are masked! isr=%#2x imr=%#2x.\n"
+ : "%s: Reentering the interrupt handler! isr=%#2x imr=%#2x.\n",
+ dev->name, inb_p(e8390_base + EN0_ISR),
+ inb_p(e8390_base + EN0_IMR));
+ return;
+ }
+
+ dev->interrupt = 1;
+
+ /* Change to page 0 and read the intr status reg. */
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+ if (ei_debug > 3)
+ printk("%s: interrupt(isr=%#2.2x).\n", dev->name,
+ inb_p(e8390_base + EN0_ISR));
+
+ /* !!Assumption!! -- we stay in page 0. Don't break this. */
+ while ((interrupts = inb_p(e8390_base + EN0_ISR)) != 0
+ && ++nr_serviced < MAX_SERVICE) {
+ if (dev->start == 0) {
+ printk("%s: interrupt from stopped card\n", dev->name);
+ interrupts = 0;
+ break;
+ }
+ if (interrupts & ENISR_OVER) {
+ ei_rx_overrun(dev);
+ } else if (interrupts & (ENISR_RX+ENISR_RX_ERR)) {
+ /* Got a good (?) packet. */
+ ei_receive(dev);
+ }
+ /* Push the next to-transmit packet through. */
+ if (interrupts & ENISR_TX) {
+ ei_tx_intr(dev);
+ } else if (interrupts & ENISR_COUNTERS) {
+ ei_local->stat.rx_frame_errors += inb_p(e8390_base + EN0_COUNTER0);
+ ei_local->stat.rx_crc_errors += inb_p(e8390_base + EN0_COUNTER1);
+ ei_local->stat.rx_missed_errors+= inb_p(e8390_base + EN0_COUNTER2);
+ outb_p(ENISR_COUNTERS, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore the transmit errs and reset intr for now. */
+ if (interrupts & ENISR_TX_ERR) {
+ outb_p(ENISR_TX_ERR, e8390_base + EN0_ISR); /* Ack intr. */
+ }
+
+ /* Ignore any RDC interrupts that make it back to here. */
+ if (interrupts & ENISR_RDC) {
+ outb_p(ENISR_RDC, e8390_base + EN0_ISR);
+ }
+
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ }
+
+ if (interrupts && ei_debug) {
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base + E8390_CMD);
+ if (nr_serviced >= MAX_SERVICE) {
+ printk("%s: Too much work at interrupt, status %#2.2x\n",
+ dev->name, interrupts);
+ outb_p(ENISR_ALL, e8390_base + EN0_ISR); /* Ack. most intrs. */
+ } else {
+ printk("%s: unknown interrupt %#2x\n", dev->name, interrupts);
+ outb_p(0xff, e8390_base + EN0_ISR); /* Ack. all intrs. */
+ }
+ }
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have finished a transmit: check for errors and then trigger the next
+ packet to be sent. */
+static void ei_tx_intr(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ int status = inb(e8390_base + EN0_TSR);
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ outb_p(ENISR_TX, e8390_base + EN0_ISR); /* Ack intr. */
+
+ if (ei_local->pingpong) {
+ ei_local->txqueue--;
+ if (ei_local->tx1 < 0) {
+ if (ei_local->lasttx != 1 && ei_local->lasttx != -1)
+ printk("%s: bogus last_tx_buffer %d, tx1=%d.\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx1);
+ ei_local->tx1 = 0;
+ dev->tbusy = 0;
+ if (ei_local->tx2 > 0) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx2, ei_local->tx_start_page + 6);
+ dev->trans_start = jiffies;
+ ei_local->tx2 = -1,
+ ei_local->lasttx = 2;
+ } else
+ ei_local->lasttx = 20, ei_local->txing = 0;
+ } else if (ei_local->tx2 < 0) {
+ if (ei_local->lasttx != 2 && ei_local->lasttx != -2)
+ printk("%s: bogus last_tx_buffer %d, tx2=%d.\n",
+ ei_local->name, ei_local->lasttx, ei_local->tx2);
+ ei_local->tx2 = 0;
+ dev->tbusy = 0;
+ if (ei_local->tx1 > 0) {
+ ei_local->txing = 1;
+ NS8390_trigger_send(dev, ei_local->tx1, ei_local->tx_start_page);
+ dev->trans_start = jiffies;
+ ei_local->tx1 = -1;
+ ei_local->lasttx = 1;
+ } else
+ ei_local->lasttx = 10, ei_local->txing = 0;
+ } else
+ printk("%s: unexpected TX-done interrupt, lasttx=%d.\n",
+ dev->name, ei_local->lasttx);
+ } else {
+ ei_local->txing = 0;
+ dev->tbusy = 0;
+ }
+
+ /* Minimize Tx latency: update the statistics after we restart TXing. */
+ if (status & ENTSR_COL) ei_local->stat.collisions++;
+ if (status & ENTSR_PTX)
+ ei_local->stat.tx_packets++;
+ else {
+ ei_local->stat.tx_errors++;
+ if (status & ENTSR_ABT) ei_local->stat.tx_aborted_errors++;
+ if (status & ENTSR_CRS) ei_local->stat.tx_carrier_errors++;
+ if (status & ENTSR_FU) ei_local->stat.tx_fifo_errors++;
+ if (status & ENTSR_CDH) ei_local->stat.tx_heartbeat_errors++;
+ if (status & ENTSR_OWC) ei_local->stat.tx_window_errors++;
+ }
+
+ mark_bh (NET_BH);
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+
+static void ei_receive(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ int rxing_page, this_frame, next_frame, current_offset;
+ int rx_pkt_count = 0;
+ struct e8390_pkt_hdr rx_frame;
+ int num_rx_pages = ei_local->stop_page-ei_local->rx_start_page;
+
+ while (++rx_pkt_count < 10) {
+ int pkt_len;
+
+ /* Get the rx page (incoming packet pointer). */
+ outb_p(E8390_NODMA+E8390_PAGE1, e8390_base + E8390_CMD);
+ rxing_page = inb_p(e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base + E8390_CMD);
+
+ /* Remove one frame from the ring. Boundary is always a page behind. */
+ this_frame = inb_p(e8390_base + EN0_BOUNDARY) + 1;
+ if (this_frame >= ei_local->stop_page)
+ this_frame = ei_local->rx_start_page;
+
+ /* Someday we'll omit the previous, iff we never get this message.
+ (There is at least one clone claimed to have a problem.) */
+ if (ei_debug > 0 && this_frame != ei_local->current_page)
+ printk("%s: mismatched read page pointers %2x vs %2x.\n",
+ dev->name, this_frame, ei_local->current_page);
+
+ if (this_frame == rxing_page) /* Read all the frames? */
+ break; /* Done for now */
+
+ current_offset = this_frame << 8;
+ ei_get_8390_hdr(dev, &rx_frame, this_frame);
+
+ pkt_len = rx_frame.count - sizeof(struct e8390_pkt_hdr);
+
+ next_frame = this_frame + 1 + ((pkt_len+4)>>8);
+
+ /* Check for bogosity warned by 3c503 book: the status byte is never
+ written. This happened a lot during testing! This code should be
+ cleaned up someday. */
+ if (rx_frame.next != next_frame
+ && rx_frame.next != next_frame + 1
+ && rx_frame.next != next_frame - num_rx_pages
+ && rx_frame.next != next_frame + 1 - num_rx_pages) {
+ ei_local->current_page = rxing_page;
+ outb(ei_local->current_page-1, e8390_base+EN0_BOUNDARY);
+ ei_local->stat.rx_errors++;
+ continue;
+ }
+
+ if (pkt_len < 60 || pkt_len > 1518) {
+ if (ei_debug)
+ printk("%s: bogus packet size: %d, status=%#2x nxpg=%#2x.\n",
+ dev->name, rx_frame.count, rx_frame.status,
+ rx_frame.next);
+ ei_local->stat.rx_errors++;
+ } else if ((rx_frame.status & 0x0F) == ENRSR_RXOK) {
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ if (ei_debug > 1)
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, pkt_len);
+ ei_local->stat.rx_dropped++;
+ break;
+ } else {
+ skb_reserve(skb,2); /* IP headers on 16 byte boundaries */
+ skb->dev = dev;
+ skb_put(skb, pkt_len); /* Make room */
+ ei_block_input(dev, pkt_len, skb, current_offset + sizeof(rx_frame));
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ ei_local->stat.rx_packets++;
+ }
+ } else {
+ int errs = rx_frame.status;
+ if (ei_debug)
+ printk("%s: bogus packet: status=%#2x nxpg=%#2x size=%d\n",
+ dev->name, rx_frame.status, rx_frame.next,
+ rx_frame.count);
+ if (errs & ENRSR_FO)
+ ei_local->stat.rx_fifo_errors++;
+ }
+ next_frame = rx_frame.next;
+
+ /* This _should_ never happen: it's here for avoiding bad clones. */
+ if (next_frame >= ei_local->stop_page) {
+ printk("%s: next frame inconsistency, %#2x\n", dev->name,
+ next_frame);
+ next_frame = ei_local->rx_start_page;
+ }
+ ei_local->current_page = next_frame;
+ outb_p(next_frame-1, e8390_base+EN0_BOUNDARY);
+ }
+ /* If any worth-while packets have been received, netif_rx()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+
+ /* Record the maximum Rx packet queue. */
+ if (rx_pkt_count > high_water_mark)
+ high_water_mark = rx_pkt_count;
+
+ /* We used to also ack ENISR_OVER here, but that would sometimes mask
+ a real overrun, leaving the 8390 in a stopped state with rec'vr off. */
+ outb_p(ENISR_RX+ENISR_RX_ERR, e8390_base+EN0_ISR);
+ return;
+}
+
+/* We have a receiver overrun: we have to kick the 8390 to get it started
+ again.*/
+static void ei_rx_overrun(struct device *dev)
+{
+ int e8390_base = dev->base_addr;
+ int reset_start_time = jiffies;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ /* We should already be stopped and in page0. Remove after testing. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base+E8390_CMD);
+
+ if (ei_debug > 1)
+ printk("%s: Receiver overrun.\n", dev->name);
+ ei_local->stat.rx_over_errors++;
+
+ /* The old Biro driver does dummy = inb_p( RBCR[01] ); at this point.
+ It might mean something -- magic to speed up a reset? A 8390 bug?*/
+
+ /* Wait for the reset to complete. This should happen almost instantly,
+ but could take up to 1.5msec in certain rare instances. There is no
+ easy way of timing something in that range, so we use 'jiffies' as
+ a sanity check. */
+ while ((inb_p(e8390_base+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2*HZ/100) {
+ printk("%s: reset did not complete at ei_rx_overrun.\n",
+ dev->name);
+ NS8390_init(dev, 1);
+ return;
+ }
+
+ /* Remove packets right away. */
+ ei_receive(dev);
+
+ outb_p(ENISR_OVER, e8390_base+EN0_ISR);
+ /* Generic 8390 insns to start up again, same as in open_8390(). */
+ outb_p(E8390_NODMA + E8390_PAGE0 + E8390_START, e8390_base + E8390_CMD);
+ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+}
+
+static struct enet_statistics *get_stats(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+
+ /* If the card is stopped, just return the present stats. */
+ if (dev->start == 0) return &ei_local->stat;
+
+ /* Read the counter registers, assuming we are in page 0. */
+ ei_local->stat.rx_frame_errors += inb_p(ioaddr + EN0_COUNTER0);
+ ei_local->stat.rx_crc_errors += inb_p(ioaddr + EN0_COUNTER1);
+ ei_local->stat.rx_missed_errors+= inb_p(ioaddr + EN0_COUNTER2);
+
+ return &ei_local->stat;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ outb_p(E8390_RXCONFIG | 0x18, ioaddr + EN0_RXCR);
+ }
+ else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
+ {
+ /* The multicast-accept list is initialized to accept-all, and we
+ rely on higher-level filtering for now. */
+ outb_p(E8390_RXCONFIG | 0x08, ioaddr + EN0_RXCR);
+ }
+ else
+ outb_p(E8390_RXCONFIG, ioaddr + EN0_RXCR);
+}
+
+/* Initialize the rest of the 8390 device structure. */
+int ethdev_init(struct device *dev)
+{
+ if (ei_debug > 1)
+ printk(version);
+
+ if (dev->priv == NULL) {
+ struct ei_device *ei_local;
+
+ dev->priv = kmalloc(sizeof(struct ei_device), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct ei_device));
+ ei_local = (struct ei_device *)dev->priv;
+ ei_local->pingpong = ei_pingpong;
+ }
+
+ dev->hard_start_xmit = &ei_start_xmit;
+ dev->get_stats = get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ ether_setup(dev);
+
+ return 0;
+}
+
+
+/* This page of functions should be 8390 generic */
+/* Follow National Semi's recommendations for initializing the "NIC". */
+void NS8390_init(struct device *dev, int startp)
+{
+ int e8390_base = dev->base_addr;
+ struct ei_device *ei_local = (struct ei_device *) dev->priv;
+ int i;
+ int endcfg = ei_local->word16 ? (0x48 | ENDCFG_WTS) : 0x48;
+ unsigned long flags;
+
+ /* Follow National Semi's recommendations for initing the DP83902. */
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base); /* 0x21 */
+ outb_p(endcfg, e8390_base + EN0_DCFG); /* 0x48 or 0x49 */
+ /* Clear the remote byte count registers. */
+ outb_p(0x00, e8390_base + EN0_RCNTLO);
+ outb_p(0x00, e8390_base + EN0_RCNTHI);
+ /* Set to monitor and loopback mode -- this is vital!. */
+ outb_p(E8390_RXOFF, e8390_base + EN0_RXCR); /* 0x20 */
+ outb_p(E8390_TXOFF, e8390_base + EN0_TXCR); /* 0x02 */
+ /* Set the transmit page and receive ring. */
+ outb_p(ei_local->tx_start_page, e8390_base + EN0_TPSR);
+ ei_local->tx1 = ei_local->tx2 = 0;
+ outb_p(ei_local->rx_start_page, e8390_base + EN0_STARTPG);
+ outb_p(ei_local->stop_page-1, e8390_base + EN0_BOUNDARY); /* 3c503 says 0x3f,NS0x26*/
+ ei_local->current_page = ei_local->rx_start_page; /* assert boundary+1 */
+ outb_p(ei_local->stop_page, e8390_base + EN0_STOPPG);
+ /* Clear the pending interrupts and mask. */
+ outb_p(0xFF, e8390_base + EN0_ISR);
+ outb_p(0x00, e8390_base + EN0_IMR);
+
+ /* Copy the station address into the DS8390 registers,
+ and set the multicast hash bitmap to receive all multicasts. */
+ save_flags(flags);
+ cli();
+ outb_p(E8390_NODMA + E8390_PAGE1 + E8390_STOP, e8390_base); /* 0x61 */
+ for(i = 0; i < 6; i++) {
+ outb_p(dev->dev_addr[i], e8390_base + EN1_PHYS + i);
+ }
+ /* Initialize the multicast list to accept-all. If we enable multicast
+ the higher levels can do the filtering. */
+ for(i = 0; i < 8; i++)
+ outb_p(0xff, e8390_base + EN1_MULT + i);
+
+ outb_p(ei_local->rx_start_page, e8390_base + EN1_CURPAG);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_STOP, e8390_base);
+ restore_flags(flags);
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ ei_local->tx1 = ei_local->tx2 = 0;
+ ei_local->txing = 0;
+ if (startp) {
+ outb_p(0xff, e8390_base + EN0_ISR);
+ outb_p(ENISR_ALL, e8390_base + EN0_IMR);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, e8390_base);
+ outb_p(E8390_TXCONFIG, e8390_base + EN0_TXCR); /* xmit on. */
+ /* 3c503 TechMan says rxconfig only after the NIC is started. */
+ outb_p(E8390_RXCONFIG, e8390_base + EN0_RXCR); /* rx on, */
+ dev->set_multicast_list(dev); /* Get the multicast status right if this
+ was a reset. */
+ }
+ return;
+}
+
+/* Trigger a transmit start, assuming the length is valid. */
+static void NS8390_trigger_send(struct device *dev, unsigned int length,
+ int start_page)
+{
+ int e8390_base = dev->base_addr;
+
+ outb_p(E8390_NODMA+E8390_PAGE0, e8390_base);
+
+ if (inb_p(e8390_base) & E8390_TRANS) {
+ printk("%s: trigger_send() called with the transmitter busy.\n",
+ dev->name);
+ return;
+ }
+ outb_p(length & 0xff, e8390_base + EN0_TCNTLO);
+ outb_p(length >> 8, e8390_base + EN0_TCNTHI);
+ outb_p(start_page, e8390_base + EN0_TPSR);
+ outb_p(E8390_NODMA+E8390_TRANS+E8390_START, e8390_base);
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void)
+{
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c 8390.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/8390.h b/i386/i386at/gpl/linux/net/8390.h
new file mode 100644
index 00000000..17b8cdb5
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/8390.h
@@ -0,0 +1,168 @@
+/* Generic NS8390 register definitions. */
+/* This file is part of Donald Becker's 8390 drivers, and is distributed
+ under the same license.
+ Some of these names and comments originated from the Crynwr
+ packet drivers, which are distributed under the GPL. */
+
+#ifndef _8390_h
+#define _8390_h
+
+#include <linux/if_ether.h>
+#include <linux/ioport.h>
+#include <linux/skbuff.h>
+
+#define TX_2X_PAGES 12
+#define TX_1X_PAGES 6
+#define TX_PAGES (ei_status.pingpong ? TX_2X_PAGES : TX_1X_PAGES)
+
+#define ETHER_ADDR_LEN 6
+
+/* The 8390 specific per-packet-header format. */
+struct e8390_pkt_hdr {
+ unsigned char status; /* status */
+ unsigned char next; /* pointer to next packet. */
+ unsigned short count; /* header + packet length in bytes */
+};
+
+/* From 8390.c */
+extern int ei_debug;
+extern struct sigaction ei_sigaction;
+
+extern int ethif_init(struct device *dev);
+extern int ethdev_init(struct device *dev);
+extern void NS8390_init(struct device *dev, int startp);
+extern int ei_open(struct device *dev);
+extern int ei_close(struct device *dev);
+extern void ei_interrupt(int irq, struct pt_regs *regs);
+
+#ifndef HAVE_AUTOIRQ
+/* From auto_irq.c */
+extern struct device *irq2dev_map[16];
+extern int autoirq_setup(int waittime);
+extern int autoirq_report(int waittime);
+#endif
+
+/* Most of these entries should be in 'struct device' (or most of the
+ things in there should be here!) */
+/* You have one of these per-board */
+struct ei_device {
+ const char *name;
+ void (*reset_8390)(struct device *);
+ void (*get_8390_hdr)(struct device *, struct e8390_pkt_hdr *, int);
+ void (*block_output)(struct device *, int, const unsigned char *, int);
+ void (*block_input)(struct device *, int, struct sk_buff *, int);
+ unsigned open:1;
+ unsigned word16:1; /* We have the 16-bit (vs 8-bit) version of the card. */
+ unsigned txing:1; /* Transmit Active */
+ unsigned irqlock:1; /* 8390's intrs disabled when '1'. */
+ unsigned dmaing:1; /* Remote DMA Active */
+ unsigned pingpong:1; /* Using the ping-pong driver */
+ unsigned char tx_start_page, rx_start_page, stop_page;
+ unsigned char current_page; /* Read pointer in buffer */
+ unsigned char interface_num; /* Net port (AUI, 10bT.) to use. */
+ unsigned char txqueue; /* Tx Packet buffer queue length. */
+ short tx1, tx2; /* Packet lengths for ping-pong tx. */
+ short lasttx; /* Alpha version consistency check. */
+ unsigned char reg0; /* Register '0' in a WD8013 */
+ unsigned char reg5; /* Register '5' in a WD8013 */
+ unsigned char saved_irq; /* Original dev->irq value. */
+ /* The new statistics table. */
+ struct enet_statistics stat;
+};
+
+/* The maximum number of 8390 interrupt service routines called per IRQ. */
+#define MAX_SERVICE 12
+
+/* The maximum time waited (in jiffies) before assuming a Tx failed. (20ms) */
+#define TX_TIMEOUT (20*HZ/100)
+
+#define ei_status (*(struct ei_device *)(dev->priv))
+
+/* Some generic ethernet register configurations. */
+#define E8390_TX_IRQ_MASK 0xa /* For register EN0_ISR */
+#define E8390_RX_IRQ_MASK 0x5
+#define E8390_RXCONFIG 0x4 /* EN0_RXCR: broadcasts, no multicast,errors */
+#define E8390_RXOFF 0x20 /* EN0_RXCR: Accept no packets */
+#define E8390_TXCONFIG 0x00 /* EN0_TXCR: Normal transmit mode */
+#define E8390_TXOFF 0x02 /* EN0_TXCR: Transmitter off */
+
+/* Register accessed at EN_CMD, the 8390 base addr. */
+#define E8390_STOP 0x01 /* Stop and reset the chip */
+#define E8390_START 0x02 /* Start the chip, clear reset */
+#define E8390_TRANS 0x04 /* Transmit a frame */
+#define E8390_RREAD 0x08 /* Remote read */
+#define E8390_RWRITE 0x10 /* Remote write */
+#define E8390_NODMA 0x20 /* Remote DMA */
+#define E8390_PAGE0 0x00 /* Select page chip registers */
+#define E8390_PAGE1 0x40 /* using the two high-order bits */
+#define E8390_PAGE2 0x80 /* Page 3 is invalid. */
+
+#define E8390_CMD 0x00 /* The command register (for all pages) */
+/* Page 0 register offsets. */
+#define EN0_CLDALO 0x01 /* Low byte of current local dma addr RD */
+#define EN0_STARTPG 0x01 /* Starting page of ring bfr WR */
+#define EN0_CLDAHI 0x02 /* High byte of current local dma addr RD */
+#define EN0_STOPPG 0x02 /* Ending page +1 of ring bfr WR */
+#define EN0_BOUNDARY 0x03 /* Boundary page of ring bfr RD WR */
+#define EN0_TSR 0x04 /* Transmit status reg RD */
+#define EN0_TPSR 0x04 /* Transmit starting page WR */
+#define EN0_NCR 0x05 /* Number of collision reg RD */
+#define EN0_TCNTLO 0x05 /* Low byte of tx byte count WR */
+#define EN0_FIFO 0x06 /* FIFO RD */
+#define EN0_TCNTHI 0x06 /* High byte of tx byte count WR */
+#define EN0_ISR 0x07 /* Interrupt status reg RD WR */
+#define EN0_CRDALO 0x08 /* low byte of current remote dma address RD */
+#define EN0_RSARLO 0x08 /* Remote start address reg 0 */
+#define EN0_CRDAHI 0x09 /* high byte, current remote dma address RD */
+#define EN0_RSARHI 0x09 /* Remote start address reg 1 */
+#define EN0_RCNTLO 0x0a /* Remote byte count reg WR */
+#define EN0_RCNTHI 0x0b /* Remote byte count reg WR */
+#define EN0_RSR 0x0c /* rx status reg RD */
+#define EN0_RXCR 0x0c /* RX configuration reg WR */
+#define EN0_TXCR 0x0d /* TX configuration reg WR */
+#define EN0_COUNTER0 0x0d /* Rcv alignment error counter RD */
+#define EN0_DCFG 0x0e /* Data configuration reg WR */
+#define EN0_COUNTER1 0x0e /* Rcv CRC error counter RD */
+#define EN0_IMR 0x0f /* Interrupt mask reg WR */
+#define EN0_COUNTER2 0x0f /* Rcv missed frame error counter RD */
+
+/* Bits in EN0_ISR - Interrupt status register */
+#define ENISR_RX 0x01 /* Receiver, no error */
+#define ENISR_TX 0x02 /* Transmitter, no error */
+#define ENISR_RX_ERR 0x04 /* Receiver, with error */
+#define ENISR_TX_ERR 0x08 /* Transmitter, with error */
+#define ENISR_OVER 0x10 /* Receiver overwrote the ring */
+#define ENISR_COUNTERS 0x20 /* Counters need emptying */
+#define ENISR_RDC 0x40 /* remote dma complete */
+#define ENISR_RESET 0x80 /* Reset completed */
+#define ENISR_ALL 0x3f /* Interrupts we will enable */
+
+/* Bits in EN0_DCFG - Data config register */
+#define ENDCFG_WTS 0x01 /* word transfer mode selection */
+
+/* Page 1 register offsets. */
+#define EN1_PHYS 0x01 /* This board's physical enet addr RD WR */
+#define EN1_CURPAG 0x07 /* Current memory page RD WR */
+#define EN1_MULT 0x08 /* Multicast filter mask array (8 bytes) RD WR */
+
+/* Bits in received packet status byte and EN0_RSR*/
+#define ENRSR_RXOK 0x01 /* Received a good packet */
+#define ENRSR_CRC 0x02 /* CRC error */
+#define ENRSR_FAE 0x04 /* frame alignment error */
+#define ENRSR_FO 0x08 /* FIFO overrun */
+#define ENRSR_MPA 0x10 /* missed pkt */
+#define ENRSR_PHY 0x20 /* physical/multicase address */
+#define ENRSR_DIS 0x40 /* receiver disable. set in monitor mode */
+#define ENRSR_DEF 0x80 /* deferring */
+
+/* Transmitted packet status, EN0_TSR. */
+#define ENTSR_PTX 0x01 /* Packet transmitted without error */
+#define ENTSR_ND 0x02 /* The transmit wasn't deferred. */
+#define ENTSR_COL 0x04 /* The transmit collided at least once. */
+#define ENTSR_ABT 0x08 /* The transmit collided 16 times, and was deferred. */
+#define ENTSR_CRS 0x10 /* The carrier sense was lost. */
+#define ENTSR_FU 0x20 /* A "FIFO underrun" occurred during transmit. */
+#define ENTSR_CDH 0x40 /* The collision detect "heartbeat" signal was lost. */
+#define ENTSR_OWC 0x80 /* There was an out-of-window collision. */
+
+#endif /* _8390_h */
diff --git a/i386/i386at/gpl/linux/net/Space.c b/i386/i386at/gpl/linux/net/Space.c
new file mode 100644
index 00000000..a05507d3
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/Space.c
@@ -0,0 +1,400 @@
+/*
+ * INET An implementation of the TCP/IP protocol suite for the LINUX
+ * operating system. INET is implemented using the BSD Socket
+ * interface as the means of communication with the user level.
+ *
+ * Holds initial configuration information for devices.
+ *
+ * NOTE: This file is a nice idea, but its current format does not work
+ * well for drivers that support multiple units, like the SLIP
+ * driver. We should actually have only one pointer to a driver
+ * here, with the driver knowing how many units it supports.
+ * Currently, the SLIP driver abuses the "base_addr" integer
+ * field of the 'device' structure to store the unit number...
+ * -FvK
+ *
+ * Version: @(#)Space.c 1.0.7 08/12/93
+ *
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Donald J. Becker, <becker@super.org>
+ *
+ * FIXME:
+ * Sort the device chain fastest first.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version
+ * 2 of the License, or (at your option) any later version.
+ */
+#include <linux/config.h>
+#include <linux/netdevice.h>
+#include <linux/errno.h>
+
+#define NEXT_DEV NULL
+
+
+/* A unified ethernet device probe. This is the easiest way to have every
+ ethernet adaptor have the name "eth[0123...]".
+ */
+
+extern int hp100_probe(struct device *dev);
+extern int ultra_probe(struct device *dev);
+extern int wd_probe(struct device *dev);
+extern int el2_probe(struct device *dev);
+extern int ne_probe(struct device *dev);
+extern int hp_probe(struct device *dev);
+extern int hp_plus_probe(struct device *dev);
+extern int znet_probe(struct device *);
+extern int express_probe(struct device *);
+extern int eepro_probe(struct device *);
+extern int el3_probe(struct device *);
+extern int at1500_probe(struct device *);
+extern int at1700_probe(struct device *);
+extern int eth16i_probe(struct device *);
+extern int depca_probe(struct device *);
+extern int apricot_probe(struct device *);
+extern int ewrk3_probe(struct device *);
+extern int de4x5_probe(struct device *);
+extern int el1_probe(struct device *);
+#if defined(CONFIG_WAVELAN)
+extern int wavelan_probe(struct device *);
+#endif /* defined(CONFIG_WAVELAN) */
+extern int el16_probe(struct device *);
+extern int elplus_probe(struct device *);
+extern int ac3200_probe(struct device *);
+extern int e2100_probe(struct device *);
+extern int ni52_probe(struct device *);
+extern int ni65_probe(struct device *);
+extern int SK_init(struct device *);
+extern int seeq8005_probe(struct device *);
+extern int tc59x_probe(struct device *);
+
+/* Detachable devices ("pocket adaptors") */
+extern int atp_init(struct device *);
+extern int de600_probe(struct device *);
+extern int de620_probe(struct device *);
+
+static int
+ethif_probe(struct device *dev)
+{
+ u_long base_addr = dev->base_addr;
+
+ if ((base_addr == 0xffe0) || (base_addr == 1))
+ return 1; /* ENXIO */
+
+ if (1
+#if defined(CONFIG_VORTEX)
+ && tc59x_probe(dev)
+#endif
+#if defined(CONFIG_SEEQ8005)
+ && seeq8005_probe(dev)
+#endif
+#if defined(CONFIG_HP100)
+ && hp100_probe(dev)
+#endif
+#if defined(CONFIG_ULTRA)
+ && ultra_probe(dev)
+#endif
+#if defined(CONFIG_WD80x3) || defined(WD80x3)
+
+ && wd_probe(dev)
+#endif
+#if defined(CONFIG_EL2) || defined(EL2) /* 3c503 */
+ && el2_probe(dev)
+#endif
+#if defined(CONFIG_HPLAN) || defined(HPLAN)
+ && hp_probe(dev)
+#endif
+#if 0
+#if defined(CONFIG_HPLAN_PLUS)
+ && hp_plus_probe(dev)
+#endif
+#endif
+#ifdef CONFIG_AC3200 /* Ansel Communications EISA 3200. */
+ && ac3200_probe(dev)
+#endif
+#ifdef CONFIG_E2100 /* Cabletron E21xx series. */
+ && e2100_probe(dev)
+#endif
+#if defined(CONFIG_NE2000) || defined(NE2000)
+ && ne_probe(dev)
+#endif
+#ifdef CONFIG_AT1500
+ && at1500_probe(dev)
+#endif
+#ifdef CONFIG_AT1700
+ && at1700_probe(dev)
+#endif
+#ifdef CONFIG_ETH16I
+ && eth16i_probe(dev) /* ICL EtherTeam 16i/32 */
+#endif
+#ifdef CONFIG_EL3 /* 3c509 */
+ && el3_probe(dev)
+#endif
+#ifdef CONFIG_ZNET /* Zenith Z-Note and some IBM Thinkpads. */
+ && znet_probe(dev)
+#endif
+#ifdef CONFIG_EEXPRESS /* Intel EtherExpress */
+ && express_probe(dev)
+#endif
+#ifdef CONFIG_EEXPRESS_PRO /* Intel EtherExpress Pro/10 */
+ && eepro_probe(dev)
+#endif
+#ifdef CONFIG_DEPCA /* DEC DEPCA */
+ && depca_probe(dev)
+#endif
+#ifdef CONFIG_EWRK3 /* DEC EtherWORKS 3 */
+ && ewrk3_probe(dev)
+#endif
+#ifdef CONFIG_DE4X5 /* DEC DE425, DE434, DE435 adapters */
+ && de4x5_probe(dev)
+#endif
+#ifdef CONFIG_APRICOT /* Apricot I82596 */
+ && apricot_probe(dev)
+#endif
+#ifdef CONFIG_EL1 /* 3c501 */
+ && el1_probe(dev)
+#endif
+#if defined(CONFIG_WAVELAN) /* WaveLAN */
+ && wavelan_probe(dev)
+#endif /* defined(CONFIG_WAVELAN) */
+#ifdef CONFIG_EL16 /* 3c507 */
+ && el16_probe(dev)
+#endif
+#ifdef CONFIG_ELPLUS /* 3c505 */
+ && elplus_probe(dev)
+#endif
+#ifdef CONFIG_DE600 /* D-Link DE-600 adapter */
+ && de600_probe(dev)
+#endif
+#ifdef CONFIG_DE620 /* D-Link DE-620 adapter */
+ && de620_probe(dev)
+#endif
+#if defined(CONFIG_SK_G16)
+ && SK_init(dev)
+#endif
+#ifdef CONFIG_NI52
+ && ni52_probe(dev)
+#endif
+#ifdef CONFIG_NI65
+ && ni65_probe(dev)
+#endif
+ && 1 ) {
+ return 1; /* -ENODEV or -EAGAIN would be more accurate. */
+ }
+ return 0;
+}
+
+
+#ifdef CONFIG_NETROM
+ extern int nr_init(struct device *);
+
+ static struct device nr3_dev = { "nr3", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, nr_init, };
+ static struct device nr2_dev = { "nr2", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr3_dev, nr_init, };
+ static struct device nr1_dev = { "nr1", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr2_dev, nr_init, };
+ static struct device nr0_dev = { "nr0", 0, 0, 0, 0, 0, 0, 0, 0, 0, &nr1_dev, nr_init, };
+
+# undef NEXT_DEV
+# define NEXT_DEV (&nr0_dev)
+#endif
+
+/* Run-time ATtachable (Pocket) devices have a different (not "eth#") name. */
+#ifdef CONFIG_ATP /* AT-LAN-TEC (RealTek) pocket adaptor. */
+static struct device atp_dev = {
+ "atp0", 0, 0, 0, 0, 0, 0, 0, 0, 0, NEXT_DEV, atp_init, /* ... */ };
+# undef NEXT_DEV
+# define NEXT_DEV (&atp_dev)
+#endif
+
+#ifdef CONFIG_ARCNET
+ extern int arcnet_probe(struct device *dev);
+ static struct device arcnet_dev = {
+ "arc0", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, arcnet_probe, };
+# undef NEXT_DEV
+# define NEXT_DEV (&arcnet_dev)
+#endif
+
+/* In Mach, by default allow at least 2 interfaces. */
+#ifdef MACH
+#ifndef ETH1_ADDR
+# define ETH1_ADDR 0
+#endif
+#ifndef ETH1_IRQ
+# define ETH1_IRQ 0
+#endif
+#endif
+
+/* The first device defaults to I/O base '0', which means autoprobe. */
+#ifndef ETH0_ADDR
+# define ETH0_ADDR 0
+#endif
+#ifndef ETH0_IRQ
+# define ETH0_IRQ 0
+#endif
+/* "eth0" defaults to autoprobe (== 0), other use a base of 0xffe0 (== -0x20),
+ which means "don't probe". These entries exist to only to provide empty
+ slots which may be enabled at boot-time. */
+
+static struct device eth3_dev = {
+ "eth3", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, NEXT_DEV, ethif_probe };
+static struct device eth2_dev = {
+ "eth2", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, &eth3_dev, ethif_probe };
+#ifdef MACH
+static struct device eth1_dev = {
+ "eth1", 0,0,0,0,ETH1_ADDR, ETH1_IRQ,0,0,0, &eth2_dev, ethif_probe };
+#else
+static struct device eth1_dev = {
+ "eth1", 0,0,0,0,0xffe0 /* I/O base*/, 0,0,0,0, &eth2_dev, ethif_probe };
+#endif
+static struct device eth0_dev = {
+ "eth0", 0, 0, 0, 0, ETH0_ADDR, ETH0_IRQ, 0, 0, 0, &eth1_dev, ethif_probe };
+
+# undef NEXT_DEV
+# define NEXT_DEV (&eth0_dev)
+
+#if defined(PLIP) || defined(CONFIG_PLIP)
+ extern int plip_init(struct device *);
+ static struct device plip2_dev = {
+ "plip2", 0, 0, 0, 0, 0x278, 2, 0, 0, 0, NEXT_DEV, plip_init, };
+ static struct device plip1_dev = {
+ "plip1", 0, 0, 0, 0, 0x378, 7, 0, 0, 0, &plip2_dev, plip_init, };
+ static struct device plip0_dev = {
+ "plip0", 0, 0, 0, 0, 0x3BC, 5, 0, 0, 0, &plip1_dev, plip_init, };
+# undef NEXT_DEV
+# define NEXT_DEV (&plip0_dev)
+#endif /* PLIP */
+
+#if defined(SLIP) || defined(CONFIG_SLIP)
+ /* To be exact, this node just hooks the initialization
+ routines to the device structures. */
+extern int slip_init_ctrl_dev(struct device *);
+static struct device slip_bootstrap = {
+ "slip_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, slip_init_ctrl_dev, };
+#undef NEXT_DEV
+#define NEXT_DEV (&slip_bootstrap)
+#endif /* SLIP */
+
+#if defined(CONFIG_PPP)
+extern int ppp_init(struct device *);
+static struct device ppp_bootstrap = {
+ "ppp_proto", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, ppp_init, };
+#undef NEXT_DEV
+#define NEXT_DEV (&ppp_bootstrap)
+#endif /* PPP */
+
+#ifdef CONFIG_DUMMY
+ extern int dummy_init(struct device *dev);
+ static struct device dummy_dev = {
+ "dummy", 0x0, 0x0, 0x0, 0x0, 0, 0, 0, 0, 0, NEXT_DEV, dummy_init, };
+# undef NEXT_DEV
+# define NEXT_DEV (&dummy_dev)
+#endif
+
+#ifdef CONFIG_EQUALIZER
+extern int eql_init(struct device *dev);
+struct device eql_dev = {
+ "eql", /* Master device for IP traffic load
+ balancing */
+ 0x0, 0x0, 0x0, 0x0, /* recv end/start; mem end/start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ eql_init /* set up the rest */
+};
+# undef NEXT_DEV
+# define NEXT_DEV (&eql_dev)
+#endif
+
+#ifdef CONFIG_IBMTR
+
+ extern int tok_probe(struct device *dev);
+ static struct device ibmtr_dev1 = {
+ "tr1", /* IBM Token Ring (Non-DMA) Interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0xa24, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tok_probe /* ??? Token_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&ibmtr_dev1)
+
+
+ static struct device ibmtr_dev0 = {
+ "tr0", /* IBM Token Ring (Non-DMA) Interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0xa20, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tok_probe /* ??? Token_init should set up the rest */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&ibmtr_dev0)
+
+#endif
+#ifdef CONFIG_NET_IPIP
+#ifdef CONFIG_IP_FORWARD
+ extern int tunnel_init(struct device *);
+
+ static struct device tunnel_dev1 =
+ {
+ "tunl1", /* IPIP tunnel */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ tunnel_init /* Fill in the details */
+ };
+
+ static struct device tunnel_dev0 =
+ {
+ "tunl0", /* IPIP tunnel */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0x0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ &tunnel_dev1, /* next device */
+ tunnel_init /* Fill in the details */
+ };
+# undef NEXT_DEV
+# define NEXT_DEV (&tunnel_dev0)
+
+#endif
+#endif
+
+#ifdef MACH
+struct device *dev_base = &eth0_dev;
+#else
+extern int loopback_init(struct device *dev);
+struct device loopback_dev = {
+ "lo", /* Software Loopback interface */
+ 0x0, /* recv memory end */
+ 0x0, /* recv memory start */
+ 0x0, /* memory end */
+ 0x0, /* memory start */
+ 0, /* base I/O address */
+ 0, /* IRQ */
+ 0, 0, 0, /* flags */
+ NEXT_DEV, /* next device */
+ loopback_init /* loopback_init should set up the rest */
+};
+
+struct device *dev_base = &loopback_dev;
+#endif
diff --git a/i386/i386at/gpl/linux/net/ac3200.c b/i386/i386at/gpl/linux/net/ac3200.c
new file mode 100644
index 00000000..054af13a
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ac3200.c
@@ -0,0 +1,385 @@
+/* ac3200.c: A driver for the Ansel Communications EISA ethernet adaptor. */
+/*
+ Written 1993, 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is driver for the Ansel Communications Model 3200 EISA Ethernet LAN
+ Adapter. The programming information is from the users manual, as related
+ by glee@ardnassak.math.clemson.edu.
+ */
+
+static const char *version =
+ "ac3200.c:v1.01 7/1/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+/* Offsets from the base address. */
+#define AC_NIC_BASE 0x00
+#define AC_SA_PROM 0x16 /* The station address PROM. */
+#define AC_ADDR0 0x00 /* Prefix station address values. */
+#define AC_ADDR1 0x40 /* !!!!These are just guesses!!!! */
+#define AC_ADDR2 0x90
+#define AC_ID_PORT 0xC80
+#define AC_EISA_ID 0x0110d305
+#define AC_RESET_PORT 0xC84
+#define AC_RESET 0x00
+#define AC_ENABLE 0x01
+#define AC_CONFIG 0xC90 /* The configuration port. */
+
+#define AC_IO_EXTENT 0x10 /* IS THIS REALLY TRUE ??? */
+ /* Actually accessed is:
+ * AC_NIC_BASE (0-15)
+ * AC_SA_PROM (0-5)
+ * AC_ID_PORT (0-3)
+ * AC_RESET_PORT
+ * AC_CONFIG
+ */
+
+/* Decoding of the configuration register. */
+static unsigned char config2irqmap[8] = {15, 12, 11, 10, 9, 7, 5, 3};
+static int addrmap[8] =
+{0xFF0000, 0xFE0000, 0xFD0000, 0xFFF0000, 0xFFE0000, 0xFFC0000, 0xD0000, 0 };
+static const char *port_name[4] = { "10baseT", "invalid", "AUI", "10base2"};
+
+#define config2irq(configval) config2irqmap[((configval) >> 3) & 7]
+#define config2mem(configval) addrmap[(configval) & 7]
+#define config2name(configval) port_name[((configval) >> 6) & 3]
+
+/* First and last 8390 pages. */
+#define AC_START_PG 0x00 /* First page of 8390 TX buffer */
+#define AC_STOP_PG 0x80 /* Last page +1 of the 8390 RX ring */
+
+int ac3200_probe(struct device *dev);
+static int ac_probe1(int ioaddr, struct device *dev);
+
+static int ac_open(struct device *dev);
+static void ac_reset_8390(struct device *dev);
+static void ac_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ac_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+static void ac_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+static int ac_close_card(struct device *dev);
+
+
+/* Probe for the AC3200.
+
+ The AC3200 can be identified by either the EISA configuration registers,
+ or the unique value in the station address PROM.
+ */
+
+int ac3200_probe(struct device *dev)
+{
+ unsigned short ioaddr = dev->base_addr;
+
+ if (ioaddr > 0x1ff) /* Check a single specified location. */
+ return ac_probe1(ioaddr, dev);
+ else if (ioaddr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ /* If you have a pre 0.99pl15 machine you should delete this line. */
+ if ( ! EISA_bus)
+ return ENXIO;
+
+ for (ioaddr = 0x1000; ioaddr < 0x9000; ioaddr += 0x1000) {
+ if (check_region(ioaddr, AC_IO_EXTENT))
+ continue;
+ if (ac_probe1(ioaddr, dev) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+static int ac_probe1(int ioaddr, struct device *dev)
+{
+ int i;
+
+#ifndef final_version
+ printk("AC3200 ethercard probe at %#3x:", ioaddr);
+
+ for(i = 0; i < 6; i++)
+ printk(" %02x", inb(ioaddr + AC_SA_PROM + i));
+#endif
+
+ /* !!!!The values of AC_ADDRn (see above) should be corrected when we
+ find out the correct station address prefix!!!! */
+ if (inb(ioaddr + AC_SA_PROM + 0) != AC_ADDR0
+ || inb(ioaddr + AC_SA_PROM + 1) != AC_ADDR1
+ || inb(ioaddr + AC_SA_PROM + 2) != AC_ADDR2 ) {
+#ifndef final_version
+ printk(" not found (invalid prefix).\n");
+#endif
+ return ENODEV;
+ }
+
+ /* The correct probe method is to check the EISA ID. */
+ for (i = 0; i < 4; i++)
+ if (inl(ioaddr + AC_ID_PORT) != AC_EISA_ID) {
+ printk("EISA ID mismatch, %8x vs %8x.\n",
+ inl(ioaddr + AC_EISA_ID), AC_EISA_ID);
+ return ENODEV;
+ }
+
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("ac3200.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ dev->dev_addr[i] = inb(ioaddr + AC_SA_PROM + i);
+
+#ifndef final_version
+ printk("\nAC3200 ethercard configuration register is %#02x,"
+ " EISA ID %02x %02x %02x %02x.\n", inb(ioaddr + AC_CONFIG),
+ inb(ioaddr + AC_ID_PORT + 0), inb(ioaddr + AC_ID_PORT + 1),
+ inb(ioaddr + AC_ID_PORT + 2), inb(ioaddr + AC_ID_PORT + 3));
+#endif
+
+ /* Assign and allocate the interrupt now. */
+ if (dev->irq == 0)
+ dev->irq = config2irq(inb(ioaddr + AC_CONFIG));
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "ac3200")) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return EAGAIN;
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to allocate memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ request_region(ioaddr, AC_IO_EXTENT, "ac3200");
+
+ dev->base_addr = ioaddr;
+
+#ifdef notyet
+ if (dev->mem_start) { /* Override the value from the board. */
+ for (i = 0; i < 7; i++)
+ if (addrmap[i] == dev->mem_start)
+ break;
+ if (i >= 7)
+ i = 0;
+ outb((inb(ioaddr + AC_CONFIG) & ~7) | i, ioaddr + AC_CONFIG);
+ }
+#endif
+
+ dev->if_port = inb(ioaddr + AC_CONFIG) >> 6;
+ dev->mem_start = config2mem(inb(ioaddr + AC_CONFIG));
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start
+ + (AC_STOP_PG - AC_START_PG)*256;
+
+ ei_status.name = "AC3200";
+ ei_status.tx_start_page = AC_START_PG;
+ ei_status.rx_start_page = AC_START_PG + TX_PAGES;
+ ei_status.stop_page = AC_STOP_PG;
+ ei_status.word16 = 1;
+
+ printk("\n%s: AC3200 at %#x, IRQ %d, %s port, shared memory %#lx-%#lx.\n",
+ dev->name, ioaddr, dev->irq, port_name[dev->if_port],
+ dev->mem_start, dev->mem_end-1);
+
+ if (ei_debug > 0)
+ printk(version);
+
+ ei_status.reset_8390 = &ac_reset_8390;
+ ei_status.block_input = &ac_block_input;
+ ei_status.block_output = &ac_block_output;
+ ei_status.get_8390_hdr = &ac_get_8390_hdr;
+
+ dev->open = &ac_open;
+ dev->stop = &ac_close_card;
+ NS8390_init(dev, 0);
+ return 0;
+}
+
+static int ac_open(struct device *dev)
+{
+#ifdef notyet
+ /* Someday we may enable the IRQ and shared memory here. */
+ int ioaddr = dev->base_addr;
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "ac3200"))
+ return -EAGAIN;
+#endif
+
+ ei_open(dev);
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static void ac_reset_8390(struct device *dev)
+{
+ ushort ioaddr = dev->base_addr;
+
+ outb(AC_RESET, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("resetting AC3200, t=%ld...", jiffies);
+
+ ei_status.txing = 0;
+ outb(AC_ENABLE, ioaddr + AC_RESET_PORT);
+ if (ei_debug > 1) printk("reset done\n");
+
+ return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ac_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ unsigned long hdr_start = dev->mem_start + ((ring_page - AC_START_PG)<<8);
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void ac_block_input(struct device *dev, int count, struct sk_buff *skb,
+ int ring_offset)
+{
+ unsigned long xfer_start = dev->mem_start + ring_offset - (AC_START_PG<<8);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, xfer_start, count, 0);
+ }
+}
+
+static void ac_block_output(struct device *dev, int count,
+ const unsigned char *buf, int start_page)
+{
+ unsigned long shmem = dev->mem_start + ((start_page - AC_START_PG)<<8);
+
+ memcpy_toio(shmem, buf, count);
+}
+
+static int ac_close_card(struct device *dev)
+{
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+#ifdef notyet
+ /* We should someday disable shared memory and interrupts. */
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+#endif
+
+ ei_close(dev);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef MODULE
+#define MAX_AC32_CARDS 4 /* Max number of AC32 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_AC32_CARDS] = { 0, };
+static struct device dev_ac32[MAX_AC32_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_AC32_CARDS] = { 0, };
+static int irq[MAX_AC32_CARDS] = { 0, };
+static int mem[MAX_AC32_CARDS] = { 0, };
+
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+ struct device *dev = &dev_ac32[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev]; /* Currently ignored by driver */
+ dev->init = ac3200_probe;
+ /* Default is to only install one card. */
+ if (io[this_dev] == 0 && this_dev != 0) break;
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "ac3200.c: No ac3200 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_AC32_CARDS; this_dev++) {
+ struct device *dev = &dev_ac32[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ /* Someday free_irq + irq2dev may be in ac_close_card() */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(dev->base_addr, AC_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c ac3200.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/apricot.c b/i386/i386at/gpl/linux/net/apricot.c
new file mode 100644
index 00000000..130d7759
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/apricot.c
@@ -0,0 +1,1046 @@
+/* apricot.c: An Apricot 82596 ethernet driver for linux. */
+/*
+ Apricot
+ Written 1994 by Mark Evans.
+ This driver is for the Apricot 82596 bus-master interface
+
+ Modularised 12/94 Mark Evans
+
+ Driver skeleton
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+
+*/
+
+static const char *version = "apricot.c:v0.2 05/12/94\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(addr, size) 0
+#define request_region(addr, size,name) do ; while(0)
+#endif
+
+#ifndef HAVE_ALLOC_SKB
+#define alloc_skb(size, priority) (struct sk_buff *) kmalloc(size,priority)
+#define kfree_skbmem(buff, size) kfree_s(buff,size)
+#endif
+
+#define APRICOT_DEBUG 1
+
+#ifdef APRICOT_DEBUG
+int i596_debug = APRICOT_DEBUG;
+#else
+int i596_debug = 1;
+#endif
+
+#define APRICOT_TOTAL_SIZE 17
+
+#define I596_NULL -1
+
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+#define CMD_FLEX 0x0008 /* Enable flexible memory model */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+#define STAT_C 0x8000 /* Set to 0 after execution */
+#define STAT_B 0x4000 /* Command being executed */
+#define STAT_OK 0x2000 /* Command executed ok */
+#define STAT_A 0x1000 /* Command aborted */
+
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define CUC_ABORT 0x0400
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define RX_ABORT 0x0040
+
+struct i596_cmd {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *next;
+};
+
+#define EOF 0x8000
+#define SIZE_MASK 0x3fff
+
+struct i596_tbd {
+ unsigned short size;
+ unsigned short pad;
+ struct i596_tbd *next;
+ char *data;
+};
+
+struct tx_cmd {
+ struct i596_cmd cmd;
+ struct i596_tbd *tbd;
+ unsigned short size;
+ unsigned short pad;
+};
+
+struct i596_rfd {
+ unsigned short stat;
+ unsigned short cmd;
+ struct i596_rfd *next;
+ long rbd;
+ unsigned short count;
+ unsigned short size;
+ char data[1532];
+};
+
+#define RX_RING_SIZE 8
+
+struct i596_scb {
+ unsigned short status;
+ unsigned short command;
+ struct i596_cmd *cmd;
+ struct i596_rfd *rfd;
+ unsigned long crc_err;
+ unsigned long align_err;
+ unsigned long resource_err;
+ unsigned long over_err;
+ unsigned long rcvdt_err;
+ unsigned long short_err;
+ unsigned short t_on;
+ unsigned short t_off;
+};
+
+struct i596_iscp {
+ unsigned long stat;
+ struct i596_scb *scb;
+};
+
+struct i596_scp {
+ unsigned long sysbus;
+ unsigned long pad;
+ struct i596_iscp *iscp;
+};
+
+struct i596_private {
+ struct i596_scp scp;
+ struct i596_iscp iscp;
+ struct i596_scb scb;
+ struct i596_cmd set_add;
+ char eth_addr[8];
+ struct i596_cmd set_conf;
+ char i596_config[16];
+ struct i596_cmd tdr;
+ unsigned long stat;
+ int last_restart;
+ struct i596_rfd *rx_tail;
+ struct i596_cmd *cmd_tail;
+ struct i596_cmd *cmd_head;
+ int cmd_backlog;
+ unsigned long last_cmd;
+ struct enet_statistics stats;
+};
+
+char init_setup[] = {
+ 0x8E, /* length, prefetch on */
+ 0xC8, /* fifo to 8, monitor off */
+ 0x80, /* don't save bad frames */
+ 0x2E, /* No source address insertion, 8 byte preamble */
+ 0x00, /* priority and backoff defaults */
+ 0x60, /* interframe spacing */
+ 0x00, /* slot time LSB */
+ 0xf2, /* slot time and retries */
+ 0x00, /* promiscuous mode */
+ 0x00, /* collision detect */
+ 0x40, /* minimum frame length */
+ 0xff,
+ 0x00,
+ 0x7f /* *multi IA */ };
+
+static int i596_open(struct device *dev);
+static int i596_start_xmit(struct sk_buff *skb, struct device *dev);
+static void i596_interrupt(int irq, struct pt_regs *regs);
+static int i596_close(struct device *dev);
+static struct enet_statistics *i596_get_stats(struct device *dev);
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd);
+static void print_eth(char *);
+static void set_multicast_list(struct device *dev);
+
+
+static inline int
+init_rx_bufs(struct device *dev, int num)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int i;
+ struct i596_rfd *rfd;
+
+ lp->scb.rfd = (struct i596_rfd *)I596_NULL;
+
+ if (i596_debug > 1) printk ("%s: init_rx_bufs %d.\n", dev->name, num);
+
+ for (i = 0; i < num; i++)
+ {
+ if (!(rfd = (struct i596_rfd *)kmalloc(sizeof(struct i596_rfd), GFP_KERNEL)))
+ break;
+
+ rfd->stat = 0x0000;
+ rfd->rbd = I596_NULL;
+ rfd->count = 0;
+ rfd->size = 1532;
+ if (i == 0)
+ {
+ rfd->cmd = CMD_EOL;
+ lp->rx_tail = rfd;
+ }
+ else
+ rfd->cmd = 0x0000;
+
+ rfd->next = lp->scb.rfd;
+ lp->scb.rfd = rfd;
+ }
+
+ if (i != 0)
+ lp->rx_tail->next = lp->scb.rfd;
+
+ return (i);
+}
+
+static inline void
+remove_rx_bufs(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ struct i596_rfd *rfd = lp->scb.rfd;
+
+ lp->rx_tail->next = (struct i596_rfd *)I596_NULL;
+
+ do
+ {
+ lp->scb.rfd = rfd->next;
+ kfree_s(rfd, sizeof(struct i596_rfd));
+ rfd = lp->scb.rfd;
+ }
+ while (rfd != lp->rx_tail);
+}
+
+static inline void
+init_i596_mem(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+ int boguscnt = 100;
+
+ /* change the scp address */
+ outw(0, ioaddr);
+ outw(0, ioaddr);
+ outb(4, ioaddr+0xf);
+ outw(((((int)&lp->scp) & 0xffff) | 2), ioaddr);
+ outw((((int)&lp->scp)>>16) & 0xffff, ioaddr);
+
+ lp->last_cmd = jiffies;
+
+ lp->scp.sysbus = 0x00440000;
+ lp->scp.iscp = &(lp->iscp);
+ lp->iscp.scb = &(lp->scb);
+ lp->iscp.stat = 0x0001;
+ lp->cmd_backlog = 0;
+
+ lp->cmd_head = lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+
+ if (i596_debug > 2) printk("%s: starting i82596.\n", dev->name);
+
+ (void) inb (ioaddr+0x10);
+ outb(4, ioaddr+0xf);
+ outw(0, ioaddr+4);
+
+ while (lp->iscp.stat)
+ if (--boguscnt == 0)
+ {
+ printk("%s: i82596 initialization timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.command = 0;
+
+ memcpy (lp->i596_config, init_setup, 14);
+ lp->set_conf.command = CmdConfigure;
+ i596_add_cmd(dev, &lp->set_conf);
+
+ memcpy (lp->eth_addr, dev->dev_addr, 6);
+ lp->set_add.command = CmdSASetup;
+ i596_add_cmd(dev, &lp->set_add);
+
+ lp->tdr.command = CmdTDR;
+ i596_add_cmd(dev, &lp->tdr);
+
+ boguscnt = 200;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: receive unit start timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.command = RX_START;
+ outw(0, ioaddr+4);
+
+ boguscnt = 200;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i82596 init timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ return;
+}
+
+static inline int
+i596_rx(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int frames = 0;
+
+ if (i596_debug > 3) printk ("i596_rx()\n");
+
+ while ((lp->scb.rfd->stat) & STAT_C)
+ {
+ if (i596_debug >2) print_eth(lp->scb.rfd->data);
+
+ if ((lp->scb.rfd->stat) & STAT_OK)
+ {
+ /* a good frame */
+ int pkt_len = lp->scb.rfd->count & 0x3fff;
+ struct sk_buff *skb = dev_alloc_skb(pkt_len);
+
+ frames++;
+
+ if (skb == NULL)
+ {
+ printk ("%s: i596_rx Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb->dev = dev;
+ memcpy(skb_put(skb,pkt_len), lp->scb.rfd->data, pkt_len);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+
+ if (i596_debug > 4) print_eth(skb->data);
+ }
+ else
+ {
+ lp->stats.rx_errors++;
+ if ((lp->scb.rfd->stat) & 0x0001) lp->stats.collisions++;
+ if ((lp->scb.rfd->stat) & 0x0080) lp->stats.rx_length_errors++;
+ if ((lp->scb.rfd->stat) & 0x0100) lp->stats.rx_over_errors++;
+ if ((lp->scb.rfd->stat) & 0x0200) lp->stats.rx_fifo_errors++;
+ if ((lp->scb.rfd->stat) & 0x0400) lp->stats.rx_frame_errors++;
+ if ((lp->scb.rfd->stat) & 0x0800) lp->stats.rx_crc_errors++;
+ if ((lp->scb.rfd->stat) & 0x1000) lp->stats.rx_length_errors++;
+ }
+
+ lp->scb.rfd->stat = 0;
+ lp->rx_tail->cmd = 0;
+ lp->rx_tail = lp->scb.rfd;
+ lp->scb.rfd = lp->scb.rfd->next;
+ lp->rx_tail->count = 0;
+ lp->rx_tail->cmd = CMD_EOL;
+
+ }
+
+ if (i596_debug > 3) printk ("frames %d\n", frames);
+
+ return 0;
+}
+
+static inline void
+i596_cleanup_cmd(struct i596_private *lp)
+{
+ struct i596_cmd *ptr;
+ int boguscnt = 100;
+
+ if (i596_debug > 4) printk ("i596_cleanup_cmd\n");
+
+ while (lp->cmd_head != (struct i596_cmd *) I596_NULL)
+ {
+ ptr = lp->cmd_head;
+
+ lp->cmd_head = lp->cmd_head->next;
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7)
+ {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = ((struct sk_buff *)(tx_cmd->tbd->data)) -1;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ lp->stats.tx_errors++;
+ lp->stats.tx_aborted_errors++;
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd)));
+ break;
+ }
+ case CmdMulticastList:
+ {
+ unsigned short count = *((unsigned short *) (ptr + 1));
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2));
+ break;
+ }
+ default:
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ }
+ }
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_cleanup_cmd timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.cmd = lp->cmd_head;
+}
+
+static inline void
+i596_reset(struct device *dev, struct i596_private *lp, int ioaddr)
+{
+ int boguscnt = 100;
+
+ if (i596_debug > 4) printk ("i596_reset\n");
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_reset timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ lp->scb.command = CUC_ABORT|RX_ABORT;
+ outw(0, ioaddr+4);
+
+ /* wait for shutdown */
+ boguscnt = 400;
+
+ while ((lp->scb.status, lp->scb.command) || lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_reset 2 timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ i596_cleanup_cmd(lp);
+ i596_rx(dev);
+
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ init_i596_mem(dev);
+}
+
+static void i596_add_cmd(struct device *dev, struct i596_cmd *cmd)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ unsigned long flags;
+ int boguscnt = 100;
+
+ if (i596_debug > 4) printk ("i596_add_cmd\n");
+
+ cmd->status = 0;
+ cmd->command |= (CMD_EOL|CMD_INTR);
+ cmd->next = (struct i596_cmd *) I596_NULL;
+
+ save_flags(flags);
+ cli();
+ if (lp->cmd_head != (struct i596_cmd *) I596_NULL)
+ lp->cmd_tail->next = cmd;
+ else
+ {
+ lp->cmd_head = cmd;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("i596_add_cmd timed out with status %4.4x, cmd %4.4x.\n",
+ lp->scb.status, lp->scb.command);
+ break;
+ }
+
+ lp->scb.cmd = cmd;
+ lp->scb.command = CUC_START;
+ outw (0, ioaddr+4);
+ }
+ lp->cmd_tail = cmd;
+ lp->cmd_backlog++;
+
+ lp->cmd_head = lp->scb.cmd;
+ restore_flags(flags);
+
+ if (lp->cmd_backlog > 16)
+ {
+ int tickssofar = jiffies - lp->last_cmd;
+
+ if (tickssofar < 25) return;
+
+ printk("%s: command unit timed out, status resetting.\n", dev->name);
+
+ i596_reset(dev, lp, ioaddr);
+ }
+}
+
+static int
+i596_open(struct device *dev)
+{
+ int i;
+
+ if (i596_debug > 1)
+ printk("%s: i596_open() irq %d.\n", dev->name, dev->irq);
+
+ if (request_irq(dev->irq, &i596_interrupt, 0, "apricot"))
+ return -EAGAIN;
+
+ irq2dev_map[dev->irq] = dev;
+
+ i = init_rx_bufs(dev, RX_RING_SIZE);
+
+ if ((i = init_rx_bufs(dev, RX_RING_SIZE)) < RX_RING_SIZE)
+ printk("%s: only able to allocate %d receive buffers\n", dev->name, i);
+
+ if (i < 4)
+ {
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+ return -EAGAIN;
+ }
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ MOD_INC_USE_COUNT;
+
+ /* Initialize the 82596 memory */
+ init_i596_mem(dev);
+
+ return 0; /* Always succeed */
+}
+
+static int
+i596_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ struct tx_cmd *tx_cmd;
+
+ if (i596_debug > 2) printk ("%s: Apricot start xmit\n", dev->name);
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, status resetting.\n",
+ dev->name);
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (i596_debug > 1) printk ("Resetting board.\n");
+
+ /* Shutdown and restart */
+ i596_reset(dev,lp, ioaddr);
+ } else {
+ /* Issue a channel attention signal */
+ if (i596_debug > 1) printk ("Kicking board.\n");
+
+ lp->scb.command = CUC_START|RX_START;
+ outw(0, ioaddr+4);
+
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher level thinks we've misses a tx-done interrupt
+ we are passed NULL. n.b. dev_tint handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* shouldn't happen */
+ if (skb->len <= 0) return 0;
+
+ if (i596_debug > 3) printk("%s: i596_start_xmit() called\n", dev->name);
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else
+ {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ dev->trans_start = jiffies;
+
+ tx_cmd = (struct tx_cmd *) kmalloc ((sizeof (struct tx_cmd) + sizeof (struct i596_tbd)), GFP_ATOMIC);
+ if (tx_cmd == NULL)
+ {
+ printk ("%s: i596_xmit Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.tx_dropped++;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ else
+ {
+ tx_cmd->tbd = (struct i596_tbd *) (tx_cmd + 1);
+ tx_cmd->tbd->next = (struct i596_tbd *) I596_NULL;
+
+ tx_cmd->cmd.command = CMD_FLEX|CmdTx;
+
+ tx_cmd->pad = 0;
+ tx_cmd->size = 0;
+ tx_cmd->tbd->pad = 0;
+ tx_cmd->tbd->size = EOF | length;
+
+ tx_cmd->tbd->data = skb->data;
+
+ if (i596_debug > 3) print_eth(skb->data);
+
+ i596_add_cmd(dev, (struct i596_cmd *)tx_cmd);
+
+ lp->stats.tx_packets++;
+ }
+ }
+
+ dev->tbusy = 0;
+
+ return 0;
+}
+
+
+static void print_eth(char *add)
+{
+ int i;
+
+ printk ("Dest ");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char)add[i]);
+ printk ("\n");
+
+ printk ("Source");
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", (unsigned char)add[i+6]);
+ printk ("\n");
+ printk ("type %2.2X%2.2X\n", (unsigned char)add[12], (unsigned char)add[13]);
+}
+
+int apricot_probe(struct device *dev)
+{
+ int i;
+ struct i596_private *lp;
+ int checksum = 0;
+ int ioaddr = 0x300;
+ char eth_addr[6];
+
+ /* this is easy the ethernet interface can only be at 0x300 */
+ /* first check nothing is already registered here */
+
+ if (check_region(ioaddr, APRICOT_TOTAL_SIZE))
+ return ENODEV;
+
+ for (i = 0; i < 8; i++)
+ {
+ eth_addr[i] = inb(ioaddr+8+i);
+ checksum += eth_addr[i];
+ }
+
+ /* checksum is a multiple of 0x100, got this wrong first time
+ some machines have 0x100, some 0x200. The DOS driver doesn't
+ even bother with the checksum */
+
+ if (checksum % 0x100) return ENODEV;
+
+ /* Some other boards trip the checksum.. but then appear as ether
+ address 0. Trap these - AC */
+
+ if(memcmp(eth_addr,"\x00\x00\x49",3)!= 0)
+ return ENODEV;
+
+ request_region(ioaddr, APRICOT_TOTAL_SIZE, "apricot");
+
+ dev->base_addr = ioaddr;
+ ether_setup(dev);
+ printk("%s: Apricot 82596 at %#3x,", dev->name, ioaddr);
+
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = eth_addr[i]);
+
+ dev->base_addr = ioaddr;
+ dev->irq = 10;
+ printk(" IRQ %d.\n", dev->irq);
+
+ if (i596_debug > 0) printk(version);
+
+ /* The APRICOT-specific entries in the device structure. */
+ dev->open = &i596_open;
+ dev->stop = &i596_close;
+ dev->hard_start_xmit = &i596_start_xmit;
+ dev->get_stats = &i596_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ dev->mem_start = (int)kmalloc(sizeof(struct i596_private)+ 0x0f, GFP_KERNEL);
+ /* align for scp */
+ dev->priv = (void *)((dev->mem_start + 0xf) & 0xfffffff0);
+
+ lp = (struct i596_private *)dev->priv;
+ memset((void *)lp, 0, sizeof(struct i596_private));
+ lp->scb.command = 0;
+ lp->scb.cmd = (struct i596_cmd *) I596_NULL;
+ lp->scb.rfd = (struct i596_rfd *)I596_NULL;
+
+ return 0;
+}
+
+static void
+i596_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct i596_private *lp;
+ short ioaddr;
+ int boguscnt = 200;
+ unsigned short status, ack_cmd = 0;
+
+ if (dev == NULL) {
+ printk ("i596_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (i596_debug > 3) printk ("%s: i596_interrupt(): irq %d\n",dev->name, irq);
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+
+ lp = (struct i596_private *)dev->priv;
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ status = lp->scb.status;
+
+ if (i596_debug > 4)
+ printk("%s: i596 interrupt, status %4.4x.\n", dev->name, status);
+
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x8000) || (status & 0x2000))
+ {
+ struct i596_cmd *ptr;
+
+ if ((i596_debug > 4) && (status & 0x8000))
+ printk("%s: i596 interrupt completed command.\n", dev->name);
+ if ((i596_debug > 4) && (status & 0x2000))
+ printk("%s: i596 interrupt command unit inactive %x.\n", dev->name, status & 0x0700);
+
+ while ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (lp->cmd_head->status & STAT_C))
+ {
+ ptr = lp->cmd_head;
+
+ lp->cmd_head = lp->cmd_head->next;
+ lp->cmd_backlog--;
+
+ switch ((ptr->command) & 0x7)
+ {
+ case CmdTx:
+ {
+ struct tx_cmd *tx_cmd = (struct tx_cmd *) ptr;
+ struct sk_buff *skb = ((struct sk_buff *)(tx_cmd->tbd->data)) -1;
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ if ((ptr->status) & STAT_OK)
+ {
+ if (i596_debug >2) print_eth(skb->data);
+ }
+ else
+ {
+ lp->stats.tx_errors++;
+ if ((ptr->status) & 0x0020) lp->stats.collisions++;
+ if (!((ptr->status) & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if ((ptr->status) & 0x0400) lp->stats.tx_carrier_errors++;
+ if ((ptr->status) & 0x0800) lp->stats.collisions++;
+ if ((ptr->status) & 0x1000) lp->stats.tx_aborted_errors++;
+ }
+
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)tx_cmd, (sizeof (struct tx_cmd) + sizeof (struct i596_tbd)));
+ break;
+ }
+ case CmdMulticastList:
+ {
+ unsigned short count = *((unsigned short *) (ptr + 1));
+
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+ kfree_s((unsigned char *)ptr, (sizeof (struct i596_cmd) + count + 2));
+ break;
+ }
+ case CmdTDR:
+ {
+ unsigned long status = *((unsigned long *) (ptr + 1));
+
+ if (status & 0x8000)
+ {
+ if (i596_debug > 3)
+ printk("%s: link ok.\n", dev->name);
+ }
+ else
+ {
+ if (status & 0x4000)
+ printk("%s: Transceiver problem.\n", dev->name);
+ if (status & 0x2000)
+ printk("%s: Termination problem.\n", dev->name);
+ if (status & 0x1000)
+ printk("%s: Short circuit.\n", dev->name);
+
+ printk("%s: Time %ld.\n", dev->name, status & 0x07ff);
+ }
+ }
+ default:
+ ptr->next = (struct i596_cmd * ) I596_NULL;
+
+ lp->last_cmd = jiffies;
+ }
+ }
+
+ ptr = lp->cmd_head;
+ while ((ptr != (struct i596_cmd *) I596_NULL) && (ptr != lp->cmd_tail))
+ {
+ ptr->command &= 0x1fff;
+ ptr = ptr->next;
+ }
+
+ if ((lp->cmd_head != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd |= CUC_START;
+ lp->scb.cmd = lp->cmd_head;
+ }
+
+ if ((status & 0x1000) || (status & 0x4000))
+ {
+ if ((i596_debug > 4) && (status & 0x4000))
+ printk("%s: i596 interrupt received a frame.\n", dev->name);
+ if ((i596_debug > 4) && (status & 0x1000))
+ printk("%s: i596 interrupt receive unit inactive %x.\n", dev->name, status & 0x0070);
+
+ i596_rx(dev);
+
+ if (dev->start) ack_cmd |= RX_START;
+ }
+
+ /* acknowledge the interrupt */
+
+/*
+ if ((lp->scb.cmd != (struct i596_cmd *) I596_NULL) && (dev->start)) ack_cmd | = CUC_START;
+*/
+ boguscnt = 100;
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: i596 interrupt, timeout status %4.4x command %4.4x.\n", dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ lp->scb.command = ack_cmd;
+
+ (void) inb (ioaddr+0x10);
+ outb (4, ioaddr+0xf);
+ outw (0, ioaddr+4);
+
+ if (i596_debug > 4)
+ printk("%s: exiting interrupt.\n", dev->name);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+i596_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ int boguscnt = 200;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (i596_debug > 1)
+ printk("%s: Shutting down ethercard, status was %4.4x.\n",
+ dev->name, lp->scb.status);
+
+ lp->scb.command = CUC_ABORT|RX_ABORT;
+ outw(0, ioaddr+4);
+
+ i596_cleanup_cmd(lp);
+
+ while (lp->scb.status, lp->scb.command)
+ if (--boguscnt == 0)
+ {
+ printk("%s: close timed timed out with status %4.4x, cmd %4.4x.\n",
+ dev->name, lp->scb.status, lp->scb.command);
+ break;
+ }
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+ remove_rx_bufs(dev);
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct enet_statistics *
+i596_get_stats(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ struct i596_private *lp = (struct i596_private *)dev->priv;
+ struct i596_cmd *cmd;
+
+ if (i596_debug > 1)
+ printk ("%s: set multicast list %d\n", dev->name, dev->mc_count);
+
+ if (dev->mc_count > 0)
+ {
+ struct dev_mc_list *dmi;
+ char *cp;
+ cmd = (struct i596_cmd *) kmalloc(sizeof(struct i596_cmd)+2+dev->mc_count*6, GFP_ATOMIC);
+ if (cmd == NULL)
+ {
+ printk ("%s: set_multicast Memory squeeze.\n", dev->name);
+ return;
+ }
+ cmd->command = CmdMulticastList;
+ *((unsigned short *) (cmd + 1)) = dev->mc_count * 6;
+ cp=((char *)(cmd + 1))+2;
+ for(dmi=dev->mc_list;dmi!=NULL;dmi=dmi->next)
+ {
+ memcpy(cp, dmi,6);
+ cp+=6;
+ }
+ print_eth (((char *)(cmd + 1)) + 2);
+ i596_add_cmd(dev, cmd);
+ }
+ else
+ {
+ if (lp->set_conf.next != (struct i596_cmd * ) I596_NULL)
+ return;
+ if (dev->mc_count == 0 && !(dev->flags&(IFF_PROMISC|IFF_ALLMULTI)))
+ {
+ if(dev->flags&IFF_ALLMULTI)
+ dev->flags|=IFF_PROMISC;
+ lp->i596_config[8] &= ~0x01;
+ }
+ else
+ lp->i596_config[8] |= 0x01;
+
+ i596_add_cmd(dev, &lp->set_conf);
+ }
+}
+
+#ifdef HAVE_DEVLIST
+static unsigned int apricot_portlist[] = {0x300, 0};
+struct netdev_entry apricot_drv =
+{"apricot", apricot_probe, APRICOT_TOTAL_SIZE, apricot_portlist};
+#endif
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_apricot = {
+ devicename, /* device name inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 10,
+ 0, 0, 0, NULL, apricot_probe };
+
+static int io = 0x300;
+static int irq = 10;
+
+int
+init_module(void)
+{
+ dev_apricot.base_addr = io;
+ dev_apricot.irq = irq;
+ if (register_netdev(&dev_apricot) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_apricot);
+ kfree_s((void *)dev_apricot.mem_start, sizeof(struct i596_private) + 0xf);
+ dev_apricot.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_apricot.base_addr, APRICOT_TOTAL_SIZE);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c apricot.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/at1700.c b/i386/i386at/gpl/linux/net/at1700.c
new file mode 100644
index 00000000..3d684c0a
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/at1700.c
@@ -0,0 +1,677 @@
+/* at1700.c: A network device driver for the Allied Telesis AT1700.
+
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a device driver for the Allied Telesis AT1700, which is a
+ straight-forward Fujitsu MB86965 implementation.
+
+ Sources:
+ The Fujitsu MB86965 datasheet.
+
+ After the initial version of this driver was written Gerry Sawkins of
+ ATI provided their EEPROM configuration code header file.
+ Thanks to NIIBE Yutaka <gniibe@mri.co.jp> for bug fixes.
+
+ Bugs:
+ The MB86965 has a design flaw that makes all probes unreliable. Not
+ only is it difficult to detect, it also moves around in I/O space in
+ response to inb()s from other device probes!
+*/
+
+static const char *version =
+ "at1700.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* This unusual address order is used to verify the CONFIG register. */
+static int at1700_probe_list[] =
+{0x260, 0x280, 0x2a0, 0x240, 0x340, 0x320, 0x380, 0x300, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+typedef unsigned char uchar;
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ uint tx_started:1; /* Number of packet on the Tx queue. */
+ uchar tx_queue; /* Number of packet on the Tx queue. */
+ ushort tx_queue_len; /* Current length of the Tx queue. */
+};
+
+
+/* Offsets from the base address. */
+#define STATUS 0
+#define TX_STATUS 0
+#define RX_STATUS 1
+#define TX_INTR 2 /* Bit-mapped interrupt enable registers. */
+#define RX_INTR 3
+#define TX_MODE 4
+#define RX_MODE 5
+#define CONFIG_0 6 /* Misc. configuration settings. */
+#define CONFIG_1 7
+/* Run-time register bank 2 definitions. */
+#define DATAPORT 8 /* Word-wide DMA or programmed-I/O dataport. */
+#define TX_START 10
+#define MODE13 13
+#define EEPROM_Ctrl 16
+#define EEPROM_Data 17
+#define IOCONFIG 19
+#define RESET 31 /* Write to reset some parts of the chip. */
+#define AT1700_IO_EXTENT 32
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x40 /* EEPROM shift clock, in reg. 16. */
+#define EE_CS 0x20 /* EEPROM chip select, in reg. 16. */
+#define EE_DATA_WRITE 0x80 /* EEPROM chip data in, in reg. 17. */
+#define EE_DATA_READ 0x80 /* EEPROM chip data out, in reg. 17. */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay() do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD (5 << 6)
+#define EE_READ_CMD (6 << 6)
+#define EE_ERASE_CMD (7 << 6)
+
+
+/* Index to functions, as function prototypes. */
+
+extern int at1700_probe(struct device *dev);
+
+static int at1700_probe1(struct device *dev, short ioaddr);
+static int read_eeprom(int ioaddr, int location);
+static int net_open(struct device *dev);
+static int net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, struct pt_regs *regs);
+static void net_rx(struct device *dev);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry at1700_drv =
+{"at1700", at1700_probe1, AT1700_IO_EXTENT, at1700_probe_list};
+#else
+int
+at1700_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return at1700_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; at1700_probe_list[i]; i++) {
+ int ioaddr = at1700_probe_list[i];
+ if (check_region(ioaddr, AT1700_IO_EXTENT))
+ continue;
+ if (at1700_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* The Fujitsu datasheet suggests that the NIC be probed for by checking its
+ "signature", the default bit pattern after a reset. This *doesn't* work --
+ there is no way to reset the bus interface without a complete power-cycle!
+
+ It turns out that ATI came to the same conclusion I did: the only thing
+ that can be done is checking a few bits and then diving right into an
+ EEPROM read. */
+
+int at1700_probe1(struct device *dev, short ioaddr)
+{
+ char irqmap[8] = {3, 4, 5, 9, 10, 11, 14, 15};
+ unsigned int i, irq;
+
+ /* Resetting the chip doesn't reset the ISA interface, so don't bother.
+ That means we have to be careful with the register values we probe for.
+ */
+#ifdef notdef
+ printk("at1700 probe at %#x, eeprom is %4.4x %4.4x %4.4x ctrl %4.4x.\n",
+ ioaddr, read_eeprom(ioaddr, 4), read_eeprom(ioaddr, 5),
+ read_eeprom(ioaddr, 6), inw(ioaddr + EEPROM_Ctrl));
+#endif
+ if (at1700_probe_list[inb(ioaddr + IOCONFIG) & 0x07] != ioaddr
+ || read_eeprom(ioaddr, 4) != 0x0000
+ || (read_eeprom(ioaddr, 5) & 0xff00) != 0xF400)
+ return -ENODEV;
+
+ /* Reset the internal state machines. */
+ outb(0, ioaddr + RESET);
+
+ irq = irqmap[(read_eeprom(ioaddr, 12)&0x04)
+ | (read_eeprom(ioaddr, 0)>>14)];
+
+ /* Snarf the interrupt vector now. */
+ if (request_irq(irq, &net_interrupt, 0, "at1700")) {
+ printk ("AT1700 found at %#3x, but it's unusable due to a conflict on"
+ "IRQ %d.\n", ioaddr, irq);
+ return EAGAIN;
+ }
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ /* Grab the region so that we can find another board if the IRQ request
+ fails. */
+ request_region(ioaddr, AT1700_IO_EXTENT, "at1700");
+
+ printk("%s: AT1700 found at %#3x, IRQ %d, address ", dev->name,
+ ioaddr, irq);
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+ irq2dev_map[irq] = dev;
+
+ for(i = 0; i < 3; i++) {
+ unsigned short eeprom_val = read_eeprom(ioaddr, 4+i);
+ printk("%04x", eeprom_val);
+ ((unsigned short *)dev->dev_addr)[i] = ntohs(eeprom_val);
+ }
+
+ /* The EEPROM word 12 bit 0x0400 means use regular 100 ohm 10baseT signals,
+ rather than 150 ohm shielded twisted pair compensation.
+ 0x0000 == auto-sense the interface
+ 0x0800 == use TP interface
+ 0x1800 == use coax interface
+ */
+ {
+ const char *porttype[] = {"auto-sense", "10baseT", "auto-sense", "10base2"};
+ ushort setup_value = read_eeprom(ioaddr, 12);
+
+ dev->if_port = setup_value >> 8;
+ printk(" %s interface.\n", porttype[(dev->if_port>>3) & 3]);
+ }
+
+ /* Set the station address in bank zero. */
+ outb(0xe0, ioaddr + 7);
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + 8 + i);
+
+ /* Switch to bank 1 and set the multicast table to accept none. */
+ outb(0xe4, ioaddr + 7);
+ for (i = 0; i < 8; i++)
+ outb(0x00, ioaddr + 8 + i);
+
+ /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+ bus access, two 4K Tx queues, and disabled Tx and Rx. */
+ outb(0xda, ioaddr + CONFIG_0);
+
+ /* Switch to bank 2 and lock our I/O address. */
+ outb(0xe8, ioaddr + 7);
+ outb(dev->if_port, MODE13);
+
+ /* Power-down the chip. Aren't we green! */
+ outb(0x00, ioaddr + CONFIG_1);
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of 'dev' with ethernet-generic values. */
+
+ ether_setup(dev);
+ return 0;
+}
+
+static int read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_Ctrl;
+ short ee_daddr = ioaddr + EEPROM_Data;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EE_CS;
+
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 9; i >= 0; i--) {
+ short dataval = (read_cmd & (1 << i)) ? EE_DATA_WRITE : 0;
+ outb(dataval, ee_daddr);
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(EE_CS, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(EE_CS, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(EE_CS | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_daddr) & EE_DATA_READ) ? 1 : 0);
+ outb(EE_CS, ee_addr);
+ eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EE_CS;
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ return retval;
+}
+
+
+
+static int net_open(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ /* Powerup the chip, initialize config register 1, and select bank 0. */
+ outb(0xe0, ioaddr + CONFIG_1);
+
+ /* Set the station address in bank zero. */
+ for (i = 0; i < 6; i++)
+ outb(dev->dev_addr[i], ioaddr + 8 + i);
+
+ /* Switch to bank 1 and set the multicast table to accept none. */
+ outb(0xe4, ioaddr + 7);
+ for (i = 0; i < 8; i++)
+ outb(0x00, ioaddr + 8 + i);
+
+ /* Set the configuration register 0 to 32K 100ns. byte-wide memory, 16 bit
+ bus access, and two 4K Tx queues. */
+ outb(0xda, ioaddr + CONFIG_0);
+
+ /* Same config 0, except enable the Rx and Tx. */
+ outb(0x5a, ioaddr + CONFIG_0);
+ /* Switch to register bank 2 for the run-time registers. */
+ outb(0xe8, ioaddr + CONFIG_1);
+
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
+ /* Turn on Rx interrupts, leave Tx interrupts off until packet Tx. */
+ outb(0x00, ioaddr + TX_INTR);
+ outb(0x81, ioaddr + RX_INTR);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ MOD_INC_USE_COUNT;
+
+ return 0;
+}
+
+static int
+net_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ printk("%s: transmit timed out with status %04x, %s?\n", dev->name,
+ inw(ioaddr + STATUS), inb(ioaddr + TX_STATUS) & 0x80
+ ? "IRQ conflict" : "network cable problem");
+ printk("%s: timeout registers: %04x %04x %04x %04x %04x %04x %04x %04x.\n",
+ dev->name, inw(ioaddr + 0), inw(ioaddr + 2), inw(ioaddr + 4),
+ inw(ioaddr + 6), inw(ioaddr + 8), inw(ioaddr + 10),
+ inw(ioaddr + 12), inw(ioaddr + 14));
+ lp->stats.tx_errors++;
+ /* ToDo: We should try to restart the adaptor... */
+ outw(0xffff, ioaddr + 24);
+ outw(0xffff, ioaddr + TX_STATUS);
+ outw(0xe85a, ioaddr + CONFIG_0);
+ outw(0x8100, ioaddr + TX_INTR);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Turn off the possible Tx interrupts. */
+ outb(0x00, ioaddr + TX_INTR);
+
+ outw(length, ioaddr + DATAPORT);
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+ lp->tx_queue++;
+ lp->tx_queue_len += length + 2;
+
+ if (lp->tx_started == 0) {
+ /* If the Tx is idle, always trigger a transmit. */
+ outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 1;
+ dev->tbusy = 0;
+ } else if (lp->tx_queue_len < 4096 - 1502)
+ /* Yes, there is room for one more packet. */
+ dev->tbusy = 0;
+
+ /* Turn on Tx interrupts back on. */
+ outb(0x82, ioaddr + TX_INTR);
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+net_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status;
+
+ if (dev == NULL) {
+ printk ("at1700_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+ status = inw(ioaddr + TX_STATUS);
+ outw(status, ioaddr + TX_STATUS);
+
+ if (net_debug > 4)
+ printk("%s: Interrupt with status %04x.\n", dev->name, status);
+ if (status & 0xff00
+ || (inb(ioaddr + RX_MODE) & 0x40) == 0) { /* Got a packet(s). */
+ net_rx(dev);
+ }
+ if (status & 0x00ff) {
+ if (status & 0x80) {
+ lp->stats.tx_packets++;
+ if (lp->tx_queue) {
+ outb(0x80 | lp->tx_queue, ioaddr + TX_START);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->tx_started = 0;
+ /* Turn on Tx interrupts off. */
+ outb(0x00, ioaddr + TX_INTR);
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ }
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+net_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 5;
+
+ while ((inb(ioaddr + RX_MODE) & 0x40) == 0) {
+ ushort status = inw(ioaddr + DATAPORT);
+ ushort pkt_len = inw(ioaddr + DATAPORT);
+
+ if (net_debug > 4)
+ printk("%s: Rxing packet mode %02x status %04x.\n",
+ dev->name, inb(ioaddr + RX_MODE), status);
+#ifndef final_version
+ if (status == 0) {
+ outb(0x05, ioaddr + 14);
+ break;
+ }
+#endif
+
+ if ((status & 0xF0) != 0x20) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x08) lp->stats.rx_length_errors++;
+ if (status & 0x04) lp->stats.rx_frame_errors++;
+ if (status & 0x02) lp->stats.rx_crc_errors++;
+ if (status & 0x01) lp->stats.rx_over_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ if (pkt_len > 1550) {
+ printk("%s: The AT1700 claimed a very large packet, size %d.\n",
+ dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+ outb(0x05, ioaddr + 14);
+ lp->stats.rx_errors++;
+ break;
+ }
+ skb = dev_alloc_skb(pkt_len+3);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet (len %d).\n",
+ dev->name, pkt_len);
+ /* Prime the FIFO and then flush the packet. */
+ inw(ioaddr + DATAPORT); inw(ioaddr + DATAPORT);
+ outb(0x05, ioaddr + 14);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2);
+
+ insw(ioaddr + DATAPORT, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
+ skb->protocol=eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ if (--boguscount <= 0)
+ break;
+ }
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ {
+ int i;
+ for (i = 0; i < 20; i++) {
+ if ((inb(ioaddr + RX_MODE) & 0x40) == 0x40)
+ break;
+ inw(ioaddr + DATAPORT); /* dummy status read */
+ outb(0x05, ioaddr + 14);
+ }
+
+ if (net_debug > 5)
+ printk("%s: Exint Rx packet with mode %02x after %d ticks.\n",
+ dev->name, inb(ioaddr + RX_MODE), i);
+ }
+ return;
+}
+
+/* The inverse routine to net_open(). */
+static int net_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Set configuration register 0 to disable Tx and Rx. */
+ outb(0xda, ioaddr + CONFIG_0);
+
+ /* Update the statistics -- ToDo. */
+
+ /* Power-down the chip. Green, green, green! */
+ outb(0x00, ioaddr + CONFIG_1);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ cli();
+ /* ToDo: Update the statistics from the device registers. */
+ sti();
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ if (dev->mc_count || dev->flags&(IFF_PROMISC|IFF_ALLMULTI))
+ {
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ dev->flags|=IFF_PROMISC;
+
+ outb(3, ioaddr + RX_MODE); /* Enable promiscuous mode */
+ }
+ else
+ outb(2, ioaddr + RX_MODE); /* Disable promiscuous, use normal mode */
+}
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_at1700 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, at1700_probe };
+
+static int io = 0x260;
+static int irq = 0;
+
+int init_module(void)
+{
+ if (io == 0)
+ printk("at1700: You should not use auto-probing with insmod!\n");
+ dev_at1700.base_addr = io;
+ dev_at1700.irq = irq;
+ if (register_netdev(&dev_at1700) != 0) {
+ printk("at1700: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_at1700);
+ kfree(dev_at1700.priv);
+ dev_at1700.priv = NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ free_irq(dev_at1700.irq);
+ irq2dev_map[dev_at1700.irq] = NULL;
+ release_region(dev_at1700.base_addr, AT1700_IO_EXTENT);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c at1700.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/atp.c b/i386/i386at/gpl/linux/net/atp.c
new file mode 100644
index 00000000..62aa04ef
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/atp.c
@@ -0,0 +1,787 @@
+/* atp.c: Attached (pocket) ethernet adapter driver for linux. */
+/*
+ This is a driver for a commonly OEMed pocket (parallel port)
+ ethernet adapter.
+
+ Written 1993,1994,1995 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ The timer-based reset code was written by Bill Carlson, wwc@super.org.
+*/
+
+static const char *version =
+ "atp.c:v1.01 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+/*
+ This file is a device driver for the RealTek (aka AT-Lan-Tec) pocket
+ ethernet adapter. This is a common low-cost OEM pocket ethernet
+ adapter, sold under many names.
+
+ Sources:
+ This driver was written from the packet driver assembly code provided by
+ Vincent Bono of AT-Lan-Tec. Ever try to figure out how a complicated
+ device works just from the assembly code? It ain't pretty. The following
+ description is written based on guesses and writing lots of special-purpose
+ code to test my theorized operation.
+
+ Theory of Operation
+
+ The RTL8002 adapter seems to be built around a custom spin of the SEEQ
+ controller core. It probably has a 16K or 64K internal packet buffer, of
+ which the first 4K is devoted to transmit and the rest to receive.
+ The controller maintains the queue of received packet and the packet buffer
+ access pointer internally, with only 'reset to beginning' and 'skip to next
+ packet' commands visible. The transmit packet queue holds two (or more?)
+ packets: both 'retransmit this packet' (due to collision) and 'transmit next
+ packet' commands must be started by hand.
+
+ The station address is stored in a standard bit-serial EEPROM which must be
+ read (ughh) by the device driver. (Provisions have been made for
+ substituting a 74S288 PROM, but I haven't gotten reports of any models
+ using it.) Unlike built-in devices, a pocket adapter can temporarily lose
+ power without indication to the device driver. The major effect is that
+ the station address, receive filter (promiscuous, etc.) and transceiver
+ must be reset.
+
+ The controller itself has 16 registers, some of which use only the lower
+ bits. The registers are read and written 4 bits at a time. The four bit
+ register address is presented on the data lines along with a few additional
+ timing and control bits. The data is then read from status port or written
+ to the data port.
+
+ Since the bulk data transfer of the actual packets through the slow
+ parallel port dominates the driver's running time, four distinct data
+ (non-register) transfer modes are provided by the adapter, two in each
+ direction. In the first mode timing for the nibble transfers is
+ provided through the data port. In the second mode the same timing is
+ provided through the control port. In either case the data is read from
+ the status port and written to the data port, just as it is accessing
+ registers.
+
+ In addition to the basic data transfer methods, several more are modes are
+ created by adding some delay by doing multiple reads of the data to allow
+ it to stabilize. This delay seems to be needed on most machines.
+
+ The data transfer mode is stored in the 'dev->if_port' field. Its default
+ value is '4'. It may be overridden at boot-time using the third parameter
+ to the "ether=..." initialization.
+
+ The header file <atp.h> provides inline functions that encapsulate the
+ register and data access methods. These functions are hand-tuned to
+ generate reasonable object code. This header file also documents my
+ interpretations of the device registers.
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "atp.h"
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define ETHERCARD_TOTAL_SIZE 3
+
+/* This code, written by wwc@super.org, resets the adapter every
+ TIMED_CHECKER ticks. This recovers from an unknown error which
+ hangs the device. */
+#define TIMED_CHECKER (HZ/4)
+#ifdef TIMED_CHECKER
+#include <linux/timer.h>
+static void atp_timed_checker(unsigned long ignored);
+static struct device *atp_timed_dev;
+static struct timer_list atp_timer = {NULL, NULL, 0, 0, atp_timed_checker};
+#endif
+
+/* Index to functions, as function prototypes. */
+
+extern int atp_probe(struct device *dev);
+
+static int atp_probe1(struct device *dev, short ioaddr);
+static void get_node_ID(struct device *dev);
+static unsigned short eeprom_op(short ioaddr, unsigned int cmd);
+static int net_open(struct device *dev);
+static void hardware_init(struct device *dev);
+static void write_packet(short ioaddr, int length, unsigned char *packet, int mode);
+static void trigger_send(short ioaddr, int length);
+static int net_send_packet(struct sk_buff *skb, struct device *dev);
+static void net_interrupt(int irq, struct pt_regs *regs);
+static void net_rx(struct device *dev);
+static void read_block(short ioaddr, int length, unsigned char *buffer, int data_mode);
+static int net_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+/* Check for a network adapter of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+int
+atp_init(struct device *dev)
+{
+ int *port, ports[] = {0x378, 0x278, 0x3bc, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return atp_probe1(dev, base_addr);
+ else if (base_addr == 1) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = ports; *port; port++) {
+ int ioaddr = *port;
+ outb(0x57, ioaddr + PAR_DATA);
+ if (inb(ioaddr + PAR_DATA) != 0x57)
+ continue;
+ if (atp_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+static int atp_probe1(struct device *dev, short ioaddr)
+{
+ int saved_ctrl_reg, status;
+
+ outb(0xff, ioaddr + PAR_DATA);
+ /* Save the original value of the Control register, in case we guessed
+ wrong. */
+ saved_ctrl_reg = inb(ioaddr + PAR_CONTROL);
+ /* IRQEN=0, SLCTB=high INITB=high, AUTOFDB=high, STBB=high. */
+ outb(0x04, ioaddr + PAR_CONTROL);
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+ eeprom_delay(2048);
+ status = read_nibble(ioaddr, CMR1);
+
+ if ((status & 0x78) != 0x08) {
+ /* The pocket adapter probe failed, restore the control register. */
+ outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
+ return 1;
+ }
+ status = read_nibble(ioaddr, CMR2_h);
+ if ((status & 0x78) != 0x10) {
+ outb(saved_ctrl_reg, ioaddr + PAR_CONTROL);
+ return 1;
+ }
+ /* Find the IRQ used by triggering an interrupt. */
+ write_reg_byte(ioaddr, CMR2, 0x01); /* No accept mode, IRQ out. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE); /* Enable Tx and Rx. */
+
+ /* Omit autoIRQ routine for now. Use "table lookup" instead. Uhgggh. */
+ if (ioaddr == 0x378)
+ dev->irq = 7;
+ else
+ dev->irq = 5;
+ write_reg_high(ioaddr, CMR1, CMR1h_TxRxOFF); /* Disable Tx and Rx units. */
+ write_reg(ioaddr, CMR2, CMR2_NULL);
+
+ dev->base_addr = ioaddr;
+
+ /* Read the station address PROM. */
+ get_node_ID(dev);
+
+ printk("%s: Pocket adapter found at %#3lx, IRQ %d, SAPROM "
+ "%02X:%02X:%02X:%02X:%02X:%02X.\n", dev->name, dev->base_addr,
+ dev->irq, dev->dev_addr[0], dev->dev_addr[1], dev->dev_addr[2],
+ dev->dev_addr[3], dev->dev_addr[4], dev->dev_addr[5]);
+
+ /* Leave the hardware in a reset state. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ ether_setup(dev);
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+
+ {
+ struct net_local *lp = (struct net_local *)dev->priv;
+ lp->addr_mode = CMR2h_Normal;
+ }
+
+ /* For the ATP adapter the "if_port" is really the data transfer mode. */
+ dev->if_port = (dev->mem_start & 0xf) ? dev->mem_start & 0x7 : 4;
+ if (dev->mem_end & 0xf)
+ net_debug = dev->mem_end & 7;
+
+ dev->open = net_open;
+ dev->stop = net_close;
+ dev->hard_start_xmit = net_send_packet;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+#ifdef TIMED_CHECKER
+ del_timer(&atp_timer);
+ atp_timer.expires = jiffies + TIMED_CHECKER;
+ atp_timed_dev = dev;
+ add_timer(&atp_timer);
+#endif
+ return 0;
+}
+
+/* Read the station address PROM, usually a word-wide EEPROM. */
+static void get_node_ID(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int sa_offset = 0;
+ int i;
+
+ write_reg(ioaddr, CMR2, CMR2_EEPROM); /* Point to the EEPROM control registers. */
+
+ /* Some adapters have the station address at offset 15 instead of offset
+ zero. Check for it, and fix it if needed. */
+ if (eeprom_op(ioaddr, EE_READ(0)) == 0xffff)
+ sa_offset = 15;
+
+ for (i = 0; i < 3; i++)
+ ((unsigned short *)dev->dev_addr)[i] =
+ ntohs(eeprom_op(ioaddr, EE_READ(sa_offset + i)));
+
+ write_reg(ioaddr, CMR2, CMR2_NULL);
+}
+
+/*
+ An EEPROM read command starts by shifting out 0x60+address, and then
+ shifting in the serial data. See the NatSemi databook for details.
+ * ________________
+ * CS : __|
+ * ___ ___
+ * CLK: ______| |___| |
+ * __ _______ _______
+ * DI : __X_______X_______X
+ * DO : _________X_______X
+ */
+
+static unsigned short eeprom_op(short ioaddr, unsigned int cmd)
+{
+ unsigned eedata_out = 0;
+ int num_bits = EE_CMD_SIZE;
+
+ while (--num_bits >= 0) {
+ char outval = test_bit(num_bits, &cmd) ? EE_DATA_WRITE : 0;
+ write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_LOW);
+ eeprom_delay(5);
+ write_reg_high(ioaddr, PROM_CMD, outval | EE_CLK_HIGH);
+ eedata_out <<= 1;
+ if (read_nibble(ioaddr, PROM_DATA) & EE_DATA_READ)
+ eedata_out++;
+ eeprom_delay(5);
+ }
+ write_reg_high(ioaddr, PROM_CMD, EE_CLK_LOW & ~EE_CS);
+ return eedata_out;
+}
+
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine sets everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+
+ This is an attachable device: if there is no dev->priv entry then it wasn't
+ probed for at boot-time, and we need to probe for it again.
+ */
+static int net_open(struct device *dev)
+{
+
+ /* The interrupt line is turned off (tri-stated) when the device isn't in
+ use. That's especially important for "attached" interfaces where the
+ port or interrupt may be shared. */
+ if (irq2dev_map[dev->irq] != 0
+ || (irq2dev_map[dev->irq] = dev) == 0
+ || request_irq(dev->irq, &net_interrupt, 0, "ATP")) {
+ return -EAGAIN;
+ }
+
+ hardware_init(dev);
+ dev->start = 1;
+ return 0;
+}
+
+/* This routine resets the hardware. We initialize everything, assuming that
+ the hardware may have been temporarily detached. */
+static void hardware_init(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+
+ for (i = 0; i < 6; i++)
+ write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+
+ if (net_debug > 2) {
+ printk("%s: Reset: current Rx mode %d.\n", dev->name,
+ (read_nibble(ioaddr, CMR2_h) >> 3) & 0x0f);
+ }
+
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ write_reg_high(ioaddr, CMR1, CMR1h_RxENABLE | CMR1h_TxENABLE);
+
+ /* Enable the interrupt line from the serial port. */
+ outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+
+ /* Unmask the interesting interrupts. */
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr);
+
+ lp->tx_unit_busy = 0;
+ lp->pac_cnt_in_tx_buf = 0;
+ lp->saved_tx_size = 0;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+}
+
+static void trigger_send(short ioaddr, int length)
+{
+ write_reg_byte(ioaddr, TxCNT0, length & 0xff);
+ write_reg(ioaddr, TxCNT1, length >> 8);
+ write_reg(ioaddr, CMR1, CMR1_Xmit);
+}
+
+static void write_packet(short ioaddr, int length, unsigned char *packet, int data_mode)
+{
+ length = (length + 1) & ~1; /* Round up to word length. */
+ outb(EOC+MAR, ioaddr + PAR_DATA);
+ if ((data_mode & 1) == 0) {
+ /* Write the packet out, starting with the write addr. */
+ outb(WrAddr+MAR, ioaddr + PAR_DATA);
+ do {
+ write_byte_mode0(ioaddr, *packet++);
+ } while (--length > 0) ;
+ } else {
+ /* Write the packet out in slow mode. */
+ unsigned char outbyte = *packet++;
+
+ outb(Ctrl_LNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ outb(WrAddr+MAR, ioaddr + PAR_DATA);
+
+ outb((outbyte & 0x0f)|0x40, ioaddr + PAR_DATA);
+ outb(outbyte & 0x0f, ioaddr + PAR_DATA);
+ outbyte >>= 4;
+ outb(outbyte & 0x0f, ioaddr + PAR_DATA);
+ outb(Ctrl_HNibWrite + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ while (--length > 0)
+ write_byte_mode1(ioaddr, *packet++);
+ }
+ /* Terminate the Tx frame. End of write: ECB. */
+ outb(0xff, ioaddr + PAR_DATA);
+ outb(Ctrl_HNibWrite | Ctrl_SelData | Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+}
+
+static int
+net_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ inb(ioaddr + PAR_CONTROL) & 0x10 ? "network cable problem"
+ : "IRQ conflict");
+ lp->stats.tx_errors++;
+ /* Try to restart the adapter. */
+ hardware_init(dev);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+ int flags;
+
+ /* Disable interrupts by writing 0x00 to the Interrupt Mask Register.
+ This sequence must not be interrupted by an incoming packet. */
+ save_flags(flags);
+ cli();
+ write_reg(ioaddr, IMR, 0);
+ write_reg_high(ioaddr, IMR, 0);
+ restore_flags(flags);
+
+ write_packet(ioaddr, length, buf, dev->if_port);
+
+ lp->pac_cnt_in_tx_buf++;
+ if (lp->tx_unit_busy == 0) {
+ trigger_send(ioaddr, length);
+ lp->saved_tx_size = 0; /* Redundant */
+ lp->re_tx = 0;
+ lp->tx_unit_busy = 1;
+ } else
+ lp->saved_tx_size = length;
+
+ dev->trans_start = jiffies;
+ /* Re-enable the LPT interrupts. */
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+net_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 20;
+ static int num_tx_since_rx = 0;
+
+ if (dev == NULL) {
+ printk ("ATP_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ /* Disable additional spurious interrupts. */
+ outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
+
+ /* The adapter's output is currently the IRQ line, switch it to data. */
+ write_reg(ioaddr, CMR2, CMR2_NULL);
+ write_reg(ioaddr, IMR, 0);
+
+ if (net_debug > 5) printk("%s: In interrupt ", dev->name);
+ while (--boguscount > 0) {
+ status = read_nibble(ioaddr, ISR);
+ if (net_debug > 5) printk("loop status %02x..", status);
+
+ if (status & (ISR_RxOK<<3)) {
+ write_reg(ioaddr, ISR, ISR_RxOK); /* Clear the Rx interrupt. */
+ do {
+ int read_status = read_nibble(ioaddr, CMR1);
+ if (net_debug > 6)
+ printk("handling Rx packet %02x..", read_status);
+ /* We acknowledged the normal Rx interrupt, so if the interrupt
+ is still outstanding we must have a Rx error. */
+ if (read_status & (CMR1_IRQ << 3)) { /* Overrun. */
+ lp->stats.rx_over_errors++;
+ /* Set to no-accept mode long enough to remove a packet. */
+ write_reg_high(ioaddr, CMR2, CMR2h_OFF);
+ net_rx(dev);
+ /* Clear the interrupt and return to normal Rx mode. */
+ write_reg_high(ioaddr, ISR, ISRh_RxErr);
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+ } else if ((read_status & (CMR1_BufEnb << 3)) == 0) {
+ net_rx(dev);
+ dev->last_rx = jiffies;
+ num_tx_since_rx = 0;
+ } else
+ break;
+ } while (--boguscount > 0);
+ } else if (status & ((ISR_TxErr + ISR_TxOK)<<3)) {
+ if (net_debug > 6) printk("handling Tx done..");
+ /* Clear the Tx interrupt. We should check for too many failures
+ and reinitialize the adapter. */
+ write_reg(ioaddr, ISR, ISR_TxErr + ISR_TxOK);
+ if (status & (ISR_TxErr<<3)) {
+ lp->stats.collisions++;
+ if (++lp->re_tx > 15) {
+ lp->stats.tx_aborted_errors++;
+ hardware_init(dev);
+ break;
+ }
+ /* Attempt to retransmit. */
+ if (net_debug > 6) printk("attempting to ReTx");
+ write_reg(ioaddr, CMR1, CMR1_ReXmit + CMR1_Xmit);
+ } else {
+ /* Finish up the transmit. */
+ lp->stats.tx_packets++;
+ lp->pac_cnt_in_tx_buf--;
+ if ( lp->saved_tx_size) {
+ trigger_send(ioaddr, lp->saved_tx_size);
+ lp->saved_tx_size = 0;
+ lp->re_tx = 0;
+ } else
+ lp->tx_unit_busy = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ num_tx_since_rx++;
+ } else if (num_tx_since_rx > 8
+ && jiffies > dev->last_rx + 100) {
+ if (net_debug > 2)
+ printk("%s: Missed packet? No Rx after %d Tx and %ld jiffies"
+ " status %02x CMR1 %02x.\n", dev->name,
+ num_tx_since_rx, jiffies - dev->last_rx, status,
+ (read_nibble(ioaddr, CMR1) >> 3) & 15);
+ lp->stats.rx_missed_errors++;
+ hardware_init(dev);
+ num_tx_since_rx = 0;
+ break;
+ } else
+ break;
+ }
+
+ /* This following code fixes a rare (and very difficult to track down)
+ problem where the adapter forgets its ethernet address. */
+ {
+ int i;
+ for (i = 0; i < 6; i++)
+ write_reg_byte(ioaddr, PAR0 + i, dev->dev_addr[i]);
+#ifdef TIMED_CHECKER
+ del_timer(&atp_timer);
+ atp_timer.expires = jiffies + TIMED_CHECKER;
+ add_timer(&atp_timer);
+#endif
+ }
+
+ /* Tell the adapter that it can go back to using the output line as IRQ. */
+ write_reg(ioaddr, CMR2, CMR2_IRQOUT);
+ /* Enable the physical interrupt line, which is sure to be low until.. */
+ outb(Ctrl_SelData + Ctrl_IRQEN, ioaddr + PAR_CONTROL);
+ /* .. we enable the interrupt sources. */
+ write_reg(ioaddr, IMR, ISR_RxOK | ISR_TxErr | ISR_TxOK);
+ write_reg_high(ioaddr, IMR, ISRh_RxErr); /* Hmmm, really needed? */
+
+ if (net_debug > 5) printk("exiting interrupt.\n");
+
+ dev->interrupt = 0;
+
+ return;
+}
+
+#ifdef TIMED_CHECKER
+/* This following code fixes a rare (and very difficult to track down)
+ problem where the adapter forgets its ethernet address. */
+static void atp_timed_checker(unsigned long ignored)
+{
+ int i;
+ int ioaddr = atp_timed_dev->base_addr;
+
+ if (!atp_timed_dev->interrupt)
+ {
+ for (i = 0; i < 6; i++)
+#if 0
+ if (read_cmd_byte(ioaddr, PAR0 + i) != atp_timed_dev->dev_addr[i])
+ {
+ struct net_local *lp = (struct net_local *)atp_timed_dev->priv;
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+ if (i == 2)
+ lp->stats.tx_errors++;
+ else if (i == 3)
+ lp->stats.tx_dropped++;
+ else if (i == 4)
+ lp->stats.collisions++;
+ else
+ lp->stats.rx_errors++;
+ }
+#else
+ write_reg_byte(ioaddr, PAR0 + i, atp_timed_dev->dev_addr[i]);
+#endif
+ }
+ del_timer(&atp_timer);
+ atp_timer.expires = jiffies + TIMED_CHECKER;
+ add_timer(&atp_timer);
+}
+#endif
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void net_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+#ifdef notdef
+ ushort header[4];
+#else
+ struct rx_header rx_head;
+#endif
+
+ /* Process the received packet. */
+ outb(EOC+MAR, ioaddr + PAR_DATA);
+ read_block(ioaddr, 8, (unsigned char*)&rx_head, dev->if_port);
+ if (net_debug > 5)
+ printk(" rx_count %04x %04x %04x %04x..", rx_head.pad,
+ rx_head.rx_count, rx_head.rx_status, rx_head.cur_addr);
+ if ((rx_head.rx_status & 0x77) != 0x01) {
+ lp->stats.rx_errors++;
+ /* Ackkk! I don't have any documentation on what the error bits mean!
+ The best I can do is slap the device around a bit. */
+ if (net_debug > 3) printk("%s: Unknown ATP Rx error %04x.\n",
+ dev->name, rx_head.rx_status);
+ hardware_init(dev);
+ return;
+ } else {
+ /* Malloc up new buffer. */
+ int pkt_len = (rx_head.rx_count & 0x7ff) - 4; /* The "-4" is omits the FCS (CRC). */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ goto done;
+ }
+ skb->dev = dev;
+
+ read_block(ioaddr, pkt_len, skb_put(skb,pkt_len), dev->if_port);
+
+ if (net_debug > 6) {
+ unsigned char *data = skb->data;
+ printk(" data %02x%02x%02x %02x%02x%02x %02x%02x%02x"
+ "%02x%02x%02x %02x%02x..",
+ data[0], data[1], data[2], data[3], data[4], data[5],
+ data[6], data[7], data[8], data[9], data[10], data[11],
+ data[12], data[13]);
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ done:
+ write_reg(ioaddr, CMR1, CMR1_NextPkt);
+ return;
+}
+
+static void read_block(short ioaddr, int length, unsigned char *p, int data_mode)
+{
+
+ if (data_mode <= 3) { /* Mode 0 or 1 */
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ outb(length == 8 ? RdAddr | HNib | MAR : RdAddr | MAR,
+ ioaddr + PAR_DATA);
+ if (data_mode <= 1) { /* Mode 0 or 1 */
+ do *p++ = read_byte_mode0(ioaddr); while (--length > 0);
+ } else /* Mode 2 or 3 */
+ do *p++ = read_byte_mode2(ioaddr); while (--length > 0);
+ } else if (data_mode <= 5)
+ do *p++ = read_byte_mode4(ioaddr); while (--length > 0);
+ else
+ do *p++ = read_byte_mode6(ioaddr); while (--length > 0);
+
+ outb(EOC+HNib+MAR, ioaddr + PAR_DATA);
+ outb(Ctrl_SelData, ioaddr + PAR_CONTROL);
+}
+
+/* The inverse routine to net_open(). */
+static int
+net_close(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx here. */
+ lp->addr_mode = CMR2h_OFF;
+ write_reg_high(ioaddr, CMR2, CMR2h_OFF);
+
+ /* Free the IRQ line. */
+ outb(0x00, ioaddr + PAR_CONTROL);
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ /* Leave the hardware in a reset state. */
+ write_reg_high(ioaddr, CMR1, CMR1h_RESET);
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ return &lp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adapter.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ int num_addrs=dev->mc_list;
+
+ if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ num_addrs=1;
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ if(num_addrs)
+ dev->flags|=IFF_PROMISC;
+ lp->addr_mode = num_addrs ? CMR2h_PROMISC : CMR2h_Normal;
+ write_reg_high(ioaddr, CMR2, lp->addr_mode);
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c atp.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/atp.h b/i386/i386at/gpl/linux/net/atp.h
new file mode 100644
index 00000000..e58f8c10
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/atp.h
@@ -0,0 +1,264 @@
+#include <linux/if_ether.h>
+#include <linux/types.h>
+#include <asm/io.h>
+
+struct net_local {
+#ifdef __KERNEL__
+ struct enet_statistics stats;
+#endif
+ ushort saved_tx_size;
+ unsigned char
+ re_tx, /* Number of packet retransmissions. */
+ tx_unit_busy,
+ addr_mode, /* Current Rx filter e.g. promiscuous, etc. */
+ pac_cnt_in_tx_buf;
+};
+
+struct rx_header {
+ ushort pad; /* The first read is always corrupted. */
+ ushort rx_count;
+ ushort rx_status; /* Unknown bit assignments :-<. */
+ ushort cur_addr; /* Apparently the current buffer address(?) */
+};
+
+#define PAR_DATA 0
+#define PAR_STATUS 1
+#define PAR_CONTROL 2
+
+#define Ctrl_LNibRead 0x08 /* LP_PSELECP */
+#define Ctrl_HNibRead 0
+#define Ctrl_LNibWrite 0x08 /* LP_PSELECP */
+#define Ctrl_HNibWrite 0
+#define Ctrl_SelData 0x04 /* LP_PINITP */
+#define Ctrl_IRQEN 0x10 /* LP_PINTEN */
+
+#define EOW 0xE0
+#define EOC 0xE0
+#define WrAddr 0x40 /* Set address of EPLC read, write register. */
+#define RdAddr 0xC0
+#define HNib 0x10
+
+enum page0_regs
+{
+ /* The first six registers hold the ethernet physical station address. */
+ PAR0 = 0, PAR1 = 1, PAR2 = 2, PAR3 = 3, PAR4 = 4, PAR5 = 5,
+ TxCNT0 = 6, TxCNT1 = 7, /* The transmit byte count. */
+ TxSTAT = 8, RxSTAT = 9, /* Tx and Rx status. */
+ ISR = 10, IMR = 11, /* Interrupt status and mask. */
+ CMR1 = 12, /* Command register 1. */
+ CMR2 = 13, /* Command register 2. */
+ MAR = 14, /* Memory address register. */
+ CMR2_h = 0x1d, };
+
+enum eepage_regs
+{ PROM_CMD = 6, PROM_DATA = 7 }; /* Note that PROM_CMD is in the "high" bits. */
+
+
+#define ISR_TxOK 0x01
+#define ISR_RxOK 0x04
+#define ISR_TxErr 0x02
+#define ISRh_RxErr 0x11 /* ISR, high nibble */
+
+#define CMR1h_RESET 0x04 /* Reset. */
+#define CMR1h_RxENABLE 0x02 /* Rx unit enable. */
+#define CMR1h_TxENABLE 0x01 /* Tx unit enable. */
+#define CMR1h_TxRxOFF 0x00
+#define CMR1_ReXmit 0x08 /* Trigger a retransmit. */
+#define CMR1_Xmit 0x04 /* Trigger a transmit. */
+#define CMR1_IRQ 0x02 /* Interrupt active. */
+#define CMR1_BufEnb 0x01 /* Enable the buffer(?). */
+#define CMR1_NextPkt 0x01 /* Enable the buffer(?). */
+
+#define CMR2_NULL 8
+#define CMR2_IRQOUT 9
+#define CMR2_RAMTEST 10
+#define CMR2_EEPROM 12 /* Set to page 1, for reading the EEPROM. */
+
+#define CMR2h_OFF 0 /* No accept mode. */
+#define CMR2h_Physical 1 /* Accept a physical address match only. */
+#define CMR2h_Normal 2 /* Accept physical and broadcast address. */
+#define CMR2h_PROMISC 3 /* Promiscuous mode. */
+
+/* An inline function used below: it differs from inb() by explicitly return an unsigned
+ char, saving a truncation. */
+extern inline unsigned char inbyte(unsigned short port)
+{
+ unsigned char _v;
+ __asm__ __volatile__ ("inb %w1,%b0" :"=a" (_v):"d" (port));
+ return _v;
+}
+
+/* Read register OFFSET.
+ This command should always be terminated with read_end(). */
+extern inline unsigned char read_nibble(short port, unsigned char offset)
+{
+ unsigned char retval;
+ outb(EOC+offset, port + PAR_DATA);
+ outb(RdAddr+offset, port + PAR_DATA);
+ inbyte(port + PAR_STATUS); /* Settling time delay */
+ retval = inbyte(port + PAR_STATUS);
+ outb(EOC+offset, port + PAR_DATA);
+
+ return retval;
+}
+
+/* Functions for bulk data read. The interrupt line is always disabled. */
+/* Get a byte using read mode 0, reading data from the control lines. */
+extern inline unsigned char read_byte_mode0(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+/* The same as read_byte_mode0(), but does multiple inb()s for stability. */
+extern inline unsigned char read_byte_mode2(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(Ctrl_LNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(Ctrl_HNibRead, ioaddr + PAR_CONTROL);
+ inbyte(ioaddr + PAR_STATUS); /* Settling time delay -- needed! */
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+/* Read a byte through the data register. */
+extern inline unsigned char read_byte_mode4(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(RdAddr | MAR, ioaddr + PAR_DATA);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+/* Read a byte through the data register, double reading to allow settling. */
+extern inline unsigned char read_byte_mode6(short ioaddr)
+{
+ unsigned char low_nib;
+
+ outb(RdAddr | MAR, ioaddr + PAR_DATA);
+ inbyte(ioaddr + PAR_STATUS);
+ low_nib = (inbyte(ioaddr + PAR_STATUS) >> 3) & 0x0f;
+ outb(RdAddr | HNib | MAR, ioaddr + PAR_DATA);
+ inbyte(ioaddr + PAR_STATUS);
+ return low_nib | ((inbyte(ioaddr + PAR_STATUS) << 1) & 0xf0);
+}
+
+extern inline void
+write_reg(short port, unsigned char reg, unsigned char value)
+{
+ unsigned char outval;
+ outb(EOC | reg, port + PAR_DATA);
+ outval = WrAddr | reg;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outval &= 0xf0;
+ outval |= value;
+ outb(outval, port + PAR_DATA);
+ outval &= 0x1f;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA);
+
+ outb(EOC | outval, port + PAR_DATA);
+}
+
+extern inline void
+write_reg_high(short port, unsigned char reg, unsigned char value)
+{
+ unsigned char outval = EOC | HNib | reg;
+
+ outb(outval, port + PAR_DATA);
+ outval &= WrAddr | HNib | 0x0f;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outval = WrAddr | HNib | value;
+ outb(outval, port + PAR_DATA);
+ outval &= HNib | 0x0f; /* HNib | value */
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA);
+
+ outb(EOC | HNib | outval, port + PAR_DATA);
+}
+
+/* Write a byte out using nibble mode. The low nibble is written first. */
+extern inline void
+write_reg_byte(short port, unsigned char reg, unsigned char value)
+{
+ unsigned char outval;
+ outb(EOC | reg, port + PAR_DATA); /* Reset the address register. */
+ outval = WrAddr | reg;
+ outb(outval, port + PAR_DATA);
+ outb(outval, port + PAR_DATA); /* Double write for PS/2. */
+
+ outb((outval & 0xf0) | (value & 0x0f), port + PAR_DATA);
+ outb(value & 0x0f, port + PAR_DATA);
+ value >>= 4;
+ outb(value, port + PAR_DATA);
+ outb(0x10 | value, port + PAR_DATA);
+ outb(0x10 | value, port + PAR_DATA);
+
+ outb(EOC | value, port + PAR_DATA); /* Reset the address register. */
+}
+
+/*
+ * Bulk data writes to the packet buffer. The interrupt line remains enabled.
+ * The first, faster method uses only the dataport (data modes 0, 2 & 4).
+ * The second (backup) method uses data and control regs (modes 1, 3 & 5).
+ * It should only be needed when there is skew between the individual data
+ * lines.
+ */
+extern inline void write_byte_mode0(short ioaddr, unsigned char value)
+{
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+}
+
+extern inline void write_byte_mode1(short ioaddr, unsigned char value)
+{
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ outb(Ctrl_IRQEN | Ctrl_LNibWrite, ioaddr + PAR_CONTROL);
+ outb((value>>4) | 0x10, ioaddr + PAR_DATA);
+ outb(Ctrl_IRQEN | Ctrl_HNibWrite, ioaddr + PAR_CONTROL);
+}
+
+/* Write 16bit VALUE to the packet buffer: the same as above just doubled. */
+extern inline void write_word_mode0(short ioaddr, unsigned short value)
+{
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb(value & 0x0f, ioaddr + PAR_DATA);
+ value >>= 4;
+ outb((value & 0x0f) | 0x10, ioaddr + PAR_DATA);
+}
+
+/* EEPROM_Ctrl bits. */
+#define EE_SHIFT_CLK 0x04 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_CLK_HIGH 0x12
+#define EE_CLK_LOW 0x16
+#define EE_DATA_WRITE 0x01 /* EEPROM chip data in. */
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+
+/* Delay between EEPROM clock transitions. */
+#define eeprom_delay(ticks) \
+do { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }} while (0)
+
+/* The EEPROM commands include the alway-set leading bit. */
+#define EE_WRITE_CMD(offset) (((5 << 6) + (offset)) << 17)
+#define EE_READ(offset) (((6 << 6) + (offset)) << 17)
+#define EE_ERASE(offset) (((7 << 6) + (offset)) << 17)
+#define EE_CMD_SIZE 27 /* The command+address+data size. */
diff --git a/i386/i386at/gpl/linux/net/de4x5.c b/i386/i386at/gpl/linux/net/de4x5.c
new file mode 100644
index 00000000..249887a6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de4x5.c
@@ -0,0 +1,2788 @@
+/* de4x5.c: A DIGITAL DE425/DE434/DE435/DE500 ethernet driver for Linux.
+
+ Copyright 1994, 1995 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of EtherWORKS ethernet cards:
+
+ DE425 TP/COAX EISA
+ DE434 TP PCI
+ DE435 TP/COAX/AUI PCI
+ DE500 10/100 PCI Fasternet
+
+ The driver has been tested on a relatively busy network using the DE425,
+ DE434, DE435 and DE500 cards and benchmarked with 'ttcp': it transferred
+ 16M of data to a DECstation 5000/200 as follows:
+
+ TCP UDP
+ TX RX TX RX
+ DE425 1030k 997k 1170k 1128k
+ DE434 1063k 995k 1170k 1125k
+ DE435 1063k 995k 1170k 1125k
+ DE500 1063k 998k 1170k 1125k in 10Mb/s mode
+
+ All values are typical (in kBytes/sec) from a sample of 4 for each
+ measurement. Their error is +/-20k on a quiet (private) network and also
+ depend on what load the CPU has.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+ This driver has been written substantially from scratch, although its
+ inheritance of style and stack interface from 'ewrk3.c' and in turn from
+ Donald Becker's 'lance.c' should be obvious.
+
+ Upto 15 EISA cards can be supported under this driver, limited primarily
+ by the available IRQ lines. I have checked different configurations of
+ multiple depca, EtherWORKS 3 cards and de4x5 cards and have not found a
+ problem yet (provided you have at least depca.c v0.38) ...
+
+ PCI support has been added to allow the driver to work with the DE434
+ and DE435 cards. The I/O accesses are a bit of a kludge due to the
+ differences in the EISA and PCI CSR address offsets from the base
+ address.
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). Loadable module support under PCI has been achieved
+ by letting any I/O address less than 0x1000 be assigned as:
+
+ 0xghh
+
+ where g is the bus number (usually 0 until the BIOS's get fixed)
+ hh is the device number (max is 32 per bus).
+
+ Essentially, the I/O address and IRQ information are ignored and filled
+ in later by the PCI BIOS during the PCI probe. Note that the board
+ should be in the system at boot time so that its I/O address and IRQ are
+ allocated by the PCI BIOS automatically. The special case of device 0 on
+ bus 0 is not allowed as the probe will think you're autoprobing a
+ module.
+
+ To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy de4x5.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) edit the source code near line 2762 to reflect the I/O address and
+ IRQ you're using, or assign these when loading by:
+
+ insmod de4x5.o irq=x io=y
+
+ 3) compile de4x5.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the de4x5 configuration turned off and reboot.
+ 5) insmod de4x5.o
+ 6) run the net startup bits for your new eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod de4x5'.
+
+ Automedia detection is included so that in principal you can disconnect
+ from, e.g. TP, reconnect to BNC and things will still work (after a
+ pause whilst the driver figures out where its media went). My tests
+ using ping showed that it appears to work....
+
+ A compile time switch to allow Znyx recognition has been added. This
+ "feature" is in no way supported nor tested in this driver and the user
+ may use it at his/her sole discretion. I have had 2 conflicting reports
+ that my driver will or won't work with Znyx. Try Donald Becker's
+ 'tulip.c' if this driver doesn't work for you. I will not be supporting
+ Znyx cards since I have no information on them and can't test them in a
+ system.
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 17-Nov-94 Initial writing. ALPHA code release.
+ 0.2 13-Jan-95 Added PCI support for DE435's.
+ 0.21 19-Jan-95 Added auto media detection.
+ 0.22 10-Feb-95 Fix interrupt handler call <chris@cosy.sbg.ac.at>.
+ Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ Add request/release_region code.
+ Add loadable modules support for PCI.
+ Clean up loadable modules support.
+ 0.23 28-Feb-95 Added DC21041 and DC21140 support.
+ Fix missed frame counter value and initialisation.
+ Fixed EISA probe.
+ 0.24 11-Apr-95 Change delay routine to use <linux/udelay>.
+ Change TX_BUFFS_AVAIL macro.
+ Change media autodetection to allow manual setting.
+ Completed DE500 (DC21140) support.
+ 0.241 18-Apr-95 Interim release without DE500 Autosense Algorithm.
+ 0.242 10-May-95 Minor changes
+ 0.30 12-Jun-95 Timer fix for DC21140
+ Portability changes.
+ Add ALPHA changes from <jestabro@ant.tay1.dec.com>.
+ Add DE500 semi automatic autosense.
+ Add Link Fail interrupt TP failure detection.
+ Add timer based link change detection.
+ Plugged a memory leak in de4x5_queue_pkt().
+ 0.31 13-Jun-95 Fixed PCI stuff for 1.3.1
+ 0.32 26-Jun-95 Added verify_area() calls in de4x5_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+
+ =========================================================================
+*/
+
+static const char *version = "de4x5.c:v0.32 6/26/95 davies@wanton.lkg.dec.com\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "de4x5.h"
+
+#ifdef DE4X5_DEBUG
+static int de4x5_debug = DE4X5_DEBUG;
+#else
+static int de4x5_debug = 1;
+#endif
+
+#ifdef DE4X5_AUTOSENSE /* Should be done on a per adapter basis */
+static int de4x5_autosense = DE4X5_AUTOSENSE;
+#else
+static int de4x5_autosense = AUTO; /* Do auto media/mode sensing */
+#endif
+
+#ifdef DE4X5_FULL_DUPLEX /* Should be done on a per adapter basis */
+static s32 de4x5_full_duplex = 1;
+#else
+static s32 de4x5_full_duplex = 0;
+#endif
+
+#define DE4X5_NDA 0xffe0 /* No Device (I/O) Address */
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+/*
+** Ethernet Info
+*/
+#define PKT_BUF_SZ 1536 /* Buffer size for each Tx/Rx buffer */
+#define MAX_PKT_SZ 1514 /* Maximum ethernet packet length */
+#define MAX_DAT_SZ 1500 /* Maximum ethernet data length */
+#define MIN_DAT_SZ 1 /* Minimum ethernet data length */
+#define PKT_HDR_LEN 14 /* Addresses and data length info */
+#define FAKE_FRAME_LEN (MAX_PKT_SZ + 1)
+#define QUEUE_PKT_TIMEOUT (3*HZ) /* 3 second timeout */
+
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+/*
+** EISA bus defines
+*/
+#define DE4X5_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define DE4X5_EISA_TOTAL_SIZE 0xfff /* I/O address extent */
+
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+
+#define DE4X5_SIGNATURE {"DE425",""}
+#define DE4X5_NAME_LENGTH 8
+
+/*
+** PCI Bus defines
+*/
+#define PCI_MAX_BUS_NUM 8
+#define DE4X5_PCI_TOTAL_SIZE 0x80 /* I/O address extent */
+#define DE4X5_CLASS_CODE 0x00020000 /* Network controller, Ethernet */
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword align */
+#define ALIGN16 ((u_long)16 - 1) /* 4 longword align */
+#define ALIGN32 ((u_long)32 - 1) /* 8 longword align */
+#define ALIGN64 ((u_long)64 - 1) /* 16 longword align */
+#define ALIGN128 ((u_long)128 - 1) /* 32 longword align */
+
+#define ALIGN ALIGN32 /* Keep the DC21040 happy... */
+#define CACHE_ALIGN CAL_16LONG
+#define DESC_SKIP_LEN DSL_0 /* Must agree with DESC_ALIGN */
+/*#define DESC_ALIGN u32 dummy[4]; / * Must agree with DESC_SKIP_LEN */
+#define DESC_ALIGN
+
+#ifdef MACH
+#define IS_NOT_DEC
+#endif
+
+#ifndef IS_NOT_DEC /* See README.de4x5 for using this */
+static int is_not_dec = 0;
+#else
+static int is_not_dec = 1;
+#endif
+
+/*
+** DE4X5 IRQ ENABLE/DISABLE
+*/
+#define ENABLE_IRQs { \
+ imr |= lp->irq_en;\
+ outl(imr, DE4X5_IMR); /* Enable the IRQs */\
+}
+
+#define DISABLE_IRQs {\
+ imr = inl(DE4X5_IMR);\
+ imr &= ~lp->irq_en;\
+ outl(imr, DE4X5_IMR); /* Disable the IRQs */\
+}
+
+#define UNMASK_IRQs {\
+ imr |= lp->irq_mask;\
+ outl(imr, DE4X5_IMR); /* Unmask the IRQs */\
+}
+
+#define MASK_IRQs {\
+ imr = inl(DE4X5_IMR);\
+ imr &= ~lp->irq_mask;\
+ outl(imr, DE4X5_IMR); /* Mask the IRQs */\
+}
+
+/*
+** DE4X5 START/STOP
+*/
+#define START_DE4X5 {\
+ omr = inl(DE4X5_OMR);\
+ omr |= OMR_ST | OMR_SR;\
+ outl(omr, DE4X5_OMR); /* Enable the TX and/or RX */\
+}
+
+#define STOP_DE4X5 {\
+ omr = inl(DE4X5_OMR);\
+ omr &= ~(OMR_ST|OMR_SR);\
+ outl(omr, DE4X5_OMR); /* Disable the TX and/or RX */ \
+}
+
+/*
+** DE4X5 SIA RESET
+*/
+#define RESET_SIA outl(0, DE4X5_SICR); /* Reset SIA connectivity regs */
+
+/*
+** DE500 AUTOSENSE TIMER INTERVAL (MILLISECS)
+*/
+#define DE4X5_AUTOSENSE_MS 250
+
+/*
+** SROM Structure
+*/
+struct de4x5_srom {
+ char reserved[18];
+ char version;
+ char num_adapters;
+ char ieee_addr[6];
+ char info[100];
+ short chksum;
+};
+
+/*
+** DE4X5 Descriptors. Make sure that all the RX buffers are contiguous
+** and have sizes of both a power of 2 and a multiple of 4.
+** A size of 256 bytes for each buffer could be chosen because over 90% of
+** all packets in our network are <256 bytes long and 64 longword alignment
+** is possible. 1536 showed better 'ttcp' performance. Take your pick. 32 TX
+** descriptors are needed for machines with an ALPHA CPU.
+*/
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 32 /* Number of TX descriptors */
+#define BUFF_ALLOC_RETRIES 10 /* In case of memory shortage */
+#define RX_BUFF_SZ 1536 /* Power of 2 for kmalloc and */
+ /* Multiple of 4 for DC21040 */
+struct de4x5_desc {
+ volatile s32 status;
+ u32 des1;
+ u32 buf;
+ u32 next;
+ DESC_ALIGN
+};
+
+/*
+** The DE4X5 private structure
+*/
+#define DE4X5_PKT_STAT_SZ 16
+#define DE4X5_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DE4X5_PKT_STAT_SZ */
+
+struct de4x5_private {
+ char adapter_name[80]; /* Adapter name */
+ struct de4x5_desc rx_ring[NUM_RX_DESC]; /* RX descriptor ring */
+ struct de4x5_desc tx_ring[NUM_TX_DESC]; /* TX descriptor ring */
+ struct sk_buff *skb[NUM_TX_DESC]; /* TX skb for freeing when sent */
+ int rx_new, rx_old; /* RX descriptor ring pointers */
+ int tx_new, tx_old; /* TX descriptor ring pointers */
+ char setup_frame[SETUP_FRAME_LEN]; /* Holds MCA and PA info. */
+ struct enet_statistics stats; /* Public stats */
+ struct {
+ u_int bins[DE4X5_PKT_STAT_SZ]; /* Private stats counters */
+ u_int unicast;
+ u_int multicast;
+ u_int broadcast;
+ u_int excessive_collisions;
+ u_int tx_underruns;
+ u_int excessive_underruns;
+ } pktStats;
+ char rxRingSize;
+ char txRingSize;
+ int bus; /* EISA or PCI */
+ int bus_num; /* PCI Bus number */
+ int chipset; /* DC21040, DC21041 or DC21140 */
+ s32 irq_mask; /* Interrupt Mask (Enable) bits */
+ s32 irq_en; /* Summary interrupt bits */
+ int media; /* Media (eg TP), mode (eg 100B)*/
+ int linkProb; /* Possible Link Problem */
+ int autosense; /* Allow/disallow autosensing */
+ int tx_enable; /* Enable descriptor polling */
+ int lostMedia; /* Possibly lost media */
+ int setup_f; /* Setup frame filtering type */
+};
+
+
+/*
+** The transmit ring full condition is described by the tx_old and tx_new
+** pointers by:
+** tx_old = tx_new Empty ring
+** tx_old = tx_new+1 Full ring
+** tx_old+txRingSize = tx_new+1 Full ring (wrapped condition)
+*/
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+lp->txRingSize-lp->tx_new-1:\
+ lp->tx_old -lp->tx_new-1)
+
+/*
+** Public Functions
+*/
+static int de4x5_open(struct device *dev);
+static int de4x5_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void de4x5_interrupt(int irq, struct pt_regs *regs);
+static int de4x5_close(struct device *dev);
+static struct enet_statistics *de4x5_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+/*
+** Private functions
+*/
+static int de4x5_hw_init(struct device *dev, u_long iobase);
+static int de4x5_init(struct device *dev);
+static int de4x5_rx(struct device *dev);
+static int de4x5_tx(struct device *dev);
+static int de4x5_ast(struct device *dev);
+
+static int autoconf_media(struct device *dev);
+static void create_packet(struct device *dev, char *frame, int len);
+static void dce_us_delay(u32 usec);
+static void dce_ms_delay(u32 msec);
+static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb);
+static void dc21040_autoconf(struct device *dev);
+static void dc21041_autoconf(struct device *dev);
+static void dc21140_autoconf(struct device *dev);
+static int test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec);
+/*static int test_sym_link(struct device *dev, u32 msec);*/
+static int ping_media(struct device *dev);
+static void reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr);
+static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec);
+static void load_ms_timer(struct device *dev, u32 msec);
+static int EISA_signature(char *name, s32 eisa_id);
+static int DevicePresent(u_long iobase);
+static short srom_rd(u_long address, u_char offset);
+static void srom_latch(u_int command, u_long address);
+static void srom_command(u_int command, u_long address);
+static void srom_address(u_int command, u_long address, u_char offset);
+static short srom_data(u_int command, u_long address);
+/*static void srom_busy(u_int command, u_long address);*/
+static void sendto_srom(u_int command, u_long addr);
+static int getfrom_srom(u_long addr);
+static void SetMulticastFilter(struct device *dev);
+static int get_hw_addr(struct device *dev);
+
+static void eisa_probe(struct device *dev, u_long iobase);
+static void pci_probe(struct device *dev, u_long iobase);
+static struct device *alloc_device(struct device *dev, u_long iobase);
+static char *build_setup_frame(struct device *dev, int mode);
+static void disable_ast(struct device *dev);
+static void enable_ast(struct device *dev, u32 time_out);
+static void kick_tx(struct device *dev);
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+# else
+static unsigned char de4x5_irq[] = {5,9,10,11};
+static int autoprobed = 0, loading_module = 0;
+#endif /* MODULE */
+
+static char name[DE4X5_NAME_LENGTH + 1];
+static int num_de4x5s = 0, num_eth = 0;
+
+/*
+** Kludge to get around the fact that the CSR addresses have different
+** offsets in the PCI and EISA boards. Also note that the ethernet address
+** PROM is accessed differently.
+*/
+static struct bus_type {
+ int bus;
+ int bus_num;
+ int device;
+ int chipset;
+ struct de4x5_srom srom;
+ int autosense;
+} bus;
+
+/*
+** Miscellaneous defines...
+*/
+#define RESET_DE4X5 {\
+ int i;\
+ i=inl(DE4X5_BMR);\
+ dce_ms_delay(1);\
+ outl(i | BMR_SWR, DE4X5_BMR);\
+ dce_ms_delay(1);\
+ outl(i, DE4X5_BMR);\
+ dce_ms_delay(1);\
+ for (i=0;i<5;i++) {inl(DE4X5_BMR); dce_ms_delay(1);}\
+ dce_ms_delay(1);\
+}
+
+
+
+int de4x5_probe(struct device *dev)
+{
+ int tmp = num_de4x5s, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else {
+ eisa_probe(dev, iobase);
+ pci_probe(dev, iobase);
+
+ if ((tmp == num_de4x5s) && (iobase != 0) && loading_module) {
+ printk("%s: de4x5_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+de4x5_hw_init(struct device *dev, u_long iobase)
+{
+ struct bus_type *lp = &bus;
+ int tmpbus, tmpchs, i, j, status=0;
+ char *tmp;
+
+ /* Ensure we're not sleeping */
+ if (lp->chipset == DC21041) {
+ outl(0, PCI_CFDA);
+ dce_ms_delay(10);
+ }
+
+ RESET_DE4X5;
+
+ if ((inl(DE4X5_STS) & (STS_TS | STS_RS)) == 0) {
+ /*
+ ** Now find out what kind of DC21040/DC21041/DC21140 board we have.
+ */
+ if (lp->bus == PCI) {
+ if (!is_not_dec) {
+ if ((lp->chipset == DC21040) || (lp->chipset == DC21041)) {
+ strcpy(name, "DE435");
+ } else if (lp->chipset == DC21140) {
+ strcpy(name, "DE500"); /* Must read the SROM here! */
+ }
+ } else {
+ strcpy(name, "UNKNOWN");
+ }
+ } else {
+ EISA_signature(name, EISA_ID0);
+ }
+
+ if (*name != '\0') { /* found a board signature */
+ dev->base_addr = iobase;
+ if (lp->bus == EISA) {
+ printk("%s: %s at %04lx (EISA slot %ld)",
+ dev->name, name, iobase, ((iobase>>12)&0x0f));
+ } else { /* PCI port address */
+ printk("%s: %s at %04lx (PCI bus %d, device %d)", dev->name, name,
+ iobase, lp->bus_num, lp->device);
+ }
+
+ printk(", h/w address ");
+ status = get_hw_addr(dev);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ tmpbus = lp->bus;
+ tmpchs = lp->chipset;
+
+ if (status == 0) {
+ struct de4x5_private *lp;
+
+ /*
+ ** Reserve a section of kernel memory for the adapter
+ ** private area and the TX/RX descriptor rings.
+ */
+ dev->priv = (void *) kmalloc(sizeof(struct de4x5_private) + ALIGN,
+ GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ /*
+ ** Align to a longword boundary
+ */
+ dev->priv = (void *)(((u_long)dev->priv + ALIGN) & ~ALIGN);
+ lp = (struct de4x5_private *)dev->priv;
+ memset(dev->priv, 0, sizeof(struct de4x5_private));
+ lp->bus = tmpbus;
+ lp->chipset = tmpchs;
+
+ /*
+ ** Choose autosensing
+ */
+ if (de4x5_autosense & AUTO) {
+ lp->autosense = AUTO;
+ } else {
+ if (lp->chipset != DC21140) {
+ if ((lp->chipset == DC21040) && (de4x5_autosense & TP_NW)) {
+ de4x5_autosense = TP;
+ }
+ if ((lp->chipset == DC21041) && (de4x5_autosense & BNC_AUI)) {
+ de4x5_autosense = BNC;
+ }
+ lp->autosense = de4x5_autosense & 0x001f;
+ } else {
+ lp->autosense = de4x5_autosense & 0x00c0;
+ }
+ }
+
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(iobase, (lp->bus == PCI ? DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE),
+ lp->adapter_name);
+
+ /*
+ ** Allocate contiguous receive buffers, long word aligned.
+ ** This could be a possible memory leak if the private area
+ ** is ever hosed.
+ */
+ for (tmp=NULL, j=0; (j<BUFF_ALLOC_RETRIES) && (tmp==NULL); j++) {
+ if ((tmp = (void *)kmalloc(RX_BUFF_SZ * NUM_RX_DESC + ALIGN,
+ GFP_KERNEL)) != NULL) {
+ tmp = (char *)(((u_long) tmp + ALIGN) & ~ALIGN);
+ for (i=0; i<NUM_RX_DESC; i++) {
+ lp->rx_ring[i].status = 0;
+ lp->rx_ring[i].des1 = RX_BUFF_SZ;
+ lp->rx_ring[i].buf = virt_to_bus(tmp + i * RX_BUFF_SZ);
+ lp->rx_ring[i].next = (u32)NULL;
+ }
+ barrier();
+ }
+ }
+
+ if (tmp != NULL) {
+ lp->rxRingSize = NUM_RX_DESC;
+ lp->txRingSize = NUM_TX_DESC;
+
+ /* Write the end of list marker to the descriptor lists */
+ lp->rx_ring[lp->rxRingSize - 1].des1 |= RD_RER;
+ lp->tx_ring[lp->txRingSize - 1].des1 |= TD_TER;
+
+ /* Tell the adapter where the TX/RX rings are located. */
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ /* Initialise the IRQ mask and Enable/Disable */
+ lp->irq_mask = IMR_RIM | IMR_TIM | IMR_TUM ;
+ lp->irq_en = IMR_NIM | IMR_AIM;
+
+ lp->tx_enable = TRUE;
+
+ if (dev->irq < 2) {
+#ifndef MODULE
+ unsigned char irqnum;
+ s32 omr;
+ autoirq_setup(0);
+
+ omr = inl(DE4X5_OMR);
+ outl(IMR_AIM|IMR_RUM, DE4X5_IMR); /* Unmask RUM interrupt */
+ outl(OMR_SR | omr, DE4X5_OMR); /* Start RX w/no descriptors */
+
+ irqnum = autoirq_report(1);
+ if (!irqnum) {
+ printk(" and failed to detect IRQ line.\n");
+ status = -ENXIO;
+ } else {
+ for (dev->irq=0,i=0; (i<sizeof(de4x5_irq)) && (!dev->irq); i++) {
+ if (irqnum == de4x5_irq[i]) {
+ dev->irq = irqnum;
+ printk(" and uses IRQ%d.\n", dev->irq);
+ }
+ }
+
+ if (!dev->irq) {
+ printk(" but incorrect IRQ line detected.\n");
+ status = -ENXIO;
+ }
+ }
+
+ outl(0, DE4X5_IMR); /* Re-mask RUM interrupt */
+
+#endif /* MODULE */
+ } else {
+ printk(" and requires IRQ%d (not probed).\n", dev->irq);
+ }
+ } else {
+ printk("%s: Kernel could not allocate RX buffer memory.\n",
+ dev->name);
+ status = -ENXIO;
+ }
+ if (status) release_region(iobase, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ } else {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ if (!status) {
+ if (de4x5_debug > 0) {
+ printk(version);
+ }
+
+ /* The DE4X5-specific entries in the device structure. */
+ dev->open = &de4x5_open;
+ dev->hard_start_xmit = &de4x5_queue_pkt;
+ dev->stop = &de4x5_close;
+ dev->get_stats = &de4x5_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &de4x5_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+
+ /* Let the adapter sleep to save power */
+ if (lp->chipset == DC21041) {
+ outl(0, DE4X5_SICR);
+ outl(CFDA_PSM, PCI_CFDA);
+ }
+ } else { /* Incorrectly initialised hardware */
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ if (lp) {
+ kfree_s(bus_to_virt(lp->rx_ring[0].buf),
+ RX_BUFF_SZ * NUM_RX_DESC + ALIGN);
+ }
+ if (dev->priv) {
+ kfree_s(dev->priv, sizeof(struct de4x5_private) + ALIGN);
+ dev->priv = NULL;
+ }
+ }
+
+ return status;
+}
+
+
+static int
+de4x5_open(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ s32 imr, omr, sts;
+
+ /*
+ ** Wake up the adapter
+ */
+ if (lp->chipset == DC21041) {
+ outl(0, PCI_CFDA);
+ dce_ms_delay(10);
+ }
+
+ if (request_irq(dev->irq, (void *)de4x5_interrupt, 0, lp->adapter_name)) {
+ printk("de4x5_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ irq2dev_map[dev->irq] = dev;
+ /*
+ ** Re-initialize the DE4X5...
+ */
+ status = de4x5_init(dev);
+
+ if (de4x5_debug > 1){
+ printk("%s: de4x5 open with irq %d\n",dev->name,dev->irq);
+ printk("\tphysical address: ");
+ for (i=0;i<6;i++){
+ printk("%2.2x:",(short)dev->dev_addr[i]);
+ }
+ printk("\n");
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%8.8lx 0x%8.8lx\n",(u_long)lp->rx_ring,(u_long)lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ",(u_long)&lp->rx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n",(u_long)&lp->rx_ring[i].status);
+ printk("TX: ");
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (u_long)&lp->tx_ring[i].status);
+ }
+ }
+ printk("...0x%8.8lx\n", (u_long)&lp->tx_ring[i].status);
+ printk("Descriptor buffers:\nRX: ");
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8x ",lp->rx_ring[i].buf);
+ }
+ }
+ printk("...0x%8.8x\n",lp->rx_ring[i].buf);
+ printk("TX: ");
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ printk("0x%8.8x ", lp->tx_ring[i].buf);
+ }
+ }
+ printk("...0x%8.8x\n", lp->tx_ring[i].buf);
+ printk("Ring size: \nRX: %d\nTX: %d\n",
+ (short)lp->rxRingSize,
+ (short)lp->txRingSize);
+ printk("\tstatus: %d\n", status);
+ }
+
+ if (!status) {
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ /* Unmask and enable DE4X5 board interrupts */
+ imr = 0;
+ UNMASK_IRQs;
+
+ /* Reset any pending (stale) interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ ENABLE_IRQs;
+ }
+ if (de4x5_debug > 1) {
+ printk("\tsts: 0x%08x\n", inl(DE4X5_STS));
+ printk("\tbmr: 0x%08x\n", inl(DE4X5_BMR));
+ printk("\timr: 0x%08x\n", inl(DE4X5_IMR));
+ printk("\tomr: 0x%08x\n", inl(DE4X5_OMR));
+ printk("\tsisr: 0x%08x\n", inl(DE4X5_SISR));
+ printk("\tsicr: 0x%08x\n", inl(DE4X5_SICR));
+ printk("\tstrr: 0x%08x\n", inl(DE4X5_STRR));
+ printk("\tsigr: 0x%08x\n", inl(DE4X5_SIGR));
+ }
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/*
+** Initialize the DE4X5 operating conditions. NB: a chip problem with the
+** DC21140 requires using perfect filtering mode for that chip. Since I can't
+** see why I'd want > 14 multicast addresses, I may change all chips to use
+** the perfect filtering mode. Keep the DMA burst length at 8: there seems
+** to be data corruption problems if it is larger (UDP errors seen from a
+** ttcp source).
+*/
+static int
+de4x5_init(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ s32 bmr, omr;
+
+ /* Lock out other processes whilst setting up the hardware */
+ set_bit(0, (void *)&dev->tbusy);
+
+ RESET_DE4X5;
+
+ bmr = inl(DE4X5_BMR);
+ bmr |= PBL_8 | DESC_SKIP_LEN | CACHE_ALIGN;
+ outl(bmr, DE4X5_BMR);
+
+ if (lp->chipset != DC21140) {
+ omr = TR_96;
+ lp->setup_f = HASH_PERF;
+ } else {
+ omr = OMR_SDP | OMR_SF;
+ lp->setup_f = PERFECT;
+ }
+ outl(virt_to_bus(lp->rx_ring), DE4X5_RRBA);
+ outl(virt_to_bus(lp->tx_ring), DE4X5_TRBA);
+
+ lp->rx_new = lp->rx_old = 0;
+ lp->tx_new = lp->tx_old = 0;
+
+ for (i = 0; i < lp->rxRingSize; i++) {
+ lp->rx_ring[i].status = R_OWN;
+ }
+
+ for (i = 0; i < lp->txRingSize; i++) {
+ lp->tx_ring[i].status = 0;
+ }
+
+ barrier();
+
+ /* Build the setup frame depending on filtering mode */
+ SetMulticastFilter(dev);
+
+ if (lp->chipset != DC21140) {
+ load_packet(dev, lp->setup_frame, HASH_F|TD_SET|SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, PERFECT_F|TD_SET|SETUP_FRAME_LEN, NULL);
+ }
+ outl(omr|OMR_ST, DE4X5_OMR);
+
+ /* Poll for completion of setup frame (interrupts are disabled for now) */
+ for (j=0, i=jiffies;(i<=jiffies+HZ/100) && (j==0);) {
+ if (lp->tx_ring[lp->tx_new].status >= 0) j=1;
+ }
+ outl(omr, DE4X5_OMR); /* Stop everything! */
+
+ if (j == 0) {
+ printk("%s: Setup frame timed out, status %08x\n", dev->name,
+ inl(DE4X5_STS));
+ status = -EIO;
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ /* Autoconfigure the connected port */
+ if (autoconf_media(dev) == 0) {
+ status = -EIO;
+ }
+
+ return 0;
+}
+
+/*
+** Writes a socket buffer address to the next available transmit descriptor
+*/
+static int
+de4x5_queue_pkt(struct sk_buff *skb, struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ s32 imr, omr, sts;
+
+ /*
+ ** Clean out the TX ring asynchronously to interrupts - sometimes the
+ ** interrupts are lost by delayed descriptor status updates relative to
+ ** the irq assertion, especially with a busy PCI bus.
+ */
+ if (set_bit(0, (void*)&dev->tbusy) == 0) {
+ cli();
+ de4x5_tx(dev);
+ dev->tbusy = 0;
+ sti();
+ }
+
+ /*
+ ** Transmitter timeout, possibly serious problems.
+ ** The 'lostMedia' threshold accounts for transient errors that
+ ** were noticed when switching media.
+ */
+ if (dev->tbusy || (lp->lostMedia > LOST_MEDIA_THRESHOLD)) {
+ u_long tickssofar = jiffies - dev->trans_start;
+ if ((tickssofar < QUEUE_PKT_TIMEOUT) &&
+ (lp->lostMedia <= LOST_MEDIA_THRESHOLD)) {
+ status = -1;
+ } else {
+ if (de4x5_debug >= 1) {
+ printk("%s: transmit timed out, status %08x, tbusy:%ld, lostMedia:%d tickssofar:%ld, resetting.\n",dev->name, inl(DE4X5_STS), dev->tbusy, lp->lostMedia, tickssofar);
+ }
+
+ /* Stop and reset the TX and RX... */
+ STOP_DE4X5;
+
+ /* Re-queue any skb's. */
+ for (i=lp->tx_old; i!=lp->tx_new; i=(++i)%lp->txRingSize) {
+ if (lp->skb[i] != NULL) {
+ if (lp->skb[i]->len != FAKE_FRAME_LEN) {
+ if (lp->tx_ring[i].status == T_OWN) {
+ dev_queue_xmit(lp->skb[i], dev, SOPRI_NORMAL);
+ } else { /* already sent */
+ dev_kfree_skb(lp->skb[i], FREE_WRITE);
+ }
+ } else {
+ dev_kfree_skb(lp->skb[i], FREE_WRITE);
+ }
+ lp->skb[i] = NULL;
+ }
+ }
+ if (skb->len != FAKE_FRAME_LEN) {
+ dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ } else {
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+
+ /* Initialise the hardware */
+ status = de4x5_init(dev);
+
+ /* Unmask DE4X5 board interrupts */
+ if (!status) {
+ /* Start here to clean stale interrupts later */
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->start = 1;
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+
+ START_DE4X5;
+
+ /* Unmask DE4X5 board interrupts */
+ imr = 0;
+ UNMASK_IRQs;
+
+ /* Clear any pending (stale) interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ ENABLE_IRQs;
+ } else {
+ printk("%s: hardware initialisation failure, status %08x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+ }
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len == FAKE_FRAME_LEN) { /* Don't TX a fake frame! */
+ dev_kfree_skb(skb, FREE_WRITE);
+ } else if (skb->len > 0) {
+ /* Enforce 1 process per h/w access */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ status = -1; /* Re-queue packet */
+ } else {
+ cli();
+ if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
+ load_packet(dev, skb->data, TD_IC | TD_LS | TD_FS | skb->len, skb);
+ if (lp->tx_enable) {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize; /* Ensure a wrap */
+ dev->trans_start = jiffies;
+
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy = 0; /* Another pkt may be queued */
+ }
+ } else { /* Ring full - re-queue */
+ status = -1;
+ }
+ sti();
+ }
+ }
+
+ return status;
+}
+
+/*
+** The DE4X5 interrupt handler.
+**
+** I/O Read/Writes through intermediate PCI bridges are never 'posted',
+** so that the asserted interrupt always has some real data to work with -
+** if these I/O accesses are ever changed to memory accesses, ensure the
+** STS write is read immediately to complete the transaction if the adapter
+** is not on bus 0. Lost interrupts can still occur when the PCI bus load
+** is high and descriptor status bits cannot be set before the associated
+** interrupt is asserted and this routine entered.
+*/
+static void
+de4x5_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct de4x5_private *lp;
+ s32 imr, omr, sts;
+ u_long iobase;
+
+ if (dev == NULL) {
+ printk ("de4x5_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct de4x5_private *)dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ DISABLE_IRQs; /* Ensure non re-entrancy */
+ dev->interrupt = MASK_INTERRUPTS;
+
+ while ((sts = inl(DE4X5_STS)) & lp->irq_mask) { /* Read IRQ status */
+ outl(sts, DE4X5_STS); /* Reset the board interrupts */
+
+ if (sts & (STS_RI | STS_RU)) /* Rx interrupt (packet[s] arrived) */
+ de4x5_rx(dev);
+
+ if (sts & (STS_TI | STS_TU)) /* Tx interrupt (packet sent) */
+ de4x5_tx(dev);
+
+ if (sts & STS_TM) /* Autosense tick */
+ de4x5_ast(dev);
+
+ if (sts & STS_LNF) { /* TP Link has failed */
+ lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
+ lp->irq_mask &= ~IMR_LFM;
+ kick_tx(dev);
+ }
+
+ if (sts & STS_SE) { /* Bus Error */
+ STOP_DE4X5;
+ printk("%s: Fatal bus error occured, sts=%#8x, device stopped.\n",
+ dev->name, sts);
+ }
+ }
+
+ if (TX_BUFFS_AVAIL && dev->tbusy) {/* Any resources available? */
+ dev->tbusy = 0; /* Clear TX busy flag */
+ mark_bh(NET_BH);
+ }
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ ENABLE_IRQs;
+ }
+
+ return;
+}
+
+static int
+de4x5_rx(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i, entry;
+ s32 status;
+ char *buf;
+
+ for (entry = lp->rx_new; lp->rx_ring[entry].status >= 0;entry = lp->rx_new) {
+ status = lp->rx_ring[entry].status;
+
+ if (status & RD_FS) { /* Remember the start of frame */
+ lp->rx_old = entry;
+ }
+
+ if (status & RD_LS) { /* Valid frame status */
+ if (status & RD_ES) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & (RD_RF | RD_TL)) lp->stats.rx_frame_errors++;
+ if (status & RD_CE) lp->stats.rx_crc_errors++;
+ if (status & RD_OF) lp->stats.rx_fifo_errors++;
+ } else { /* A valid frame received */
+ struct sk_buff *skb;
+ short pkt_len = (short)(lp->rx_ring[entry].status >> 16) - 4;
+
+ if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
+ skb->dev = dev;
+
+ skb_reserve(skb,2); /* Align */
+ if (entry < lp->rx_old) { /* Wrapped buffer */
+ short len = (lp->rxRingSize - lp->rx_old) * RX_BUFF_SZ;
+ memcpy(skb_put(skb,len), bus_to_virt(lp->rx_ring[lp->rx_old].buf), len);
+ memcpy(skb_put(skb,pkt_len-len), bus_to_virt(lp->rx_ring[0].buf), pkt_len - len);
+ } else { /* Linear buffer */
+ memcpy(skb_put(skb,pkt_len), bus_to_virt(lp->rx_ring[lp->rx_old].buf), pkt_len);
+ }
+
+ /* Push up the protocol stack */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /* Update stats */
+ lp->stats.rx_packets++;
+ for (i=1; i<DE4X5_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < (i*DE4X5_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DE4X5_PKT_STAT_SZ;
+ }
+ }
+ buf = skb->data; /* Look at the dest addr */
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s32 *)&buf[0] == -1) && (*(s16 *)&buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s32 *)&buf[0] == *(s32 *)&dev->dev_addr[0]) &&
+ (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+
+ /* Change buffer ownership for this last frame, back to the adapter */
+ for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)%lp->rxRingSize) {
+ lp->rx_ring[lp->rx_old].status = R_OWN;
+ barrier();
+ }
+ lp->rx_ring[entry].status = R_OWN;
+ barrier();
+ }
+
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) % lp->rxRingSize;
+ }
+
+ return 0;
+}
+
+/*
+** Buffer sent - check for TX buffer errors.
+*/
+static int
+de4x5_tx(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int entry;
+ s32 status;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = lp->tx_ring[entry].status;
+ if (status < 0) { /* Buffer not sent yet */
+ break;
+ } else if (status & TD_ES) { /* An error happened */
+ lp->stats.tx_errors++;
+ if (status & TD_NC) lp->stats.tx_carrier_errors++;
+ if (status & TD_LC) lp->stats.tx_window_errors++;
+ if (status & TD_UF) lp->stats.tx_fifo_errors++;
+ if (status & TD_LC) lp->stats.collisions++;
+ if (status & TD_EC) lp->pktStats.excessive_collisions++;
+ if (status & TD_DE) lp->stats.tx_aborted_errors++;
+
+ if ((status != 0x7fffffff) && /* Not setup frame */
+ (status & (TD_LO | TD_NC | TD_EC | TD_LF))) {
+ lp->lostMedia++;
+ if (lp->lostMedia > LOST_MEDIA_THRESHOLD) { /* Trip autosense */
+ kick_tx(dev);
+ }
+ } else {
+ outl(POLL_DEMAND, DE4X5_TPD); /* Restart a stalled TX */
+ }
+ } else { /* Packet sent */
+ lp->stats.tx_packets++;
+ lp->lostMedia = 0; /* Remove transient problem */
+ }
+ /* Free the buffer if it's not a setup frame. */
+ if (lp->skb[entry] != NULL) {
+ dev_kfree_skb(lp->skb[entry], FREE_WRITE);
+ lp->skb[entry] = NULL;
+ }
+
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) % lp->txRingSize;
+ }
+
+ return 0;
+}
+
+static int
+de4x5_ast(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 gep;
+
+ disable_ast(dev);
+
+ if (lp->chipset == DC21140) {
+ gep = inl(DE4X5_GEP);
+ if (((lp->media == _100Mb) && (gep & GEP_SLNK)) ||
+ ((lp->media == _10Mb) && (gep & GEP_LNP)) ||
+ ((lp->media == _10Mb) && !(gep & GEP_SLNK)) ||
+ (lp->media == NC)) {
+ if (lp->linkProb || ((lp->media == NC) && (!(gep & GEP_LNP)))) {
+ lp->lostMedia = LOST_MEDIA_THRESHOLD + 1;
+ lp->linkProb = 0;
+ kick_tx(dev);
+ } else {
+ switch(lp->media) {
+ case NC:
+ lp->linkProb = 0;
+ enable_ast(dev, DE4X5_AUTOSENSE_MS);
+ break;
+
+ case _10Mb:
+ lp->linkProb = 1; /* Flag a potential problem */
+ enable_ast(dev, 1500);
+ break;
+
+ case _100Mb:
+ lp->linkProb = 1; /* Flag a potential problem */
+ enable_ast(dev, 4000);
+ break;
+ }
+ }
+ } else {
+ lp->linkProb = 0; /* Link OK */
+ enable_ast(dev, DE4X5_AUTOSENSE_MS);
+ }
+ }
+
+ return 0;
+}
+
+static int
+de4x5_close(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 imr, omr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (de4x5_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %8.8x.\n",
+ dev->name, inl(DE4X5_STS));
+ }
+
+ /*
+ ** We stop the DE4X5 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
+
+ STOP_DE4X5;
+
+ /*
+ ** Free the associated irq
+ */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+
+ /* Put the adapter to sleep to save power */
+ if (lp->chipset == DC21041) {
+ outl(0, DE4X5_SICR);
+ outl(CFDA_PSM, PCI_CFDA);
+ }
+
+ return 0;
+}
+
+static struct enet_statistics *
+de4x5_get_stats(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->stats.rx_missed_errors = (int) (inl(DE4X5_MFC) & (MFC_OVFL | MFC_CNTR));
+
+ return &lp->stats;
+}
+
+static void load_packet(struct device *dev, char *buf, u32 flags, struct sk_buff *skb)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+
+ lp->tx_ring[lp->tx_new].buf = virt_to_bus(buf);
+ lp->tx_ring[lp->tx_new].des1 &= TD_TER;
+ lp->tx_ring[lp->tx_new].des1 |= flags;
+ lp->skb[lp->tx_new] = skb;
+ barrier();
+ lp->tx_ring[lp->tx_new].status = T_OWN;
+ barrier();
+
+ return;
+}
+/*
+** Set or clear the multicast filter for this adaptor.
+** num_addrs == -1 Promiscuous mode, receive all packets - now supported.
+** Can also use the ioctls.
+** num_addrs == 0 Normal mode, clear multicast list
+** num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+** best-effort filtering.
+** num_addrs == HASH_TABLE_LEN
+** Set all multicast bits (pass all multicasts).
+*/
+static void
+set_multicast_list(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ /* First, double check that the adapter is open */
+ if (irq2dev_map[dev->irq] != NULL) {
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ u32 omr;
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ } else {
+ SetMulticastFilter(dev);
+ if (lp->setup_f == HASH_PERF) {
+ load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ }
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->trans_start = jiffies;
+ }
+ }
+
+ return;
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Little endian crc one liner from Matt Thomas, DEC.
+*/
+static void SetMulticastFilter(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ u_long iobase = dev->base_addr;
+ int i, j, bit, byte;
+ u16 hashcode;
+ u32 omr, crc, poly = CRC_POLYNOMIAL_LE;
+ char *pa;
+ unsigned char *addrs;
+
+ omr = inl(DE4X5_OMR);
+ omr &= ~OMR_PR;
+ pa = build_setup_frame(dev, ALL); /* Build the basic frame */
+
+ if ((dev->flags & IFF_ALLMULTI) || (dev->mc_count > 14)) {
+ omr |= OMR_PM; /* Pass all multicasts */
+ } else if (lp->setup_f == HASH_PERF) {
+ /* Now update the MCA table */
+ for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & HASH_BITS; /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ byte <<= 1; /* calc offset into setup frame */
+ if (byte & 0x02) {
+ byte -= 1;
+ }
+ lp->setup_frame[byte] |= bit;
+ }
+ }
+ } else { /* Perfect filtering */
+ for (j=0; j<dev->mc_count; j++) {
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ for (i=0; i<ETH_ALEN; i++) {
+ *(pa + (i&1)) = *addrs++;
+ if (i & 0x01) pa += 4;
+ }
+ }
+ }
+ outl(omr, DE4X5_OMR);
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard. Upto 15 EISA devices are supported.
+*/
+static void eisa_probe(struct device *dev, u_long ioaddr)
+{
+ int i, maxSlots, status;
+ u_short vendor, device;
+ s32 cfid;
+ u_long iobase;
+ struct bus_type *lp = &bus;
+ char name[DE4X5_STRLEN];
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if ((ioaddr < 0x1000) && (ioaddr > 0)) return; /* PCI MODULE special */
+
+ lp->bus = EISA;
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+
+ for (status = -ENODEV; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ cfid = inl(PCI_CFID);
+ device = (u_short)(cfid >> 16);
+ vendor = (u_short) cfid;
+
+ lp->bus = EISA;
+ lp->chipset = device;
+ if (DevicePresent(EISA_APROM) == 0) {
+ /* Write the PCI Configuration Registers */
+ outl(PCI_COMMAND_IO | PCI_COMMAND_MASTER, PCI_CFCS);
+ outl(0x00004000, PCI_CFLT);
+ outl(iobase, PCI_CBIO);
+
+ if (check_region(iobase, DE4X5_EISA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** PCI bus I/O device probe
+** NB: PCI I/O accesses and Bus Mastering are enabled by the PCI BIOS, not
+** the driver. Some PCI BIOS's, pre V2.1, need the slot + features to be
+** enabled by the user first in the set up utility. Hence we just check for
+** enabled features and silently ignore the card if they're not.
+**
+** STOP PRESS: Some BIOS's __require__ the driver to enable the bus mastering
+** bit. Here, check for I/O accesses and then set BM. If you put the card in
+** a non BM slot, you're on your own (and complain to the PC vendor that your
+** PC doesn't conform to the PCI standard)!
+*/
+#define PCI_DEVICE (dev_num << 3)
+#define PCI_LAST_DEV 32
+
+static void pci_probe(struct device *dev, u_long ioaddr)
+{
+ u_char irq;
+ u_char pb, pbus, dev_num, dnum, dev_fn;
+ u_short vendor, device, index, status;
+ u_int class = DE4X5_CLASS_CODE;
+ u_int iobase;
+ struct bus_type *lp = &bus;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+
+ if (pcibios_present()) {
+ lp->bus = PCI;
+
+ if (ioaddr < 0x1000) {
+ pbus = (u_short)(ioaddr >> 8);
+ dnum = (u_short)(ioaddr & 0xff);
+ } else {
+ pbus = 0;
+ dnum = 0;
+ }
+
+ for (index=0;
+ (pcibios_find_class(class, index, &pb, &dev_fn)!= PCIBIOS_DEVICE_NOT_FOUND);
+ index++) {
+ dev_num = PCI_SLOT(dev_fn);
+
+ if ((!pbus && !dnum) || ((pbus == pb) && (dnum == dev_num))) {
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_DEVICE_ID, &device);
+ if (is_DC21040 || is_DC21041 || is_DC21140) {
+ /* Set the device number information */
+ lp->device = dev_num;
+ lp->bus_num = pb;
+
+ /* Set the chipset information */
+ lp->chipset = device;
+
+ /* Get the board I/O address */
+ pcibios_read_config_dword(pb, PCI_DEVICE, PCI_BASE_ADDRESS_0, &iobase);
+ iobase &= CBIO_MASK;
+
+ /* Fetch the IRQ to be used */
+ pcibios_read_config_byte(pb, PCI_DEVICE, PCI_INTERRUPT_LINE, &irq);
+
+ /* Check if I/O accesses and Bus Mastering are enabled */
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ if (status & PCI_COMMAND_IO) {
+ if (!(status & PCI_COMMAND_MASTER)) {
+ status |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pb, PCI_DEVICE, PCI_COMMAND, status);
+ pcibios_read_config_word(pb, PCI_DEVICE, PCI_COMMAND, &status);
+ }
+ if (status & PCI_COMMAND_MASTER) {
+ if ((DevicePresent(DE4X5_APROM) == 0) || is_not_dec) {
+ if (check_region(iobase, DE4X5_PCI_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ dev->irq = irq;
+ if ((status = de4x5_hw_init(dev, iobase)) == 0) {
+ num_de4x5s++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name, (u_short)iobase);
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == DE4X5_NDA) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current position first.
+ */
+ if ((dev->base_addr == 0) && (num_de4x5s > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == DE4X5_NDA) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &de4x5_probe; /* initialisation routine */
+ num_de4x5s++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (; (tmp->next!=NULL) && (tmp->base_addr!=DE4X5_NDA); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == DE4X5_NDA)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Auto configure the media here rather than setting the port at compile
+** time. This routine is called by de4x5_init() when a loss of media is
+** detected (excessive collisions, loss of carrier, no carrier or link fail
+** [TP]) to check whether the user has been sneaky and changed the port on us.
+*/
+static int autoconf_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->tx_enable = YES;
+ if (de4x5_debug > 0 ) {
+ if (lp->chipset != DC21140) {
+ printk("%s: Searching for media... ",dev->name);
+ } else {
+ printk("%s: Searching for mode... ",dev->name);
+ }
+ }
+
+ if (lp->chipset == DC21040) {
+ lp->media = (lp->autosense == AUTO ? TP : lp->autosense);
+ dc21040_autoconf(dev);
+ } else if (lp->chipset == DC21041) {
+ lp->media = (lp->autosense == AUTO ? TP_NW : lp->autosense);
+ dc21041_autoconf(dev);
+ } else if (lp->chipset == DC21140) {
+ disable_ast(dev);
+ lp->media = (lp->autosense == AUTO ? _10Mb : lp->autosense);
+ dc21140_autoconf(dev);
+ }
+
+ if (de4x5_debug > 0 ) {
+ if (lp->chipset != DC21140) {
+ printk("media is %s\n", (lp->media == NC ? "unconnected!" :
+ (lp->media == TP ? "TP." :
+ (lp->media == ANS ? "TP/Nway." :
+ (lp->media == BNC ? "BNC." :
+ (lp->media == AUI ? "AUI." :
+ "BNC/AUI."
+ ))))));
+ } else {
+ printk("mode is %s\n",(lp->media == NC ? "link down.":
+ (lp->media == _100Mb ? "100Mb/s." :
+ (lp->media == _10Mb ? "10Mb/s." :
+ "\?\?\?"
+ ))));
+ }
+ }
+
+ if (lp->media) {
+ lp->lostMedia = 0;
+ inl(DE4X5_MFC); /* Zero the lost frames counter */
+ if ((lp->media == TP) || (lp->media == ANS)) {
+ lp->irq_mask |= IMR_LFM;
+ }
+ }
+ dce_ms_delay(10);
+
+ return (lp->media);
+}
+
+static void dc21040_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, linkBad;
+ s32 sisr = 0, t_3s = 3000;
+
+ switch (lp->media) {
+ case TP:
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ for (linkBad=1,i=0;(i<t_3s) && linkBad && !(sisr & SISR_NCR);i++) {
+ if (((sisr = inl(DE4X5_SISR)) & SISR_LKF) == 0) linkBad = 0;
+ dce_ms_delay(1);
+ }
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = BNC_AUI;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case BNC:
+ case AUI:
+ case BNC_AUI:
+ reset_init_sia(dev, 0x8f09, 0x0705, 0x0006);
+ dce_ms_delay(500);
+ linkBad = ping_media(dev);
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = EXT_SIA;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case EXT_SIA:
+ reset_init_sia(dev, 0x3041, 0x0000, 0x0006);
+ dce_ms_delay(500);
+ linkBad = ping_media(dev);
+ if (linkBad && (lp->autosense == AUTO)) {
+ lp->media = NC;
+ dc21040_autoconf(dev);
+ }
+ break;
+
+ case NC:
+#ifndef __alpha__
+ reset_init_sia(dev, 0x8f01, 0xffff, 0x0000);
+ break;
+#else
+ /* JAE: for Alpha, default to BNC/AUI, *not* TP */
+ reset_init_sia(dev, 0x8f09, 0x0705, 0x0006);
+#endif /* i386 */
+ }
+
+ return;
+}
+
+/*
+** Autoconfigure the media when using the DC21041. AUI needs to be tested
+** before BNC, because the BNC port will indicate activity if it's not
+** terminated correctly. The only way to test for that is to place a loopback
+** packet onto the network and watch for errors.
+*/
+static void dc21041_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, irqs, irq_mask, omr;
+
+ switch (lp->media) {
+ case TP_NW:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FD, DE4X5_OMR);
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xffff, 0x0008, 2400);
+ if (sts & STS_LNP) {
+ lp->media = ANS;
+ } else {
+ lp->media = AUI;
+ }
+ dc21041_autoconf(dev);
+ break;
+
+ case ANS:
+ irqs = STS_LNP;
+ irq_mask = IMR_LPM;
+ sts = test_ans(dev, irqs, irq_mask, 3000);
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ lp->media = TP;
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case TP:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for TP */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = STS_LNF | STS_LNP;
+ irq_mask = IMR_LFM | IMR_LPM;
+ sts = test_media(dev, irqs, irq_mask, 0xef01, 0xff3f, 0x0008, 2400);
+ if (!(sts & STS_LNP) && (lp->autosense == AUTO)) {
+ if (inl(DE4X5_SISR) & SISR_NRA) { /* Non selected port activity */
+ lp->media = AUI;
+ } else {
+ lp->media = BNC;
+ }
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case AUI:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for AUI */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf7fd, 0x000e, 1000);
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = BNC;
+ dc21041_autoconf(dev);
+ }
+ break;
+
+ case BNC:
+ omr = inl(DE4X5_OMR); /* Set up half duplex for BNC */
+ outl(omr & ~OMR_FD, DE4X5_OMR);
+ irqs = 0;
+ irq_mask = 0;
+ sts = test_media(dev, irqs, irq_mask, 0xef09, 0xf7fd, 0x0006, 1000);
+ if (!(inl(DE4X5_SISR) & SISR_SRA) && (lp->autosense == AUTO)) {
+ lp->media = NC;
+ } else { /* Ensure media connected */
+ if (ping_media(dev)) lp->media = NC;
+ }
+ break;
+
+ case NC:
+ omr = inl(DE4X5_OMR); /* Set up full duplex for the autonegotiate */
+ outl(omr | OMR_FD, DE4X5_OMR);
+ reset_init_sia(dev, 0xef01, 0xffff, 0x0008);/* Initialise the SIA */
+ break;
+ }
+
+ return;
+}
+
+/*
+** Reduced feature version (temporary I hope)
+*/
+static void dc21140_autoconf(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 omr;
+
+ switch(lp->media) {
+ case _100Mb: /* Set 100Mb/s, MII Port with PCS Function and Scrambler */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
+ omr |= (de4x5_full_duplex ? OMR_FD : 0); /* Set up Full Duplex */
+ outl(omr | OMR_PS | OMR_HBD | OMR_PCS | OMR_SCR, DE4X5_OMR);
+ outl(GEP_FDXD | GEP_MODE, DE4X5_GEP);
+ break;
+
+ case _10Mb: /* Set conventional 10Mb/s ENDEC interface */
+ omr = (inl(DE4X5_OMR) & ~(OMR_PS | OMR_HBD | OMR_TTM | OMR_PCS | OMR_SCR));
+ omr |= (de4x5_full_duplex ? OMR_FD : 0); /* Set up Full Duplex */
+ outl(omr | OMR_TTM, DE4X5_OMR);
+ outl(GEP_FDXD, DE4X5_GEP);
+ break;
+ }
+
+ return;
+}
+
+static int
+test_media(struct device *dev, s32 irqs, s32 irq_mask, s32 csr13, s32 csr14, s32 csr15, s32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, time, csr12;
+
+ reset_init_sia(dev, csr13, csr14, csr15);
+
+ /* Set link_fail_inhibit_timer */
+ load_ms_timer(dev, msec);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* clear csr12 NRA and SRA bits */
+ csr12 = inl(DE4X5_SISR);
+ outl(csr12, DE4X5_SISR);
+
+ /* Poll for timeout - timer interrupt doesn't work correctly */
+ do {
+ time = inl(DE4X5_GPT) & GPT_VAL;
+ sts = inl(DE4X5_STS);
+ } while ((time != 0) && !(sts & irqs));
+
+ sts = inl(DE4X5_STS);
+
+ return sts;
+}
+/*
+static int test_sym_link(struct device *dev, u32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u32 gep, time;
+
+ / * Set link_fail_inhibit_timer * /
+ load_ms_timer(dev, msec);
+
+ / * Poll for timeout or SYM_LINK=0 * /
+ do {
+ time = inl(DE4X5_GPT) & GPT_VAL;
+ gep = inl(DE4X5_GEP) & (GEP_SLNK | GEP_LNP);
+ } while ((time > 0) && (gep & GEP_SLNK));
+
+ return gep;
+}
+*/
+/*
+** Send a packet onto the media and watch for send errors that indicate the
+** media is bad or unconnected.
+*/
+static int ping_media(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, entry, linkBad;
+ s32 omr, t_3s = 4000;
+ char frame[64];
+
+ create_packet(dev, frame, sizeof(frame));
+
+ entry = lp->tx_new; /* Remember the ring position */
+ load_packet(dev, frame, TD_LS | TD_FS | sizeof(frame),NULL);
+
+ omr = inl(DE4X5_OMR);
+ outl(omr|OMR_ST, DE4X5_OMR);
+
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ lp->tx_old = lp->tx_new;
+
+ /* Poll for completion of frame (interrupts are disabled for now)... */
+ for (linkBad=1,i=0;(i<t_3s) && linkBad;i++) {
+ if ((inl(DE4X5_SISR) & SISR_NCR) == 1) break;
+ if (lp->tx_ring[entry].status >= 0) linkBad=0;
+ dce_ms_delay(1);
+ }
+ outl(omr, DE4X5_OMR);
+
+ return ((linkBad || (lp->tx_ring[entry].status & TD_ES)) ? 1 : 0);
+}
+
+/*
+** Check the Auto Negotiation State. Return OK when a link pass interrupt
+** is received and the auto-negotiation status is NWAY OK.
+*/
+static int test_ans(struct device *dev, s32 irqs, s32 irq_mask, s32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 sts, ans;
+
+ outl(irq_mask, DE4X5_IMR);
+
+ /* Set timeout limit */
+ load_ms_timer(dev, msec);
+
+ /* clear all pending interrupts */
+ sts = inl(DE4X5_STS);
+ outl(sts, DE4X5_STS);
+
+ /* Poll for interrupts */
+ do {
+ ans = inl(DE4X5_SISR) & SISR_ANS;
+ sts = inl(DE4X5_STS);
+ } while (!(sts & irqs) && (ans ^ ANS_NWOK) != 0);
+
+ return ((sts & STS_LNP) && ((ans ^ ANS_NWOK) == 0) ? STS_LNP : 0);
+}
+
+/*
+**
+*/
+static void reset_init_sia(struct device *dev, s32 sicr, s32 strr, s32 sigr)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ RESET_SIA;
+ outl(sigr, DE4X5_SIGR);
+ outl(strr, DE4X5_STRR);
+ outl(sicr, DE4X5_SICR);
+
+ return;
+}
+
+/*
+** Load the timer on the DC21041 and 21140. Max time is 13.42 secs.
+*/
+static void load_ms_timer(struct device *dev, u32 msec)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ s32 i = 2048, j;
+
+ if (lp->chipset == DC21140) {
+ j = inl(DE4X5_OMR);
+ if ((j & OMR_TTM) && (j & OMR_PS)) { /* 10Mb/s MII */
+ i = 8192;
+ } else if ((~j & OMR_TTM) && (j & OMR_PS)) { /* 100Mb/s MII */
+ i = 819;
+ }
+ }
+
+ outl((s32)(msec * 10000)/i, DE4X5_GPT);
+
+ return;
+}
+
+/*
+** Create an Ethernet packet with an invalid CRC
+*/
+static void create_packet(struct device *dev, char *frame, int len)
+{
+ int i;
+ char *buf = frame;
+
+ for (i=0; i<ETH_ALEN; i++) { /* Use this source address */
+ *buf++ = dev->dev_addr[i];
+ }
+ for (i=0; i<ETH_ALEN; i++) { /* Use this destination address */
+ *buf++ = dev->dev_addr[i];
+ }
+
+ *buf++ = 0; /* Packet length (2 bytes) */
+ *buf++ = 1;
+
+ return;
+}
+
+/*
+** Known delay in microseconds
+*/
+static void dce_us_delay(u32 usec)
+{
+ udelay(usec);
+
+ return;
+}
+
+/*
+** Known delay in milliseconds, in millisecond steps.
+*/
+static void dce_ms_delay(u32 msec)
+{
+ u_int i;
+
+ for (i=0; i<msec; i++) {
+ dce_us_delay(1000);
+ }
+
+ return;
+}
+
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(char *name, s32 eisa_id)
+{
+ u_int i;
+ const char *signatures[] = DE4X5_SIGNATURE;
+ char ManCode[DE4X5_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status; /* return the device name string */
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DIGITAL network adapter products.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+
+static int DevicePresent(u_long aprom_addr)
+{
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ } dev;
+ char data;
+ int i, j, tmp, status = 0;
+ short sigLength;
+ struct bus_type *lp = &bus;
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ if (lp->chipset == DC21040) {
+ for (i=0,j=0;(j<sigLength) && (i<PROBE_LENGTH+sigLength-1);i++) {
+ if (lp->bus == PCI) {
+ while ((tmp = inl(aprom_addr)) < 0);
+ data = (char)tmp;
+ } else {
+ data = inb(aprom_addr);
+ }
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) {
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ } else { /* use new srom */
+ short *p = (short *)&lp->srom;
+ for (i=0; i<(sizeof(struct de4x5_srom)>>1); i++) {
+ *p++ = srom_rd(aprom_addr, i);
+ }
+ }
+
+ return status;
+}
+
+static int get_hw_addr(struct device *dev)
+{
+ u_long iobase = dev->base_addr;
+ int i, k, tmp, status = 0;
+ u_short j,chksum;
+ struct bus_type *lp = &bus;
+
+ for (i=0,k=0,j=0;j<3;j++) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_char) tmp;
+ dev->dev_addr[i++] = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ k += (u_short) (tmp << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ } else {
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
+ dev->dev_addr[i] = (u_char) lp->srom.ieee_addr[i]; i++;
+ }
+ } else {
+ k += (u_char) (tmp = inb(EISA_APROM));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(EISA_APROM)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+ }
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+
+ if (lp->bus == PCI) {
+ if (lp->chipset == DC21040) {
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum = (u_char) tmp;
+ while ((tmp = inl(DE4X5_APROM)) < 0);
+ chksum |= (u_short) (tmp << 8);
+ if (k != chksum) status = -1;
+ }
+ } else {
+ chksum = (u_char) inb(EISA_APROM);
+ chksum |= (u_short) (inb(EISA_APROM) << 8);
+ if (k != chksum) status = -1;
+ }
+
+
+ return status;
+}
+
+/*
+** SROM Read
+*/
+static short srom_rd(u_long addr, u_char offset)
+{
+ sendto_srom(SROM_RD | SROM_SR, addr);
+
+ srom_latch(SROM_RD | SROM_SR | DT_CS, addr);
+ srom_command(SROM_RD | SROM_SR | DT_IN | DT_CS, addr);
+ srom_address(SROM_RD | SROM_SR | DT_CS, addr, offset);
+
+ return srom_data(SROM_RD | SROM_SR | DT_CS, addr);
+}
+
+static void srom_latch(u_int command, u_long addr)
+{
+ sendto_srom(command, addr);
+ sendto_srom(command | DT_CLK, addr);
+ sendto_srom(command, addr);
+
+ return;
+}
+
+static void srom_command(u_int command, u_long addr)
+{
+ srom_latch(command, addr);
+ srom_latch(command, addr);
+ srom_latch((command & 0x0000ff00) | DT_CS, addr);
+
+ return;
+}
+
+static void srom_address(u_int command, u_long addr, u_char offset)
+{
+ int i;
+ char a;
+
+ a = (char)(offset << 2);
+ for (i=0; i<6; i++, a <<= 1) {
+ srom_latch(command | ((a < 0) ? DT_IN : 0), addr);
+ }
+ dce_us_delay(1);
+
+ i = (getfrom_srom(addr) >> 3) & 0x01;
+ if (i != 0) {
+ printk("Bad SROM address phase.....\n");
+/* printk(".");*/
+ }
+
+ return;
+}
+
+static short srom_data(u_int command, u_long addr)
+{
+ int i;
+ short word = 0;
+ s32 tmp;
+
+ for (i=0; i<16; i++) {
+ sendto_srom(command | DT_CLK, addr);
+ tmp = getfrom_srom(addr);
+ sendto_srom(command, addr);
+
+ word = (word << 1) | ((tmp >> 3) & 0x01);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return word;
+}
+
+/*
+static void srom_busy(u_int command, u_long addr)
+{
+ sendto_srom((command & 0x0000ff00) | DT_CS, addr);
+
+ while (!((getfrom_srom(addr) >> 3) & 0x01)) {
+ dce_ms_delay(1);
+ }
+
+ sendto_srom(command & 0x0000ff00, addr);
+
+ return;
+}
+*/
+
+static void sendto_srom(u_int command, u_long addr)
+{
+ outl(command, addr);
+ dce_us_delay(1);
+
+ return;
+}
+
+static int getfrom_srom(u_long addr)
+{
+ s32 tmp;
+
+ tmp = inl(addr);
+ dce_us_delay(1);
+
+ return tmp;
+}
+
+static char *build_setup_frame(struct device *dev, int mode)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ int i;
+ char *pa = lp->setup_frame;
+
+ /* Initialise the setup frame */
+ if (mode == ALL) {
+ memset(lp->setup_frame, 0, SETUP_FRAME_LEN);
+ }
+
+ if (lp->setup_f == HASH_PERF) {
+ for (pa=lp->setup_frame+IMPERF_PA_OFFSET, i=0; i<ETH_ALEN; i++) {
+ *(pa + i) = dev->dev_addr[i]; /* Host address */
+ if (i & 0x01) pa += 2;
+ }
+ *(lp->setup_frame + (HASH_TABLE_LEN >> 3) - 3) = 0x80; /* B'cast address */
+ } else {
+ for (i=0; i<ETH_ALEN; i++) { /* Host address */
+ *(pa + (i&1)) = dev->dev_addr[i];
+ if (i & 0x01) pa += 4;
+ }
+ for (i=0; i<ETH_ALEN; i++) { /* Broadcast address */
+ *(pa + (i&1)) = (char) 0xff;
+ if (i & 0x01) pa += 4;
+ }
+ }
+
+ return pa; /* Points to the next entry */
+}
+
+static void enable_ast(struct device *dev, u32 time_out)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->irq_mask |= IMR_TMM;
+ outl(lp->irq_mask, DE4X5_IMR);
+ load_ms_timer(dev, time_out);
+
+ return;
+}
+
+static void disable_ast(struct device *dev)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+
+ lp->irq_mask &= ~IMR_TMM;
+ outl(lp->irq_mask, DE4X5_IMR);
+ load_ms_timer(dev, 0);
+
+ return;
+}
+
+static void kick_tx(struct device *dev)
+{
+ struct sk_buff *skb;
+
+ if ((skb = alloc_skb(0, GFP_ATOMIC)) != NULL) {
+ skb->len= FAKE_FRAME_LEN;
+ skb->arp=1;
+ skb->dev=dev;
+ dev_queue_xmit(skb, dev, SOPRI_NORMAL);
+ }
+
+ return;
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+*/
+static int de4x5_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct de4x5_private *lp = (struct de4x5_private *)dev->priv;
+ struct de4x5_ioctl *ioc = (struct de4x5_ioctl *) &rq->ifr_data;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ s32 omr;
+ union {
+ u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
+ u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case DE4X5_GET_HWADDR: /* Get the hardware address */
+ ioc->len = ETH_ALEN;
+ status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
+ if (status)
+ break;
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+
+ break;
+ case DE4X5_SET_HWADDR: /* Set the hardware address */
+ status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN);
+ if (status)
+ break;
+ status = -EPERM;
+ if (!suser())
+ break;
+ status = 0;
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ build_setup_frame(dev, PHYS_ADDR_ONLY);
+ /* Set up the descriptor and give ownership to the card */
+ while (set_bit(0, (void *)&dev->tbusy) != 0);/* Wait for lock to free*/
+ if (lp->setup_f == HASH_PERF) {
+ load_packet(dev, lp->setup_frame, TD_IC | HASH_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ } else {
+ load_packet(dev, lp->setup_frame, TD_IC | PERFECT_F | TD_SET |
+ SETUP_FRAME_LEN, NULL);
+ }
+ lp->tx_new = (++lp->tx_new) % lp->txRingSize;
+ outl(POLL_DEMAND, DE4X5_TPD); /* Start the TX */
+ dev->tbusy = 0; /* Unlock the TX ring */
+
+ break;
+ case DE4X5_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PR;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr &= ~OMR_PR;
+ outb(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case DE4X5_GET_MCA: /* Get the multicast address table */
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ status = verify_area(VERIFY_WRITE, ioc->data, ioc->len);
+ if (status)
+ break;
+ memcpy_tofs(ioc->data, lp->setup_frame, ioc->len);
+
+ break;
+ case DE4X5_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (ioc->len != HASH_TABLE_LEN) { /* MCA changes */
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN * ioc->len))) {
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ omr = inl(DE4X5_OMR);
+ omr |= OMR_PM;
+ outl(omr, DE4X5_OMR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_STATS: /* Get the driver statistics */
+ ioc->len = sizeof(lp->pktStats);
+ status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len);
+ if (status)
+ break;
+
+ cli();
+ memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ sti();
+
+ break;
+ case DE4X5_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_OMR: /* Get the OMR Register contents */
+ tmp.addr[0] = inl(DE4X5_OMR);
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, 1))) {
+ memcpy_tofs(ioc->data, tmp.addr, 1);
+ }
+
+ break;
+ case DE4X5_SET_OMR: /* Set the OMR Register contents */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, 1))) {
+ memcpy_fromfs(tmp.addr, ioc->data, 1);
+ outl(tmp.addr[0], DE4X5_OMR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DE4X5_GET_REG: /* Get the DE4X5 Registers */
+ j = 0;
+ tmp.lval[0] = inl(DE4X5_STS); j+=4;
+ tmp.lval[1] = inl(DE4X5_BMR); j+=4;
+ tmp.lval[2] = inl(DE4X5_IMR); j+=4;
+ tmp.lval[3] = inl(DE4X5_OMR); j+=4;
+ tmp.lval[4] = inl(DE4X5_SISR); j+=4;
+ tmp.lval[5] = inl(DE4X5_SICR); j+=4;
+ tmp.lval[6] = inl(DE4X5_STRR); j+=4;
+ tmp.lval[7] = inl(DE4X5_SIGR); j+=4;
+ ioc->len = j;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+ break;
+
+#define DE4X5_DUMP 0x0f /* Dump the DE4X5 Status */
+
+ case DE4X5_DUMP:
+ j = 0;
+ tmp.addr[j++] = dev->irq;
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[j++] = dev->dev_addr[i];
+ }
+ tmp.addr[j++] = lp->rxRingSize;
+ tmp.lval[j>>2] = (long)lp->rx_ring; j+=4;
+ tmp.lval[j>>2] = (long)lp->tx_ring; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->rx_ring[i].status; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (long)&lp->tx_ring[i].status; j+=4;
+
+ for (i=0;i<lp->rxRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (s32)lp->rx_ring[i].buf; j+=4;
+ for (i=0;i<lp->txRingSize-1;i++){
+ if (i < 3) {
+ tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+ }
+ }
+ tmp.lval[j>>2] = (s32)lp->tx_ring[i].buf; j+=4;
+
+ for (i=0;i<lp->rxRingSize;i++){
+ tmp.lval[j>>2] = lp->rx_ring[i].status; j+=4;
+ }
+ for (i=0;i<lp->txRingSize;i++){
+ tmp.lval[j>>2] = lp->tx_ring[i].status; j+=4;
+ }
+
+ tmp.lval[j>>2] = inl(DE4X5_STS); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_BMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_IMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_OMR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SISR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SICR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_STRR); j+=4;
+ tmp.lval[j>>2] = inl(DE4X5_SIGR); j+=4;
+
+ tmp.addr[j++] = lp->txRingSize;
+ tmp.addr[j++] = dev->tbusy;
+
+ ioc->len = j;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device thisDE4X5 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x2000, 10, /* I/O address, IRQ */
+ 0, 0, 0, NULL, de4x5_probe };
+
+static int io=0x000b; /* EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=10; /* or use the insmod io= irq= options */
+
+int
+init_module(void)
+{
+ thisDE4X5.base_addr=io;
+ thisDE4X5.irq=irq;
+ if (register_netdev(&thisDE4X5) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ struct de4x5_private *lp = (struct de4x5_private *) thisDE4X5.priv;
+
+ if (lp) {
+ kfree_s(bus_to_virt(lp->rx_ring[0].buf), RX_BUFF_SZ * NUM_RX_DESC + ALIGN);
+ }
+ kfree_s(thisDE4X5.priv, sizeof(struct de4x5_private) + ALIGN);
+ thisDE4X5.priv = NULL;
+
+ release_region(thisDE4X5.base_addr, (lp->bus == PCI ?
+ DE4X5_PCI_TOTAL_SIZE :
+ DE4X5_EISA_TOTAL_SIZE));
+ unregister_netdev(&thisDE4X5);
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de4x5.c"
+ * End:
+ */
+
+
diff --git a/i386/i386at/gpl/linux/net/de4x5.h b/i386/i386at/gpl/linux/net/de4x5.h
new file mode 100644
index 00000000..b0ee43ea
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de4x5.h
@@ -0,0 +1,645 @@
+/*
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of the
+ GNU Public License, incorporated herein by reference.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+*/
+
+/*
+** DC21040 CSR<1..15> Register Address Map
+*/
+#define DE4X5_BMR iobase+(0x000 << lp->bus) /* Bus Mode Register */
+#define DE4X5_TPD iobase+(0x008 << lp->bus) /* Transmit Poll Demand Reg */
+#define DE4X5_RPD iobase+(0x010 << lp->bus) /* Receive Poll Demand Reg */
+#define DE4X5_RRBA iobase+(0x018 << lp->bus) /* RX Ring Base Address Reg */
+#define DE4X5_TRBA iobase+(0x020 << lp->bus) /* TX Ring Base Address Reg */
+#define DE4X5_STS iobase+(0x028 << lp->bus) /* Status Register */
+#define DE4X5_OMR iobase+(0x030 << lp->bus) /* Operation Mode Register */
+#define DE4X5_IMR iobase+(0x038 << lp->bus) /* Interrupt Mask Register */
+#define DE4X5_MFC iobase+(0x040 << lp->bus) /* Missed Frame Counter */
+#define DE4X5_APROM iobase+(0x048 << lp->bus) /* Ethernet Address PROM */
+#define DE4X5_BROM iobase+(0x048 << lp->bus) /* Boot ROM Register */
+#define DE4X5_SROM iobase+(0x048 << lp->bus) /* Serial ROM Register */
+#define DE4X5_DDR iobase+(0x050 << lp->bus) /* Data Diagnostic Register */
+#define DE4X5_FDR iobase+(0x058 << lp->bus) /* Full Duplex Register */
+#define DE4X5_GPT iobase+(0x058 << lp->bus) /* General Purpose Timer Reg.*/
+#define DE4X5_GEP iobase+(0x060 << lp->bus) /* General Purpose Register */
+#define DE4X5_SISR iobase+(0x060 << lp->bus) /* SIA Status Register */
+#define DE4X5_SICR iobase+(0x068 << lp->bus) /* SIA Connectivity Register */
+#define DE4X5_STRR iobase+(0x070 << lp->bus) /* SIA TX/RX Register */
+#define DE4X5_SIGR iobase+(0x078 << lp->bus) /* SIA General Register */
+
+/*
+** EISA Register Address Map
+*/
+#define EISA_ID iobase+0x0c80 /* EISA ID Registers */
+#define EISA_ID0 iobase+0x0c80 /* EISA ID Register 0 */
+#define EISA_ID1 iobase+0x0c81 /* EISA ID Register 1 */
+#define EISA_ID2 iobase+0x0c82 /* EISA ID Register 2 */
+#define EISA_ID3 iobase+0x0c83 /* EISA ID Register 3 */
+#define EISA_CR iobase+0x0c84 /* EISA Control Register */
+#define EISA_REG0 iobase+0x0c88 /* EISA Configuration Register 0 */
+#define EISA_REG1 iobase+0x0c89 /* EISA Configuration Register 1 */
+#define EISA_REG2 iobase+0x0c8a /* EISA Configuration Register 2 */
+#define EISA_REG3 iobase+0x0c8f /* EISA Configuration Register 3 */
+#define EISA_APROM iobase+0x0c90 /* Ethernet Address PROM */
+
+/*
+** PCI/EISA Configuration Registers Address Map
+*/
+#define PCI_CFID iobase+0x0008 /* PCI Configuration ID Register */
+#define PCI_CFCS iobase+0x000c /* PCI Command/Status Register */
+#define PCI_CFRV iobase+0x0018 /* PCI Revision Register */
+#define PCI_CFLT iobase+0x001c /* PCI Latency Timer Register */
+#define PCI_CBIO iobase+0x0028 /* PCI Base I/O Register */
+#define PCI_CBMA iobase+0x002c /* PCI Base Memory Address Register */
+#define PCI_CBER iobase+0x0030 /* PCI Expansion ROM Base Address Reg. */
+#define PCI_CFIT iobase+0x003c /* PCI Configuration Interrupt Register */
+#define PCI_CFDA iobase+0x0040 /* PCI Driver Area Register */
+
+/*
+** EISA Configuration Register 0 bit definitions
+*/
+#define ER0_BSW 0x80 /* EISA Bus Slave Width, 1: 32 bits */
+#define ER0_BMW 0x40 /* EISA Bus Master Width, 1: 32 bits */
+#define ER0_EPT 0x20 /* EISA PREEMPT Time, 0: 23 BCLKs */
+#define ER0_ISTS 0x10 /* Interrupt Status (X) */
+#define ER0_LI 0x08 /* Latch Interrupts */
+#define ER0_INTL 0x06 /* INTerrupt Level */
+#define ER0_INTT 0x01 /* INTerrupt Type, 0: Level, 1: Edge */
+
+/*
+** EISA Configuration Register 1 bit definitions
+*/
+#define ER1_IAM 0xe0 /* ISA Address Mode */
+#define ER1_IAE 0x10 /* ISA Addressing Enable */
+#define ER1_UPIN 0x0f /* User Pins */
+
+/*
+** EISA Configuration Register 2 bit definitions
+*/
+#define ER2_BRS 0xc0 /* Boot ROM Size */
+#define ER2_BRA 0x3c /* Boot ROM Address <16:13> */
+
+/*
+** EISA Configuration Register 3 bit definitions
+*/
+#define ER3_BWE 0x40 /* Burst Write Enable */
+#define ER3_BRE 0x04 /* Burst Read Enable */
+#define ER3_LSR 0x02 /* Local Software Reset */
+
+/*
+** PCI Configuration ID Register (PCI_CFID)
+*/
+#define CFID_DID 0xff00 /* Device ID */
+#define CFID_VID 0x00ff /* Vendor ID */
+#define DC21040_DID 0x0002 /* Unique Device ID # */
+#define DC21040_VID 0x1011 /* DC21040 Manufacturer */
+#define DC21041_DID 0x0014 /* Unique Device ID # */
+#define DC21041_VID 0x1011 /* DC21041 Manufacturer */
+#define DC21140_DID 0x0009 /* Unique Device ID # */
+#define DC21140_VID 0x1011 /* DC21140 Manufacturer */
+
+/*
+** Chipset defines
+*/
+#define DC21040 DC21040_DID
+#define DC21041 DC21041_DID
+#define DC21140 DC21140_DID
+
+#define is_DC21040 ((vendor == DC21040_VID) && (device == DC21040_DID))
+#define is_DC21041 ((vendor == DC21041_VID) && (device == DC21041_DID))
+#define is_DC21140 ((vendor == DC21140_VID) && (device == DC21140_DID))
+
+/*
+** PCI Configuration Command/Status Register (PCI_CFCS)
+*/
+#define CFCS_DPE 0x80000000 /* Detected Parity Error (S) */
+#define CFCS_SSE 0x40000000 /* Signal System Error (S) */
+#define CFCS_RMA 0x20000000 /* Receive Master Abort (S) */
+#define CFCS_RTA 0x10000000 /* Receive Target Abort (S) */
+#define CFCS_DST 0x06000000 /* DEVSEL Timing (S) */
+#define CFCS_DPR 0x01000000 /* Data Parity Report (S) */
+#define CFCS_FBB 0x00800000 /* Fast Back-To-Back (S) */
+#define CFCS_SLE 0x00000100 /* System Error Enable (C) */
+#define CFCS_PER 0x00000040 /* Parity Error Response (C) */
+#define CFCS_MO 0x00000004 /* Master Operation (C) */
+#define CFCS_MSA 0x00000002 /* Memory Space Access (C) */
+#define CFCS_IOSA 0x00000001 /* I/O Space Access (C) */
+
+/*
+** PCI Configuration Revision Register (PCI_CFRV)
+*/
+#define CFRV_BC 0xff000000 /* Base Class */
+#define CFRV_SC 0x00ff0000 /* Subclass */
+#define CFRV_SN 0x000000f0 /* Step Number */
+#define CFRV_RN 0x0000000f /* Revision Number */
+#define BASE_CLASS 0x02000000 /* Indicates Network Controller */
+#define SUB_CLASS 0x00000000 /* Indicates Ethernet Controller */
+#define STEP_NUMBER 0x00000020 /* Increments for future chips */
+#define REV_NUMBER 0x00000003 /* 0x00, 0x01, 0x02, 0x03: Rev in Step */
+#define CFRV_MASK 0xffff0000 /* Register mask */
+
+/*
+** PCI Configuration Latency Timer Register (PCI_CFLT)
+*/
+#define CFLT_BC 0x0000ff00 /* Latency Timer bits */
+
+/*
+** PCI Configuration Base I/O Address Register (PCI_CBIO)
+*/
+#define CBIO_MASK 0xffffff80 /* Base I/O Address Mask */
+#define CBIO_IOSI 0x00000001 /* I/O Space Indicator (RO, value is 1) */
+
+/*
+** PCI Configuration Expansion ROM Base Address Register (PCI_CBER)
+*/
+#define CBER_MASK 0xfffffc00 /* Expansion ROM Base Address Mask */
+#define CBER_ROME 0x00000001 /* ROM Enable */
+
+/*
+** PCI Configuration Driver Area Register (PCI_CFDA)
+*/
+#define CFDA_PSM 0x80000000 /* Power Saving Mode */
+
+/*
+** DC21040 Bus Mode Register (DE4X5_BMR)
+*/
+#define BMR_DBO 0x00100000 /* Descriptor Byte Ordering (Endian) */
+#define BMR_TAP 0x000e0000 /* Transmit Automatic Polling */
+#define BMR_DAS 0x00010000 /* Diagnostic Address Space */
+#define BMR_CAL 0x0000c000 /* Cache Alignment */
+#define BMR_PBL 0x00003f00 /* Programmable Burst Length */
+#define BMR_BLE 0x00000080 /* Big/Little Endian */
+#define BMR_DSL 0x0000007c /* Descriptor Skip Length */
+#define BMR_BAR 0x00000002 /* Bus ARbitration */
+#define BMR_SWR 0x00000001 /* Software Reset */
+
+#define TAP_NOPOLL 0x00000000 /* No automatic polling */
+#define TAP_200US 0x00020000 /* TX automatic polling every 200us */
+#define TAP_800US 0x00040000 /* TX automatic polling every 800us */
+#define TAP_1_6MS 0x00060000 /* TX automatic polling every 1.6ms */
+#define TAP_12_8US 0x00080000 /* TX automatic polling every 12.8us */
+#define TAP_25_6US 0x000a0000 /* TX automatic polling every 25.6us */
+#define TAP_51_2US 0x000c0000 /* TX automatic polling every 51.2us */
+#define TAP_102_4US 0x000e0000 /* TX automatic polling every 102.4us */
+
+#define CAL_NOUSE 0x00000000 /* Not used */
+#define CAL_8LONG 0x00004000 /* 8-longword alignment */
+#define CAL_16LONG 0x00008000 /* 16-longword alignment */
+#define CAL_32LONG 0x0000c000 /* 32-longword alignment */
+
+#define PBL_0 0x00000000 /* DMA burst length = amount in RX FIFO */
+#define PBL_1 0x00000100 /* 1 longword DMA burst length */
+#define PBL_2 0x00000200 /* 2 longwords DMA burst length */
+#define PBL_4 0x00000400 /* 4 longwords DMA burst length */
+#define PBL_8 0x00000800 /* 8 longwords DMA burst length */
+#define PBL_16 0x00001000 /* 16 longwords DMA burst length */
+#define PBL_32 0x00002000 /* 32 longwords DMA burst length */
+
+#define DSL_0 0x00000000 /* 0 longword / descriptor */
+#define DSL_1 0x00000004 /* 1 longword / descriptor */
+#define DSL_2 0x00000008 /* 2 longwords / descriptor */
+#define DSL_4 0x00000010 /* 4 longwords / descriptor */
+#define DSL_8 0x00000020 /* 8 longwords / descriptor */
+#define DSL_16 0x00000040 /* 16 longwords / descriptor */
+#define DSL_32 0x00000080 /* 32 longwords / descriptor */
+
+/*
+** DC21040 Transmit Poll Demand Register (DE4X5_TPD)
+*/
+#define TPD 0x00000001 /* Transmit Poll Demand */
+
+/*
+** DC21040 Receive Poll Demand Register (DE4X5_RPD)
+*/
+#define RPD 0x00000001 /* Receive Poll Demand */
+
+/*
+** DC21040 Receive Ring Base Address Register (DE4X5_RRBA)
+*/
+#define RRBA 0xfffffffc /* RX Descriptor List Start Address */
+
+/*
+** DC21040 Transmit Ring Base Address Register (DE4X5_TRBA)
+*/
+#define TRBA 0xfffffffc /* TX Descriptor List Start Address */
+
+/*
+** DC21040 Status Register (DE4X5_STS)
+*/
+#define STS_BE 0x03800000 /* Bus Error Bits */
+#define STS_TS 0x00700000 /* Transmit Process State */
+#define STS_RS 0x000e0000 /* Receive Process State */
+#define STS_NIS 0x00010000 /* Normal Interrupt Summary */
+#define STS_AIS 0x00008000 /* Abnormal Interrupt Summary */
+#define STS_ER 0x00004000 /* Early Receive */
+#define STS_SE 0x00002000 /* System Error */
+#define STS_LNF 0x00001000 /* Link Fail */
+#define STS_FD 0x00000800 /* Full-Duplex Short Frame Received */
+#define STS_TM 0x00000800 /* Timer Expired (DC21041) */
+#define STS_AT 0x00000400 /* AUI/TP Pin */
+#define STS_RWT 0x00000200 /* Receive Watchdog Time-Out */
+#define STS_RPS 0x00000100 /* Receive Process Stopped */
+#define STS_RU 0x00000080 /* Receive Buffer Unavailable */
+#define STS_RI 0x00000040 /* Receive Interrupt */
+#define STS_UNF 0x00000020 /* Transmit Underflow */
+#define STS_LNP 0x00000010 /* Link Pass */
+#define STS_TJT 0x00000008 /* Transmit Jabber Time-Out */
+#define STS_TU 0x00000004 /* Transmit Buffer Unavailable */
+#define STS_TPS 0x00000002 /* Transmit Process Stopped */
+#define STS_TI 0x00000001 /* Transmit Interrupt */
+
+#define EB_PAR 0x00000000 /* Parity Error */
+#define EB_MA 0x00800000 /* Master Abort */
+#define EB_TA 0x01000000 /* Target Abort */
+#define EB_RES0 0x01800000 /* Reserved */
+#define EB_RES1 0x02000000 /* Reserved */
+
+#define TS_STOP 0x00000000 /* Stopped */
+#define TS_FTD 0x00100000 /* Fetch Transmit Descriptor */
+#define TS_WEOT 0x00200000 /* Wait for End Of Transmission */
+#define TS_QDAT 0x00300000 /* Queue skb data into TX FIFO */
+#define TS_RES 0x00400000 /* Reserved */
+#define TS_SPKT 0x00500000 /* Setup Packet */
+#define TS_SUSP 0x00600000 /* Suspended */
+#define TS_CLTD 0x00700000 /* Close Transmit Descriptor */
+
+#define RS_STOP 0x00000000 /* Stopped */
+#define RS_FRD 0x00020000 /* Fetch Receive Descriptor */
+#define RS_CEOR 0x00040000 /* Check for End of Receive Packet */
+#define RS_WFRP 0x00060000 /* Wait for Receive Packet */
+#define RS_SUSP 0x00080000 /* Suspended */
+#define RS_CLRD 0x000a0000 /* Close Receive Descriptor */
+#define RS_FLUSH 0x000c0000 /* Flush RX FIFO */
+#define RS_QRFS 0x000e0000 /* Queue RX FIFO into RX Skb */
+
+#define INT_CANCEL 0x0001ffff /* For zeroing all interrupt sources */
+
+/*
+** DC21040 Operation Mode Register (DE4X5_OMR)
+*/
+#define OMR_SDP 0x02000000 /* SD Polarity - MUST BE ASSERTED */
+#define OMR_SCR 0x01000000 /* Scrambler Mode */
+#define OMR_PCS 0x00800000 /* PCS Function */
+#define OMR_TTM 0x00400000 /* Transmit Threshold Mode */
+#define OMR_SF 0x00200000 /* Store and Forward */
+#define OMR_HBD 0x00080000 /* HeartBeat Disable */
+#define OMR_PS 0x00040000 /* Port Select */
+#define OMR_CA 0x00020000 /* Capture Effect Enable */
+#define OMR_BP 0x00010000 /* Back Pressure */
+#define OMR_TR 0x0000c000 /* Threshold Control Bits */
+#define OMR_ST 0x00002000 /* Start/Stop Transmission Command */
+#define OMR_FC 0x00001000 /* Force Collision Mode */
+#define OMR_OM 0x00000c00 /* Operating Mode */
+#define OMR_FD 0x00000200 /* Full Duplex Mode */
+#define OMR_FKD 0x00000100 /* Flaky Oscillator Disable */
+#define OMR_PM 0x00000080 /* Pass All Multicast */
+#define OMR_PR 0x00000040 /* Promiscuous Mode */
+#define OMR_SB 0x00000020 /* Start/Stop Backoff Counter */
+#define OMR_IF 0x00000010 /* Inverse Filtering */
+#define OMR_PB 0x00000008 /* Pass Bad Frames */
+#define OMR_HO 0x00000004 /* Hash Only Filtering Mode */
+#define OMR_SR 0x00000002 /* Start/Stop Receive */
+#define OMR_HP 0x00000001 /* Hash/Perfect Receive Filtering Mode */
+
+#define TR_72 0x00000000 /* Threshold set to 72 bytes */
+#define TR_96 0x00004000 /* Threshold set to 96 bytes */
+#define TR_128 0x00008000 /* Threshold set to 128 bytes */
+#define TR_160 0x0000c000 /* Threshold set to 160 bytes */
+
+/*
+** DC21040 Interrupt Mask Register (DE4X5_IMR)
+*/
+#define IMR_NIM 0x00010000 /* Normal Interrupt Summary Mask */
+#define IMR_AIM 0x00008000 /* Abnormal Interrupt Summary Mask */
+#define IMR_ERM 0x00004000 /* Early Receive Mask */
+#define IMR_SEM 0x00002000 /* System Error Mask */
+#define IMR_LFM 0x00001000 /* Link Fail Mask */
+#define IMR_FDM 0x00000800 /* Full-Duplex (Short Frame) Mask */
+#define IMR_TMM 0x00000800 /* Timer Expired Mask (DC21041) */
+#define IMR_ATM 0x00000400 /* AUI/TP Switch Mask */
+#define IMR_RWM 0x00000200 /* Receive Watchdog Time-Out Mask */
+#define IMR_RSM 0x00000100 /* Receive Stopped Mask */
+#define IMR_RUM 0x00000080 /* Receive Buffer Unavailable Mask */
+#define IMR_RIM 0x00000040 /* Receive Interrupt Mask */
+#define IMR_UNM 0x00000020 /* Underflow Interrupt Mask */
+#define IMR_LPM 0x00000010 /* Link Pass */
+#define IMR_TJM 0x00000008 /* Transmit Time-Out Jabber Mask */
+#define IMR_TUM 0x00000004 /* Transmit Buffer Unavailable Mask */
+#define IMR_TSM 0x00000002 /* Transmission Stopped Mask */
+#define IMR_TIM 0x00000001 /* Transmit Interrupt Mask */
+
+/*
+** DC21040 Missed Frame Counter (DE4X5_MFC)
+*/
+#define MFC_OVFL 0x00010000 /* Counter Overflow Bit */
+#define MFC_CNTR 0x0000ffff /* Counter Bits */
+
+/*
+** DC21040 Ethernet Address PROM (DE4X5_APROM)
+*/
+#define APROM_DN 0x80000000 /* Data Not Valid */
+#define APROM_DT 0x000000ff /* Address Byte */
+
+/*
+** DC21041 Boot/Ethernet Address ROM (DE4X5_BROM)
+*/
+#define BROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */
+#define BROM_RD 0x00004000 /* Read from Boot ROM */
+#define BROM_WR 0x00002000 /* Write to Boot ROM */
+#define BROM_BR 0x00001000 /* Select Boot ROM when set */
+#define BROM_SR 0x00000800 /* Select Serial ROM when set */
+#define BROM_REG 0x00000400 /* External Register Select */
+#define BROM_DT 0x000000ff /* Data Byte */
+
+/*
+** DC21041 Serial/Ethernet Address ROM (DE4X5_SROM)
+*/
+#define SROM_MODE 0x00008000 /* MODE_1: 0, MODE_0: 1 (read only) */
+#define SROM_RD 0x00004000 /* Read from Boot ROM */
+#define SROM_WR 0x00002000 /* Write to Boot ROM */
+#define SROM_BR 0x00001000 /* Select Boot ROM when set */
+#define SROM_SR 0x00000800 /* Select Serial ROM when set */
+#define SROM_REG 0x00000400 /* External Register Select */
+#define SROM_DT 0x000000ff /* Data Byte */
+
+#define DT_OUT 0x00000008 /* Serial Data Out */
+#define DT_IN 0x00000004 /* Serial Data In */
+#define DT_CLK 0x00000002 /* Serial ROM Clock */
+#define DT_CS 0x00000001 /* Serial ROM Chip Select */
+
+/*
+** DC21040 Full Duplex Register (DE4X5_FDR)
+*/
+#define FDR_FDACV 0x0000ffff /* Full Duplex Auto Configuration Value */
+
+/*
+** DC21041 General Purpose Timer Register (DE4X5_GPT)
+*/
+#define GPT_CON 0x00010000 /* One shot: 0, Continuous: 1 */
+#define GPT_VAL 0x0000ffff /* Timer Value */
+
+/*
+** DC21140 General Purpose Register (DE4X5_GEP) (hardware dependent bits)
+*/
+/* Valid ONLY for DE500 hardware */
+#define GEP_LNP 0x00000080 /* Link Pass (input) */
+#define GEP_SLNK 0x00000040 /* SYM LINK (input) */
+#define GEP_SDET 0x00000020 /* Signal Detect (input) */
+#define GEP_FDXD 0x00000008 /* Full Duplex Disable (output) */
+#define GEP_PHYL 0x00000004 /* PHY Loopback (output) */
+#define GEP_FLED 0x00000002 /* Force Activity LED on (output) */
+#define GEP_MODE 0x00000001 /* 0: 10Mb/s, 1: 100Mb/s */
+#define GEP_INIT 0x0000010f /* Setup inputs (0) and outputs (1) */
+
+
+/*
+** DC21040 SIA Status Register (DE4X5_SISR)
+*/
+#define SISR_LPC 0xffff0000 /* Link Partner's Code Word */
+#define SISR_LPN 0x00008000 /* Link Partner Negotiable */
+#define SISR_ANS 0x00007000 /* Auto Negotiation Arbitration State */
+#define SISR_NSN 0x00000800 /* Non Stable NLPs Detected */
+#define SISR_ANR_FDS 0x00000400 /* Auto Negotiate Restart/Full Duplex Sel.*/
+#define SISR_NRA 0x00000200 /* Non Selected Port Receive Activity */
+#define SISR_SRA 0x00000100 /* Selected Port Receive Activity */
+#define SISR_DAO 0x00000080 /* PLL All One */
+#define SISR_DAZ 0x00000040 /* PLL All Zero */
+#define SISR_DSP 0x00000020 /* PLL Self-Test Pass */
+#define SISR_DSD 0x00000010 /* PLL Self-Test Done */
+#define SISR_APS 0x00000008 /* Auto Polarity State */
+#define SISR_LKF 0x00000004 /* Link Fail Status */
+#define SISR_NCR 0x00000002 /* Network Connection Error */
+#define SISR_PAUI 0x00000001 /* AUI_TP Indication */
+#define SIA_RESET 0x00000000 /* SIA Reset */
+
+#define ANS_NDIS 0x00000000 /* Nway disable */
+#define ANS_TDIS 0x00001000 /* Transmit Disable */
+#define ANS_ADET 0x00002000 /* Ability Detect */
+#define ANS_ACK 0x00003000 /* Acknowledge */
+#define ANS_CACK 0x00004000 /* Complete Acknowledge */
+#define ANS_NWOK 0x00005000 /* Nway OK - FLP Link Good */
+#define ANS_LCHK 0x00006000 /* Link Check */
+
+/*
+** DC21040 SIA Connectivity Register (DE4X5_SICR)
+*/
+#define SICR_SDM 0xffff0000 /* SIA Diagnostics Mode */
+#define SICR_OE57 0x00008000 /* Output Enable 5 6 7 */
+#define SICR_OE24 0x00004000 /* Output Enable 2 4 */
+#define SICR_OE13 0x00002000 /* Output Enable 1 3 */
+#define SICR_IE 0x00001000 /* Input Enable */
+#define SICR_EXT 0x00000000 /* SIA MUX Select External SIA Mode */
+#define SICR_D_SIA 0x00000400 /* SIA MUX Select Diagnostics - SIA Sigs */
+#define SICR_DPLL 0x00000800 /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_APLL 0x00000a00 /* SIA MUX Select Diagnostics - DPLL Sigs*/
+#define SICR_D_RxM 0x00000c00 /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_M_RxM 0x00000d00 /* SIA MUX Select Diagnostics - RxM Sigs */
+#define SICR_LNKT 0x00000e00 /* SIA MUX Select Diagnostics - Link Test*/
+#define SICR_SEL 0x00000f00 /* SIA MUX Select AUI or TP with LEDs */
+#define SICR_ASE 0x00000080 /* APLL Start Enable*/
+#define SICR_SIM 0x00000040 /* Serial Interface Input Multiplexer */
+#define SICR_ENI 0x00000020 /* Encoder Input Multiplexer */
+#define SICR_EDP 0x00000010 /* SIA PLL External Input Enable */
+#define SICR_AUI 0x00000008 /* 10Base-T or AUI */
+#define SICR_CAC 0x00000004 /* CSR Auto Configuration */
+#define SICR_PS 0x00000002 /* Pin AUI/TP Selection */
+#define SICR_SRL 0x00000001 /* SIA Reset */
+#define SICR_RESET 0xffff0000 /* Reset value for SICR */
+
+/*
+** DC21040 SIA Transmit and Receive Register (DE4X5_STRR)
+*/
+#define STRR_TAS 0x00008000 /* 10Base-T/AUI Autosensing Enable */
+#define STRR_SPP 0x00004000 /* Set Polarity Plus */
+#define STRR_APE 0x00002000 /* Auto Polarity Enable */
+#define STRR_LTE 0x00001000 /* Link Test Enable */
+#define STRR_SQE 0x00000800 /* Signal Quality Enable */
+#define STRR_CLD 0x00000400 /* Collision Detect Enable */
+#define STRR_CSQ 0x00000200 /* Collision Squelch Enable */
+#define STRR_RSQ 0x00000100 /* Receive Squelch Enable */
+#define STRR_ANE 0x00000080 /* Auto Negotiate Enable */
+#define STRR_HDE 0x00000040 /* Half Duplex Enable */
+#define STRR_CPEN 0x00000030 /* Compensation Enable */
+#define STRR_LSE 0x00000008 /* Link Pulse Send Enable */
+#define STRR_DREN 0x00000004 /* Driver Enable */
+#define STRR_LBK 0x00000002 /* Loopback Enable */
+#define STRR_ECEN 0x00000001 /* Encoder Enable */
+#define STRR_RESET 0xffffffff /* Reset value for STRR */
+
+/*
+** DC21040 SIA General Register (DE4X5_SIGR)
+*/
+#define SIGR_LV2 0x00008000 /* General Purpose LED2 value */
+#define SIGR_LE2 0x00004000 /* General Purpose LED2 enable */
+#define SIGR_FRL 0x00002000 /* Force Receiver Low */
+#define SIGR_DPST 0x00001000 /* PLL Self Test Start */
+#define SIGR_LSD 0x00000800 /* LED Stretch Disable */
+#define SIGR_FLF 0x00000400 /* Force Link Fail */
+#define SIGR_FUSQ 0x00000200 /* Force Unsquelch */
+#define SIGR_TSCK 0x00000100 /* Test Clock */
+#define SIGR_LV1 0x00000080 /* General Purpose LED1 value */
+#define SIGR_LE1 0x00000040 /* General Purpose LED1 enable */
+#define SIGR_RWR 0x00000020 /* Receive Watchdog Release */
+#define SIGR_RWD 0x00000010 /* Receive Watchdog Disable */
+#define SIGR_ABM 0x00000008 /* BNC: 0, AUI:1 */
+#define SIGR_JCK 0x00000004 /* Jabber Clock */
+#define SIGR_HUJ 0x00000002 /* Host Unjab */
+#define SIGR_JBD 0x00000001 /* Jabber Disable */
+#define SIGR_RESET 0xffff0000 /* Reset value for SIGR */
+
+/*
+** Receive Descriptor Bit Summary
+*/
+#define R_OWN 0x80000000 /* Own Bit */
+#define RD_FL 0x7fff0000 /* Frame Length */
+#define RD_ES 0x00008000 /* Error Summary */
+#define RD_LE 0x00004000 /* Length Error */
+#define RD_DT 0x00003000 /* Data Type */
+#define RD_RF 0x00000800 /* Runt Frame */
+#define RD_MF 0x00000400 /* Multicast Frame */
+#define RD_FS 0x00000200 /* First Descriptor */
+#define RD_LS 0x00000100 /* Last Descriptor */
+#define RD_TL 0x00000080 /* Frame Too Long */
+#define RD_CS 0x00000040 /* Collision Seen */
+#define RD_FT 0x00000020 /* Frame Type */
+#define RD_RJ 0x00000010 /* Receive Watchdog */
+#define RD_DB 0x00000004 /* Dribbling Bit */
+#define RD_CE 0x00000002 /* CRC Error */
+#define RD_OF 0x00000001 /* Overflow */
+
+#define RD_RER 0x02000000 /* Receive End Of Ring */
+#define RD_RCH 0x01000000 /* Second Address Chained */
+#define RD_RBS2 0x003ff800 /* Buffer 2 Size */
+#define RD_RBS1 0x000007ff /* Buffer 1 Size */
+
+/*
+** Transmit Descriptor Bit Summary
+*/
+#define T_OWN 0x80000000 /* Own Bit */
+#define TD_ES 0x00008000 /* Error Summary */
+#define TD_TO 0x00004000 /* Transmit Jabber Time-Out */
+#define TD_LO 0x00000800 /* Loss Of Carrier */
+#define TD_NC 0x00000400 /* No Carrier */
+#define TD_LC 0x00000200 /* Late Collision */
+#define TD_EC 0x00000100 /* Excessive Collisions */
+#define TD_HF 0x00000080 /* Heartbeat Fail */
+#define TD_CC 0x00000078 /* Collision Counter */
+#define TD_LF 0x00000004 /* Link Fail */
+#define TD_UF 0x00000002 /* Underflow Error */
+#define TD_DE 0x00000001 /* Deferred */
+
+#define TD_IC 0x80000000 /* Interrupt On Completion */
+#define TD_LS 0x40000000 /* Last Segment */
+#define TD_FS 0x20000000 /* First Segment */
+#define TD_FT1 0x10000000 /* Filtering Type */
+#define TD_SET 0x08000000 /* Setup Packet */
+#define TD_AC 0x04000000 /* Add CRC Disable */
+#define TD_TER 0x02000000 /* Transmit End Of Ring */
+#define TD_TCH 0x01000000 /* Second Address Chained */
+#define TD_DPD 0x00800000 /* Disabled Padding */
+#define TD_FT0 0x00400000 /* Filtering Type */
+#define TD_RBS2 0x003ff800 /* Buffer 2 Size */
+#define TD_RBS1 0x000007ff /* Buffer 1 Size */
+
+#define PERFECT_F 0x00000000
+#define HASH_F TD_FT0
+#define INVERSE_F TD_FT1
+#define HASH_O_F TD_FT1| TD_F0
+
+/*
+** Media / mode state machine definitions
+*/
+#define NC 0x0000 /* No Connection */
+#define TP 0x0001 /* 10Base-T */
+#define TP_NW 0x0002 /* 10Base-T with Nway */
+#define BNC 0x0004 /* Thinwire */
+#define AUI 0x0008 /* Thickwire */
+#define BNC_AUI 0x0010 /* BNC/AUI on DC21040 indistinguishable */
+#define ANS 0x0020 /* Intermediate AutoNegotiation State */
+#define EXT_SIA 0x0400 /* external SIA (as on DEC MULTIA) */
+
+#define _10Mb 0x0040 /* 10Mb/s Ethernet */
+#define _100Mb 0x0080 /* 100Mb/s Ethernet */
+#define SYM_WAIT 0x0100 /* Wait for SYM_LINK */
+#define INIT 0x0200 /* Initial state */
+
+#define AUTO 0x4000 /* Auto sense the media or speed */
+
+/*
+** Miscellaneous
+*/
+#define PCI 0
+#define EISA 1
+
+#define HASH_TABLE_LEN 512 /* Bits */
+#define HASH_BITS 0x01ff /* 9 LS bits */
+
+#define SETUP_FRAME_LEN 192 /* Bytes */
+#define IMPERF_PA_OFFSET 156 /* Bytes */
+
+#define POLL_DEMAND 1
+
+#define LOST_MEDIA_THRESHOLD 3
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define DE4X5_STRLEN 8
+
+/*
+** Address Filtering Modes
+*/
+#define PERFECT 0 /* 16 perfect physical addresses */
+#define HASH_PERF 1 /* 1 perfect, 512 multicast addresses */
+#define PERFECT_REJ 2 /* Reject 16 perfect physical addresses */
+#define ALL_HASH 3 /* Hashes all physical & multicast addrs */
+
+#define ALL 0 /* Clear out all the setup frame */
+#define PHYS_ADDR_ONLY 1 /* Update the physical address only */
+
+/*
+** Booleans
+*/
+#define NO 0
+#define FALSE 0
+
+#define YES !0
+#define TRUE !0
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define DE4X5IOCTL SIOCDEVPRIVATE
+
+struct de4x5_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define DE4X5_GET_HWADDR 0x01 /* Get the hardware address */
+#define DE4X5_SET_HWADDR 0x02 /* Get the hardware address */
+#define DE4X5_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define DE4X5_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define DE4X5_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define DE4X5_GET_MCA 0x06 /* Get a multicast address */
+#define DE4X5_SET_MCA 0x07 /* Set a multicast address */
+#define DE4X5_CLR_MCA 0x08 /* Clear a multicast address */
+#define DE4X5_MCA_EN 0x09 /* Enable a multicast address group */
+#define DE4X5_GET_STATS 0x0a /* Get the driver statistics */
+#define DE4X5_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define DE4X5_GET_OMR 0x0c /* Get the OMR Register contents */
+#define DE4X5_SET_OMR 0x0d /* Set the OMR Register contents */
+#define DE4X5_GET_REG 0x0e /* Get the DE4X5 Registers */
diff --git a/i386/i386at/gpl/linux/net/de600.c b/i386/i386at/gpl/linux/net/de600.c
new file mode 100644
index 00000000..256759df
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de600.c
@@ -0,0 +1,853 @@
+static const char *version =
+ "de600.c: $Revision: 1.1.1.1 $, Bjorn Ekwall (bj0rn@blox.se)\n";
+/*
+ * de600.c
+ *
+ * Linux driver for the D-Link DE-600 Ethernet pocket adapter.
+ *
+ * Portions (C) Copyright 1993, 1994 by Bjorn Ekwall
+ * The Author may be reached as bj0rn@blox.se
+ *
+ * Based on adapter information gathered from DE600.ASM by D-Link Inc.,
+ * as included on disk C in the v.2.11 of PC/TCP from FTP Software.
+ * For DE600.asm:
+ * Portions (C) Copyright 1990 D-Link, Inc.
+ * Copyright, 1988-1992, Russell Nelson, Crynwr Software
+ *
+ * Adapted to the sample network driver core for linux,
+ * written by: Donald Becker <becker@super.org>
+ * C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+ *
+ * compile-command:
+ * "gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O6 -fomit-frame-pointer \
+ * -m486 -c de600.c
+ *
+ **************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ **************************************************************/
+/* Add another "; SLOW_DOWN_IO" here if your adapter won't work OK: */
+#define DE600_SLOW_DOWN SLOW_DOWN_IO; SLOW_DOWN_IO; SLOW_DOWN_IO
+
+ /*
+ * If you still have trouble reading/writing to the adapter,
+ * modify the following "#define": (see <asm/io.h> for more info)
+#define REALLY_SLOW_IO
+ */
+#define SLOW_IO_BY_JUMPING /* Looks "better" than dummy write to port 0x80 :-) */
+
+/*
+ * If you want to enable automatic continuous checking for the DE600,
+ * keep this #define enabled.
+ * It doesn't cost much per packet, so I think it is worth it!
+ * If you disagree, comment away the #define, and live with it...
+ *
+ */
+#define CHECK_LOST_DE600
+
+/*
+ * Enable this #define if you want the adapter to do a "ifconfig down" on
+ * itself when we have detected that something is possibly wrong with it.
+ * The default behaviour is to retry with "adapter_init()" until success.
+ * This should be used for debugging purposes only.
+ * (Depends on the CHECK_LOST_DE600 above)
+ *
+ */
+#define SHUTDOWN_WHEN_LOST
+
+/*
+ * See comment at "de600_rspace()"!
+ * This is an *ugly* hack, but for now it achieves its goal of
+ * faking a TCP flow-control that will not flood the poor DE600.
+ *
+ * Tricks TCP to announce a small max window (max 2 fast packets please :-)
+ *
+ * Comment away at your own risk!
+ *
+ * Update: Use the more general per-device maxwindow parameter instead.
+ */
+#undef FAKE_SMALL_MAX
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifdef DE600_DEBUG
+#define PRINTK(x) if (de600_debug >= 2) printk x
+#else
+#define DE600_DEBUG 0
+#define PRINTK(x) /**/
+#endif
+unsigned int de600_debug = DE600_DEBUG;
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#ifdef FAKE_SMALL_MAX
+static unsigned long de600_rspace(struct sock *sk);
+#include <net/sock.h>
+#endif
+
+#define netstats enet_statistics
+typedef unsigned char byte;
+
+/**************************************************
+ * *
+ * Definition of D-Link Ethernet Pocket adapter *
+ * *
+ **************************************************/
+/*
+ * D-Link Ethernet pocket adapter ports
+ */
+/*
+ * OK, so I'm cheating, but there are an awful lot of
+ * reads and writes in order to get anything in and out
+ * of the DE-600 with 4 bits at a time in the parallel port,
+ * so every saved instruction really helps :-)
+ *
+ * That is, I don't care what the device struct says
+ * but hope that Space.c will keep the rest of the drivers happy.
+ */
+#ifndef DE600_IO
+#define DE600_IO 0x378
+#endif
+
+#define DATA_PORT (DE600_IO)
+#define STATUS_PORT (DE600_IO + 1)
+#define COMMAND_PORT (DE600_IO + 2)
+
+#ifndef DE600_IRQ
+#define DE600_IRQ 7
+#endif
+/*
+ * It really should look like this, and autoprobing as well...
+ *
+#define DATA_PORT (dev->base_addr + 0)
+#define STATUS_PORT (dev->base_addr + 1)
+#define COMMAND_PORT (dev->base_addr + 2)
+#define DE600_IRQ dev->irq
+ */
+
+/*
+ * D-Link COMMAND_PORT commands
+ */
+#define SELECT_NIC 0x04 /* select Network Interface Card */
+#define SELECT_PRN 0x1c /* select Printer */
+#define NML_PRN 0xec /* normal Printer situation */
+#define IRQEN 0x10 /* enable IRQ line */
+
+/*
+ * D-Link STATUS_PORT
+ */
+#define RX_BUSY 0x80
+#define RX_GOOD 0x40
+#define TX_FAILED16 0x10
+#define TX_BUSY 0x08
+
+/*
+ * D-Link DATA_PORT commands
+ * command in low 4 bits
+ * data in high 4 bits
+ * select current data nibble with HI_NIBBLE bit
+ */
+#define WRITE_DATA 0x00 /* write memory */
+#define READ_DATA 0x01 /* read memory */
+#define STATUS 0x02 /* read status register */
+#define COMMAND 0x03 /* write command register (see COMMAND below) */
+#define NULL_COMMAND 0x04 /* null command */
+#define RX_LEN 0x05 /* read received packet length */
+#define TX_ADDR 0x06 /* set adapter transmit memory address */
+#define RW_ADDR 0x07 /* set adapter read/write memory address */
+#define HI_NIBBLE 0x08 /* read/write the high nibble of data,
+ or-ed with rest of command */
+
+/*
+ * command register, accessed through DATA_PORT with low bits = COMMAND
+ */
+#define RX_ALL 0x01 /* PROMISCUOUS */
+#define RX_BP 0x02 /* default: BROADCAST & PHYSICAL ADDRESS */
+#define RX_MBP 0x03 /* MULTICAST, BROADCAST & PHYSICAL ADDRESS */
+
+#define TX_ENABLE 0x04 /* bit 2 */
+#define RX_ENABLE 0x08 /* bit 3 */
+
+#define RESET 0x80 /* set bit 7 high */
+#define STOP_RESET 0x00 /* set bit 7 low */
+
+/*
+ * data to command register
+ * (high 4 bits in write to DATA_PORT)
+ */
+#define RX_PAGE2_SELECT 0x10 /* bit 4, only 2 pages to select */
+#define RX_BASE_PAGE 0x20 /* bit 5, always set when specifying RX_ADDR */
+#define FLIP_IRQ 0x40 /* bit 6 */
+
+/*
+ * D-Link adapter internal memory:
+ *
+ * 0-2K 1:st transmit page (send from pointer up to 2K)
+ * 2-4K 2:nd transmit page (send from pointer up to 4K)
+ *
+ * 4-6K 1:st receive page (data from 4K upwards)
+ * 6-8K 2:nd receive page (data from 6K upwards)
+ *
+ * 8K+ Adapter ROM (contains magic code and last 3 bytes of Ethernet address)
+ */
+#define MEM_2K 0x0800 /* 2048 */
+#define MEM_4K 0x1000 /* 4096 */
+#define MEM_6K 0x1800 /* 6144 */
+#define NODE_ADDRESS 0x2000 /* 8192 */
+
+#define RUNT 60 /* Too small Ethernet packet */
+
+/**************************************************
+ * *
+ * End of definition *
+ * *
+ **************************************************/
+
+/*
+ * Index to functions, as function prototypes.
+ */
+/* Routines used internally. (See "convenience macros") */
+static byte de600_read_status(struct device *dev);
+static byte de600_read_byte(unsigned char type, struct device *dev);
+
+/* Put in the device structure. */
+static int de600_open(struct device *dev);
+static int de600_close(struct device *dev);
+static struct netstats *get_stats(struct device *dev);
+static int de600_start_xmit(struct sk_buff *skb, struct device *dev);
+
+/* Dispatch from interrupts. */
+static void de600_interrupt(int irq, struct pt_regs *regs);
+static int de600_tx_intr(struct device *dev, int irq_status);
+static void de600_rx_intr(struct device *dev);
+
+/* Initialization */
+static void trigger_interrupt(struct device *dev);
+int de600_probe(struct device *dev);
+static int adapter_init(struct device *dev);
+
+/*
+ * D-Link driver variables:
+ */
+static volatile int rx_page = 0;
+
+#define TX_PAGES 2
+static volatile int tx_fifo[TX_PAGES];
+static volatile int tx_fifo_in = 0;
+static volatile int tx_fifo_out = 0;
+static volatile int free_tx_pages = TX_PAGES;
+static int was_down = 0;
+
+/*
+ * Convenience macros/functions for D-Link adapter
+ */
+
+#define select_prn() outb_p(SELECT_PRN, COMMAND_PORT); DE600_SLOW_DOWN
+#define select_nic() outb_p(SELECT_NIC, COMMAND_PORT); DE600_SLOW_DOWN
+
+/* Thanks for hints from Mark Burton <markb@ordern.demon.co.uk> */
+#define de600_put_byte(data) ( \
+ outb_p(((data) << 4) | WRITE_DATA , DATA_PORT), \
+ outb_p(((data) & 0xf0) | WRITE_DATA | HI_NIBBLE, DATA_PORT))
+
+/*
+ * The first two outb_p()'s below could perhaps be deleted if there
+ * would be more delay in the last two. Not certain about it yet...
+ */
+#define de600_put_command(cmd) ( \
+ outb_p(( rx_page << 4) | COMMAND , DATA_PORT), \
+ outb_p(( rx_page & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT), \
+ outb_p(((rx_page | cmd) << 4) | COMMAND , DATA_PORT), \
+ outb_p(((rx_page | cmd) & 0xf0) | COMMAND | HI_NIBBLE, DATA_PORT))
+
+#define de600_setup_address(addr,type) ( \
+ outb_p((((addr) << 4) & 0xf0) | type , DATA_PORT), \
+ outb_p(( (addr) & 0xf0) | type | HI_NIBBLE, DATA_PORT), \
+ outb_p((((addr) >> 4) & 0xf0) | type , DATA_PORT), \
+ outb_p((((addr) >> 8) & 0xf0) | type | HI_NIBBLE, DATA_PORT))
+
+#define rx_page_adr() ((rx_page & RX_PAGE2_SELECT)?(MEM_6K):(MEM_4K))
+
+/* Flip bit, only 2 pages */
+#define next_rx_page() (rx_page ^= RX_PAGE2_SELECT)
+
+#define tx_page_adr(a) (((a) + 1) * MEM_2K)
+
+static inline byte
+de600_read_status(struct device *dev)
+{
+ byte status;
+
+ outb_p(STATUS, DATA_PORT);
+ status = inb(STATUS_PORT);
+ outb_p(NULL_COMMAND | HI_NIBBLE, DATA_PORT);
+
+ return status;
+}
+
+static inline byte
+de600_read_byte(unsigned char type, struct device *dev) { /* dev used by macros */
+ byte lo;
+
+ (void)outb_p((type), DATA_PORT);
+ lo = ((unsigned char)inb(STATUS_PORT)) >> 4;
+ (void)outb_p((type) | HI_NIBBLE, DATA_PORT);
+ return ((unsigned char)inb(STATUS_PORT) & (unsigned char)0xf0) | lo;
+}
+
+/*
+ * Open/initialize the board. This is called (in the current kernel)
+ * after booting when 'ifconfig <dev->name> $IP_ADDR' is run (in rc.inet1).
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is a non-reboot way to recover if something goes wrong.
+ */
+static int
+de600_open(struct device *dev)
+{
+ if (request_irq(DE600_IRQ, de600_interrupt, 0, "de600")) {
+ printk ("%s: unable to get IRQ %d\n", dev->name, DE600_IRQ);
+ return 1;
+ }
+ irq2dev_map[DE600_IRQ] = dev;
+
+ MOD_INC_USE_COUNT;
+ dev->start = 1;
+ if (adapter_init(dev)) {
+ return 1;
+ }
+
+ return 0;
+}
+
+/*
+ * The inverse routine to de600_open().
+ */
+static int
+de600_close(struct device *dev)
+{
+ select_nic();
+ rx_page = 0;
+ de600_put_command(RESET);
+ de600_put_command(STOP_RESET);
+ de600_put_command(0);
+ select_prn();
+
+ if (dev->start) {
+ free_irq(DE600_IRQ);
+ irq2dev_map[DE600_IRQ] = NULL;
+ dev->start = 0;
+ MOD_DEC_USE_COUNT;
+ }
+ return 0;
+}
+
+static struct netstats *
+get_stats(struct device *dev)
+{
+ return (struct netstats *)(dev->priv);
+}
+
+static inline void
+trigger_interrupt(struct device *dev)
+{
+ de600_put_command(FLIP_IRQ);
+ select_prn();
+ DE600_SLOW_DOWN;
+ select_nic();
+ de600_put_command(0);
+}
+
+/*
+ * Copy a buffer to the adapter transmit page memory.
+ * Start sending.
+ */
+static int
+de600_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ int transmit_from;
+ int len;
+ int tickssofar;
+ byte *buffer = skb->data;
+
+ /*
+ * If some higher layer thinks we've missed a
+ * tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (free_tx_pages <= 0) { /* Do timeouts, to avoid hangs. */
+ tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < 5)
+ return 1;
+
+ /* else */
+ printk("%s: transmit timed out (%d), %s?\n",
+ dev->name,
+ tickssofar,
+ "network cable problem"
+ );
+ /* Restart the adapter. */
+ if (adapter_init(dev)) {
+ return 1;
+ }
+ }
+
+ /* Start real output */
+ PRINTK(("de600_start_xmit:len=%d, page %d/%d\n", skb->len, tx_fifo_in, free_tx_pages));
+
+ if ((len = skb->len) < RUNT)
+ len = RUNT;
+
+ cli();
+ select_nic();
+ tx_fifo[tx_fifo_in] = transmit_from = tx_page_adr(tx_fifo_in) - len;
+ tx_fifo_in = (tx_fifo_in + 1) % TX_PAGES; /* Next free tx page */
+
+#ifdef CHECK_LOST_DE600
+ /* This costs about 40 instructions per packet... */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ de600_read_byte(READ_DATA, dev);
+ if (was_down || (de600_read_byte(READ_DATA, dev) != 0xde)) {
+ if (adapter_init(dev)) {
+ sti();
+ return 1;
+ }
+ }
+#endif
+
+ de600_setup_address(transmit_from, RW_ADDR);
+ for ( ; len > 0; --len, ++buffer)
+ de600_put_byte(*buffer);
+
+ if (free_tx_pages-- == TX_PAGES) { /* No transmission going on */
+ dev->trans_start = jiffies;
+ dev->tbusy = 0; /* allow more packets into adapter */
+ /* Send page and generate a faked interrupt */
+ de600_setup_address(transmit_from, TX_ADDR);
+ de600_put_command(TX_ENABLE);
+ }
+ else {
+ dev->tbusy = !free_tx_pages;
+ select_prn();
+ }
+
+ sti(); /* interrupts back on */
+
+#ifdef FAKE_SMALL_MAX
+ /* This will "patch" the socket TCP proto at an early moment */
+ if (skb->sk && (skb->sk->protocol == IPPROTO_TCP) &&
+ (skb->sk->prot->rspace != &de600_rspace))
+ skb->sk->prot->rspace = de600_rspace; /* Ugh! */
+#endif
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/*
+ * The typical workload of the driver:
+ * Handle the network interface interrupts.
+ */
+static void
+de600_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = irq2dev_map[irq];
+ byte irq_status;
+ int retrig = 0;
+ int boguscount = 0;
+
+ /* This might just as well be deleted now, no crummy drivers present :-) */
+ if ((dev == NULL) || (dev->start == 0) || (DE600_IRQ != irq)) {
+ printk("%s: bogus interrupt %d\n", dev?dev->name:"DE-600", irq);
+ return;
+ }
+
+ dev->interrupt = 1;
+ select_nic();
+ irq_status = de600_read_status(dev);
+
+ do {
+ PRINTK(("de600_interrupt (%02X)\n", irq_status));
+
+ if (irq_status & RX_GOOD)
+ de600_rx_intr(dev);
+ else if (!(irq_status & RX_BUSY))
+ de600_put_command(RX_ENABLE);
+
+ /* Any transmission in progress? */
+ if (free_tx_pages < TX_PAGES)
+ retrig = de600_tx_intr(dev, irq_status);
+ else
+ retrig = 0;
+
+ irq_status = de600_read_status(dev);
+ } while ( (irq_status & RX_GOOD) || ((++boguscount < 100) && retrig) );
+ /*
+ * Yeah, it _looks_ like busy waiting, smells like busy waiting
+ * and I know it's not PC, but please, it will only occur once
+ * in a while and then only for a loop or so (< 1ms for sure!)
+ */
+
+ /* Enable adapter interrupts */
+ dev->interrupt = 0;
+ select_prn();
+
+ if (retrig)
+ trigger_interrupt(dev);
+
+ sti();
+ return;
+}
+
+static int
+de600_tx_intr(struct device *dev, int irq_status)
+{
+ /*
+ * Returns 1 if tx still not done
+ */
+
+ mark_bh(NET_BH);
+ /* Check if current transmission is done yet */
+ if (irq_status & TX_BUSY)
+ return 1; /* tx not done, try again */
+
+ /* else */
+ /* If last transmission OK then bump fifo index */
+ if (!(irq_status & TX_FAILED16)) {
+ tx_fifo_out = (tx_fifo_out + 1) % TX_PAGES;
+ ++free_tx_pages;
+ ((struct netstats *)(dev->priv))->tx_packets++;
+ dev->tbusy = 0;
+ }
+
+ /* More to send, or resend last packet? */
+ if ((free_tx_pages < TX_PAGES) || (irq_status & TX_FAILED16)) {
+ dev->trans_start = jiffies;
+ de600_setup_address(tx_fifo[tx_fifo_out], TX_ADDR);
+ de600_put_command(TX_ENABLE);
+ return 1;
+ }
+ /* else */
+
+ return 0;
+}
+
+/*
+ * We have a good packet, get it out of the adapter.
+ */
+static void
+de600_rx_intr(struct device *dev)
+{
+ struct sk_buff *skb;
+ int i;
+ int read_from;
+ int size;
+ register unsigned char *buffer;
+
+ cli();
+ /* Get size of received packet */
+ size = de600_read_byte(RX_LEN, dev); /* low byte */
+ size += (de600_read_byte(RX_LEN, dev) << 8); /* high byte */
+ size -= 4; /* Ignore trailing 4 CRC-bytes */
+
+ /* Tell adapter where to store next incoming packet, enable receiver */
+ read_from = rx_page_adr();
+ next_rx_page();
+ de600_put_command(RX_ENABLE);
+ sti();
+
+ if ((size < 32) || (size > 1535)) {
+ printk("%s: Bogus packet size %d.\n", dev->name, size);
+ if (size > 10000)
+ adapter_init(dev);
+ return;
+ }
+
+ skb = dev_alloc_skb(size+2);
+ sti();
+ if (skb == NULL) {
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, size);
+ return;
+ }
+ /* else */
+
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align */
+
+ /* 'skb->data' points to the start of sk_buff data area. */
+ buffer = skb_put(skb,size);
+
+ /* copy the packet into the buffer */
+ de600_setup_address(read_from, RW_ADDR);
+ for (i = size; i > 0; --i, ++buffer)
+ *buffer = de600_read_byte(READ_DATA, dev);
+
+ ((struct netstats *)(dev->priv))->rx_packets++; /* count all receives */
+
+ skb->protocol=eth_type_trans(skb,dev);
+
+ netif_rx(skb);
+ /*
+ * If any worth-while packets have been received, netif_rx()
+ * has done a mark_bh(INET_BH) for us and will work on them
+ * when we get to the bottom-half routine.
+ */
+}
+
+int
+de600_probe(struct device *dev)
+{
+ int i;
+ static struct netstats de600_netstats;
+ /*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+
+ printk("%s: D-Link DE-600 pocket adapter", dev->name);
+ /* Alpha testers must have the version number to report bugs. */
+ if (de600_debug > 1)
+ printk(version);
+
+ /* probe for adapter */
+ rx_page = 0;
+ select_nic();
+ (void)de600_read_status(dev);
+ de600_put_command(RESET);
+ de600_put_command(STOP_RESET);
+ if (de600_read_status(dev) & 0xf0) {
+ printk(": not at I/O %#3x.\n", DATA_PORT);
+ return ENODEV;
+ }
+
+ /*
+ * Maybe we found one,
+ * have to check if it is a D-Link DE-600 adapter...
+ */
+
+ /* Get the adapter ethernet address from the ROM */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ for (i = 0; i < ETH_ALEN; i++) {
+ dev->dev_addr[i] = de600_read_byte(READ_DATA, dev);
+ dev->broadcast[i] = 0xff;
+ }
+
+ /* Check magic code */
+ if ((dev->dev_addr[1] == 0xde) && (dev->dev_addr[2] == 0x15)) {
+ /* OK, install real address */
+ dev->dev_addr[0] = 0x00;
+ dev->dev_addr[1] = 0x80;
+ dev->dev_addr[2] = 0xc8;
+ dev->dev_addr[3] &= 0x0f;
+ dev->dev_addr[3] |= 0x70;
+ } else {
+ printk(" not identified in the printer port\n");
+ return ENODEV;
+ }
+
+#if 0 /* Not yet */
+ if (check_region(DE600_IO, 3)) {
+ printk(", port 0x%x busy\n", DE600_IO);
+ return EBUSY;
+ }
+#endif
+ request_region(DE600_IO, 3, "de600");
+
+ printk(", Ethernet Address: %02X", dev->dev_addr[0]);
+ for (i = 1; i < ETH_ALEN; i++)
+ printk(":%02X",dev->dev_addr[i]);
+ printk("\n");
+
+ /* Initialize the device structure. */
+ /*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+ dev->priv = &de600_netstats;
+
+ memset(dev->priv, 0, sizeof(struct netstats));
+ dev->get_stats = get_stats;
+
+ dev->open = de600_open;
+ dev->stop = de600_close;
+ dev->hard_start_xmit = &de600_start_xmit;
+
+ ether_setup(dev);
+
+ dev->flags&=~IFF_MULTICAST;
+
+ select_prn();
+ return 0;
+}
+
+static int
+adapter_init(struct device *dev)
+{
+ int i;
+ long flags;
+
+ save_flags(flags);
+ cli();
+
+ select_nic();
+ rx_page = 0; /* used by RESET */
+ de600_put_command(RESET);
+ de600_put_command(STOP_RESET);
+#ifdef CHECK_LOST_DE600
+ /* Check if it is still there... */
+ /* Get the some bytes of the adapter ethernet address from the ROM */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ de600_read_byte(READ_DATA, dev);
+ if ((de600_read_byte(READ_DATA, dev) != 0xde) ||
+ (de600_read_byte(READ_DATA, dev) != 0x15)) {
+ /* was: if (de600_read_status(dev) & 0xf0) { */
+ printk("Something has happened to the DE-600! Please check it"
+#ifdef SHUTDOWN_WHEN_LOST
+ " and do a new ifconfig"
+#endif /* SHUTDOWN_WHEN_LOST */
+ "!\n");
+#ifdef SHUTDOWN_WHEN_LOST
+ /* Goodbye, cruel world... */
+ dev->flags &= ~IFF_UP;
+ de600_close(dev);
+#endif /* SHUTDOWN_WHEN_LOST */
+ was_down = 1;
+ dev->tbusy = 1; /* Transmit busy... */
+ restore_flags(flags);
+ return 1; /* failed */
+ }
+#endif /* CHECK_LOST_DE600 */
+ if (was_down) {
+ printk("Thanks, I feel much better now!\n");
+ was_down = 0;
+ }
+
+ dev->tbusy = 0; /* Transmit busy... */
+ dev->interrupt = 0;
+ tx_fifo_in = 0;
+ tx_fifo_out = 0;
+ free_tx_pages = TX_PAGES;
+
+ /* set the ether address. */
+ de600_setup_address(NODE_ADDRESS, RW_ADDR);
+ for (i = 0; i < ETH_ALEN; i++)
+ de600_put_byte(dev->dev_addr[i]);
+
+ /* where to start saving incoming packets */
+ rx_page = RX_BP | RX_BASE_PAGE;
+ de600_setup_address(MEM_4K, RW_ADDR);
+ /* Enable receiver */
+ de600_put_command(RX_ENABLE);
+ select_prn();
+ restore_flags(flags);
+
+ return 0; /* OK */
+}
+
+#ifdef FAKE_SMALL_MAX
+/*
+ * The new router code (coming soon 8-) ) will fix this properly.
+ */
+#define DE600_MIN_WINDOW 1024
+#define DE600_MAX_WINDOW 2048
+#define DE600_TCP_WINDOW_DIFF 1024
+/*
+ * Copied from "net/inet/sock.c"
+ *
+ * Sets a lower max receive window in order to achieve <= 2
+ * packets arriving at the adapter in fast succession.
+ * (No way that a DE-600 can keep up with a net saturated
+ * with packets homing in on it :-( )
+ *
+ * Since there are only 2 receive buffers in the DE-600
+ * and it takes some time to copy from the adapter,
+ * this is absolutely necessary for any TCP performance whatsoever!
+ *
+ * Note that the returned window info will never be smaller than
+ * DE600_MIN_WINDOW, i.e. 1024
+ * This differs from the standard function, that can return an
+ * arbitrarily small window!
+ */
+#define min(a,b) ((a)<(b)?(a):(b))
+static unsigned long
+de600_rspace(struct sock *sk)
+{
+ int amt;
+
+ if (sk != NULL) {
+/*
+ * Hack! You might want to play with commenting away the following line,
+ * if you know what you do!
+ sk->max_unacked = DE600_MAX_WINDOW - DE600_TCP_WINDOW_DIFF;
+ */
+
+ if (sk->rmem_alloc >= sk->rcvbuf-2*DE600_MIN_WINDOW) return(0);
+ amt = min((sk->rcvbuf-sk->rmem_alloc)/2/*-DE600_MIN_WINDOW*/, DE600_MAX_WINDOW);
+ if (amt < 0) return(0);
+ return(amt);
+ }
+ return(0);
+}
+#endif
+
+#ifdef MODULE
+static char nullname[8];
+static struct device de600_dev = {
+ nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de600_probe };
+
+int
+init_module(void)
+{
+ if (register_netdev(&de600_dev) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&de600_dev);
+ release_region(DE600_IO, 3);
+}
+#endif /* MODULE */
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c"
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c"
+ * compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de600.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/de620.c b/i386/i386at/gpl/linux/net/de620.c
new file mode 100644
index 00000000..2b17c390
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de620.c
@@ -0,0 +1,1045 @@
+/*
+ * de620.c $Revision: 1.1.1.1 $ BETA
+ *
+ *
+ * Linux driver for the D-Link DE-620 Ethernet pocket adapter.
+ *
+ * Portions (C) Copyright 1993, 1994 by Bjorn Ekwall <bj0rn@blox.se>
+ *
+ * Based on adapter information gathered from DOS packetdriver
+ * sources from D-Link Inc: (Special thanks to Henry Ngai of D-Link.)
+ * Portions (C) Copyright D-Link SYSTEM Inc. 1991, 1992
+ * Copyright, 1988, Russell Nelson, Crynwr Software
+ *
+ * Adapted to the sample network driver core for linux,
+ * written by: Donald Becker <becker@super.org>
+ * (Now at <becker@cesdis.gsfc.nasa.gov>
+ *
+ * Valuable assistance from:
+ * J. Joshua Kopper <kopper@rtsg.mot.com>
+ * Olav Kvittem <Olav.Kvittem@uninett.no>
+ * Germano Caronni <caronni@nessie.cs.id.ethz.ch>
+ * Jeremy Fitzhardinge <jeremy@suite.sw.oz.au>
+ *
+ *****************************************************************************/
+/*
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ *****************************************************************************/
+static const char *version =
+ "de620.c: $Revision: 1.1.1.1 $, Bjorn Ekwall <bj0rn@blox.se>\n";
+
+/***********************************************************************
+ *
+ * "Tuning" section.
+ *
+ * Compile-time options: (see below for descriptions)
+ * -DDE620_IO=0x378 (lpt1)
+ * -DDE620_IRQ=7 (lpt1)
+ * -DDE602_DEBUG=...
+ * -DSHUTDOWN_WHEN_LOST
+ * -DCOUNT_LOOPS
+ * -DLOWSPEED
+ * -DREAD_DELAY
+ * -DWRITE_DELAY
+ */
+
+/*
+ * This driver assumes that the printer port is a "normal",
+ * dumb, uni-directional port!
+ * If your port is "fancy" in any way, please try to set it to "normal"
+ * with your BIOS setup. I have no access to machines with bi-directional
+ * ports, so I can't test such a driver :-(
+ * (Yes, I _know_ it is possible to use DE620 with bidirectional ports...)
+ *
+ * There are some clones of DE620 out there, with different names.
+ * If the current driver does not recognize a clone, try to change
+ * the following #define to:
+ *
+ * #define DE620_CLONE 1
+ */
+#define DE620_CLONE 0
+
+/*
+ * If the adapter has problems with high speeds, enable this #define
+ * otherwise full printerport speed will be attempted.
+ *
+ * You can tune the READ_DELAY/WRITE_DELAY below if you enable LOWSPEED
+ *
+#define LOWSPEED
+ */
+
+#ifndef READ_DELAY
+#define READ_DELAY 100 /* adapter internal read delay in 100ns units */
+#endif
+
+#ifndef WRITE_DELAY
+#define WRITE_DELAY 100 /* adapter internal write delay in 100ns units */
+#endif
+
+/*
+ * Enable this #define if you want the adapter to do a "ifconfig down" on
+ * itself when we have detected that something is possibly wrong with it.
+ * The default behaviour is to retry with "adapter_init()" until success.
+ * This should be used for debugging purposes only.
+ *
+#define SHUTDOWN_WHEN_LOST
+ */
+
+/*
+ * Enable debugging by "-DDE620_DEBUG=3" when compiling,
+ * OR in "./CONFIG"
+ * OR by enabling the following #define
+ *
+ * use 0 for production, 1 for verification, >2 for debug
+ *
+#define DE620_DEBUG 3
+ */
+
+#ifdef LOWSPEED
+/*
+ * Enable this #define if you want to see debugging output that show how long
+ * we have to wait before the DE-620 is ready for the next read/write/command.
+ *
+#define COUNT_LOOPS
+ */
+#endif
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/string.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <linux/in.h>
+#include <linux/ptrace.h>
+#include <asm/system.h>
+#include <linux/errno.h>
+
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* Constant definitions for the DE-620 registers, commands and bits */
+#include "de620.h"
+
+#define netstats enet_statistics
+typedef unsigned char byte;
+
+/*******************************************************
+ * *
+ * Definition of D-Link DE-620 Ethernet Pocket adapter *
+ * See also "de620.h" *
+ * *
+ *******************************************************/
+#ifndef DE620_IO /* Compile-time configurable */
+#define DE620_IO 0x378
+#endif
+
+#ifndef DE620_IRQ /* Compile-time configurable */
+#define DE620_IRQ 7
+#endif
+
+#define DATA_PORT (dev->base_addr)
+#define STATUS_PORT (dev->base_addr + 1)
+#define COMMAND_PORT (dev->base_addr + 2)
+
+#define RUNT 60 /* Too small Ethernet packet */
+#define GIANT 1514 /* largest legal size packet, no fcs */
+
+#ifdef DE620_DEBUG /* Compile-time configurable */
+#define PRINTK(x) if (de620_debug >= 2) printk x
+#else
+#define DE620_DEBUG 0
+#define PRINTK(x) /**/
+#endif
+
+
+/*
+ * Force media with insmod:
+ * insmod de620.o bnc=1
+ * or
+ * insmod de620.o utp=1
+ *
+ * Force io and/or irq with insmod:
+ * insmod de620.o io=0x378 irq=7
+ *
+ * Make a clone skip the Ethernet-address range check:
+ * insmod de620.o clone=1
+ */
+static int bnc = 0;
+static int utp = 0;
+static int io = DE620_IO;
+static int irq = DE620_IRQ;
+static int clone = DE620_CLONE;
+
+static unsigned int de620_debug = DE620_DEBUG;
+
+/***********************************************
+ * *
+ * Index to functions, as function prototypes. *
+ * *
+ ***********************************************/
+
+/*
+ * Routines used internally. (See also "convenience macros.. below")
+ */
+
+/* Put in the device structure. */
+static int de620_open(struct device *);
+static int de620_close(struct device *);
+static struct netstats *get_stats(struct device *);
+static void de620_set_multicast_list(struct device *);
+static int de620_start_xmit(struct sk_buff *, struct device *);
+
+/* Dispatch from interrupts. */
+static void de620_interrupt(int, struct pt_regs *);
+static int de620_rx_intr(struct device *);
+
+/* Initialization */
+static int adapter_init(struct device *);
+int de620_probe(struct device *);
+static int read_eeprom(struct device *);
+
+
+/*
+ * D-Link driver variables:
+ */
+#define SCR_DEF NIBBLEMODE |INTON | SLEEP | AUTOTX
+#define TCR_DEF RXPB /* not used: | TXSUCINT | T16INT */
+#define DE620_RX_START_PAGE 12 /* 12 pages (=3k) reserved for tx */
+#define DEF_NIC_CMD IRQEN | ICEN | DS1
+
+static volatile byte NIC_Cmd;
+static volatile byte next_rx_page;
+static byte first_rx_page;
+static byte last_rx_page;
+static byte EIPRegister;
+
+static struct nic {
+ byte NodeID[6];
+ byte RAM_Size;
+ byte Model;
+ byte Media;
+ byte SCR;
+} nic_data;
+
+/**********************************************************
+ * *
+ * Convenience macros/functions for D-Link DE-620 adapter *
+ * *
+ **********************************************************/
+#define de620_tx_buffs(dd) (inb(STATUS_PORT) & (TXBF0 | TXBF1))
+#define de620_flip_ds(dd) NIC_Cmd ^= DS0 | DS1; outb(NIC_Cmd, COMMAND_PORT);
+
+/* Check for ready-status, and return a nibble (high 4 bits) for data input */
+#ifdef COUNT_LOOPS
+static int tot_cnt;
+#endif
+static inline byte
+de620_ready(struct device *dev)
+{
+ byte value;
+ register short int cnt = 0;
+
+ while ((((value = inb(STATUS_PORT)) & READY) == 0) && (cnt <= 1000))
+ ++cnt;
+
+#ifdef COUNT_LOOPS
+ tot_cnt += cnt;
+#endif
+ return value & 0xf0; /* nibble */
+}
+
+static inline void
+de620_send_command(struct device *dev, byte cmd)
+{
+ de620_ready(dev);
+ if (cmd == W_DUMMY)
+ outb(NIC_Cmd, COMMAND_PORT);
+
+ outb(cmd, DATA_PORT);
+
+ outb(NIC_Cmd ^ CS0, COMMAND_PORT);
+ de620_ready(dev);
+ outb(NIC_Cmd, COMMAND_PORT);
+}
+
+static inline void
+de620_put_byte(struct device *dev, byte value)
+{
+ /* The de620_ready() makes 7 loops, on the average, on a DX2/66 */
+ de620_ready(dev);
+ outb(value, DATA_PORT);
+ de620_flip_ds(dev);
+}
+
+static inline byte
+de620_read_byte(struct device *dev)
+{
+ byte value;
+
+ /* The de620_ready() makes 7 loops, on the average, on a DX2/66 */
+ value = de620_ready(dev); /* High nibble */
+ de620_flip_ds(dev);
+ value |= de620_ready(dev) >> 4; /* Low nibble */
+ return value;
+}
+
+static inline void
+de620_write_block(struct device *dev, byte *buffer, int count)
+{
+#ifndef LOWSPEED
+ byte uflip = NIC_Cmd ^ (DS0 | DS1);
+ byte dflip = NIC_Cmd;
+#else /* LOWSPEED */
+#ifdef COUNT_LOOPS
+ int bytes = count;
+#endif /* COUNT_LOOPS */
+#endif /* LOWSPEED */
+
+#ifdef LOWSPEED
+#ifdef COUNT_LOOPS
+ tot_cnt = 0;
+#endif /* COUNT_LOOPS */
+ /* No further optimization useful, the limit is in the adapter. */
+ for ( ; count > 0; --count, ++buffer) {
+ de620_put_byte(dev,*buffer);
+ }
+ de620_send_command(dev,W_DUMMY);
+#ifdef COUNT_LOOPS
+ /* trial debug output: loops per byte in de620_ready() */
+ printk("WRITE(%d)\n", tot_cnt/((bytes?bytes:1)));
+#endif /* COUNT_LOOPS */
+#else /* not LOWSPEED */
+ for ( ; count > 0; count -=2) {
+ outb(*buffer++, DATA_PORT);
+ outb(uflip, COMMAND_PORT);
+ outb(*buffer++, DATA_PORT);
+ outb(dflip, COMMAND_PORT);
+ }
+ de620_send_command(dev,W_DUMMY);
+#endif /* LOWSPEED */
+}
+
+static inline void
+de620_read_block(struct device *dev, byte *data, int count)
+{
+#ifndef LOWSPEED
+ byte value;
+ byte uflip = NIC_Cmd ^ (DS0 | DS1);
+ byte dflip = NIC_Cmd;
+#else /* LOWSPEED */
+#ifdef COUNT_LOOPS
+ int bytes = count;
+
+ tot_cnt = 0;
+#endif /* COUNT_LOOPS */
+#endif /* LOWSPEED */
+
+#ifdef LOWSPEED
+ /* No further optimization useful, the limit is in the adapter. */
+ while (count-- > 0) {
+ *data++ = de620_read_byte(dev);
+ de620_flip_ds(dev);
+ }
+#ifdef COUNT_LOOPS
+ /* trial debug output: loops per byte in de620_ready() */
+ printk("READ(%d)\n", tot_cnt/(2*(bytes?bytes:1)));
+#endif /* COUNT_LOOPS */
+#else /* not LOWSPEED */
+ while (count-- > 0) {
+ value = inb(STATUS_PORT) & 0xf0; /* High nibble */
+ outb(uflip, COMMAND_PORT);
+ *data++ = value | inb(STATUS_PORT) >> 4; /* Low nibble */
+ outb(dflip , COMMAND_PORT);
+ }
+#endif /* LOWSPEED */
+}
+
+static inline void
+de620_set_delay(struct device *dev)
+{
+ de620_ready(dev);
+ outb(W_DFR, DATA_PORT);
+ outb(NIC_Cmd ^ CS0, COMMAND_PORT);
+
+ de620_ready(dev);
+#ifdef LOWSPEED
+ outb(WRITE_DELAY, DATA_PORT);
+#else
+ outb(0, DATA_PORT);
+#endif
+ de620_flip_ds(dev);
+
+ de620_ready(dev);
+#ifdef LOWSPEED
+ outb(READ_DELAY, DATA_PORT);
+#else
+ outb(0, DATA_PORT);
+#endif
+ de620_flip_ds(dev);
+}
+
+static inline void
+de620_set_register(struct device *dev, byte reg, byte value)
+{
+ de620_ready(dev);
+ outb(reg, DATA_PORT);
+ outb(NIC_Cmd ^ CS0, COMMAND_PORT);
+
+ de620_put_byte(dev, value);
+}
+
+static inline byte
+de620_get_register(struct device *dev, byte reg)
+{
+ byte value;
+
+ de620_send_command(dev,reg);
+ value = de620_read_byte(dev);
+ de620_send_command(dev,W_DUMMY);
+
+ return value;
+}
+
+/*********************************************************************
+ *
+ * Open/initialize the board.
+ *
+ * This routine should set everything up anew at each open, even
+ * registers that "should" only need to be set once at boot, so that
+ * there is a non-reboot way to recover if something goes wrong.
+ *
+ */
+static int
+de620_open(struct device *dev)
+{
+ if (request_irq(dev->irq, de620_interrupt, 0, "de620")) {
+ printk ("%s: unable to get IRQ %d\n", dev->name, dev->irq);
+ return 1;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ MOD_INC_USE_COUNT;
+ if (adapter_init(dev)) {
+ return 1;
+ }
+ dev->start = 1;
+ return 0;
+}
+
+/************************************************
+ *
+ * The inverse routine to de620_open().
+ *
+ */
+static int
+de620_close(struct device *dev)
+{
+ /* disable recv */
+ de620_set_register(dev, W_TCR, RXOFF);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+
+ dev->start = 0;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*********************************************
+ *
+ * Return current statistics
+ *
+ */
+static struct netstats *
+get_stats(struct device *dev)
+{
+ return (struct netstats *)(dev->priv);
+}
+
+/*********************************************
+ *
+ * Set or clear the multicast filter for this adaptor.
+ * (no real multicast implemented for the DE-620, but she can be promiscuous...)
+ *
+ */
+
+static void de620_set_multicast_list(struct device *dev)
+{
+ if (dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ { /* Enable promiscuous mode */
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. - AC
+ */
+ dev->flags|=IFF_PROMISC;
+
+ de620_set_register(dev, W_TCR, (TCR_DEF & ~RXPBM) | RXALL);
+ }
+ else
+ { /* Disable promiscuous mode, use normal mode */
+ de620_set_register(dev, W_TCR, TCR_DEF);
+ }
+}
+
+/*******************************************************
+ *
+ * Copy a buffer to the adapter transmit page memory.
+ * Start sending.
+ */
+static int
+de620_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ unsigned long flags;
+ int len;
+ int tickssofar;
+ byte *buffer = skb->data;
+ byte using_txbuf;
+
+ /*
+ * If some higher layer thinks we've missed a
+ * tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ using_txbuf = de620_tx_buffs(dev); /* Peek at the adapter */
+ dev->tbusy = (using_txbuf == (TXBF0 | TXBF1)); /* Boolean! */
+
+ if (dev->tbusy) { /* Do timeouts, to avoid hangs. */
+ tickssofar = jiffies - dev->trans_start;
+
+ if (tickssofar < 5)
+ return 1;
+
+ /* else */
+ printk("%s: transmit timed out (%d), %s?\n",
+ dev->name,
+ tickssofar,
+ "network cable problem"
+ );
+ /* Restart the adapter. */
+ if (adapter_init(dev)) /* maybe close it */
+ return 1;
+ }
+
+ if ((len = skb->len) < RUNT)
+ len = RUNT;
+ if (len & 1) /* send an even number of bytes */
+ ++len;
+
+ /* Start real output */
+ save_flags(flags);
+ cli();
+
+ PRINTK(("de620_start_xmit: len=%d, bufs 0x%02x\n",
+ (int)skb->len, using_txbuf));
+
+ /* select a free tx buffer. if there is one... */
+ switch (using_txbuf) {
+ default: /* both are free: use TXBF0 */
+ case TXBF1: /* use TXBF0 */
+ de620_send_command(dev,W_CR | RW0);
+ using_txbuf |= TXBF0;
+ break;
+
+ case TXBF0: /* use TXBF1 */
+ de620_send_command(dev,W_CR | RW1);
+ using_txbuf |= TXBF1;
+ break;
+
+ case (TXBF0 | TXBF1): /* NONE!!! */
+ printk("de620: Ouch! No tx-buffer available!\n");
+ restore_flags(flags);
+ return 1;
+ break;
+ }
+ de620_write_block(dev, buffer, len);
+
+ dev->trans_start = jiffies;
+ dev->tbusy = (using_txbuf == (TXBF0 | TXBF1)); /* Boolean! */
+
+ ((struct netstats *)(dev->priv))->tx_packets++;
+
+ restore_flags(flags); /* interrupts maybe back on */
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ return 0;
+}
+
+/*****************************************************
+ *
+ * Handle the network interface interrupts.
+ *
+ */
+static void
+de620_interrupt(int irq_in, struct pt_regs *regs)
+{
+ struct device *dev = irq2dev_map[irq_in];
+ byte irq_status;
+ int bogus_count = 0;
+ int again = 0;
+
+ /* This might be deleted now, no crummy drivers present :-) Or..? */
+ if ((dev == NULL) || (irq != irq_in)) {
+ printk("%s: bogus interrupt %d\n", dev?dev->name:"de620", irq_in);
+ return;
+ }
+
+ cli();
+ dev->interrupt = 1;
+
+ /* Read the status register (_not_ the status port) */
+ irq_status = de620_get_register(dev, R_STS);
+
+ PRINTK(("de620_interrupt (%2.2X)\n", irq_status));
+
+ if (irq_status & RXGOOD) {
+ do {
+ again = de620_rx_intr(dev);
+ PRINTK(("again=%d\n", again));
+ }
+ while (again && (++bogus_count < 100));
+ }
+
+ dev->tbusy = (de620_tx_buffs(dev) == (TXBF0 | TXBF1)); /* Boolean! */
+
+ dev->interrupt = 0;
+ sti();
+ return;
+}
+
+/**************************************
+ *
+ * Get a packet from the adapter
+ *
+ * Send it "upstairs"
+ *
+ */
+static int
+de620_rx_intr(struct device *dev)
+{
+ struct header_buf {
+ byte status;
+ byte Rx_NextPage;
+ unsigned short Rx_ByteCount;
+ } header_buf;
+ struct sk_buff *skb;
+ int size;
+ byte *buffer;
+ byte pagelink;
+ byte curr_page;
+
+ PRINTK(("de620_rx_intr: next_rx_page = %d\n", next_rx_page));
+
+ /* Tell the adapter that we are going to read data, and from where */
+ de620_send_command(dev, W_CR | RRN);
+ de620_set_register(dev, W_RSA1, next_rx_page);
+ de620_set_register(dev, W_RSA0, 0);
+
+ /* Deep breath, and away we goooooo */
+ de620_read_block(dev, (byte *)&header_buf, sizeof(struct header_buf));
+ PRINTK(("page status=0x%02x, nextpage=%d, packetsize=%d\n",
+ header_buf.status, header_buf.Rx_NextPage, header_buf.Rx_ByteCount));
+
+ /* Plausible page header? */
+ pagelink = header_buf.Rx_NextPage;
+ if ((pagelink < first_rx_page) || (last_rx_page < pagelink)) {
+ /* Ouch... Forget it! Skip all and start afresh... */
+ printk("%s: Ring overrun? Restoring...\n", dev->name);
+ /* You win some, you loose some. And sometimes plenty... */
+ adapter_init(dev);
+ ((struct netstats *)(dev->priv))->rx_over_errors++;
+ return 0;
+ }
+
+ /* OK, this look good, so far. Let's see if it's consistent... */
+ /* Let's compute the start of the next packet, based on where we are */
+ pagelink = next_rx_page +
+ ((header_buf.Rx_ByteCount + (4 - 1 + 0x100)) >> 8);
+
+ /* Are we going to wrap around the page counter? */
+ if (pagelink > last_rx_page)
+ pagelink -= (last_rx_page - first_rx_page + 1);
+
+ /* Is the _computed_ next page number equal to what the adapter says? */
+ if (pagelink != header_buf.Rx_NextPage) {
+ /* Naah, we'll skip this packet. Probably bogus data as well */
+ printk("%s: Page link out of sync! Restoring...\n", dev->name);
+ next_rx_page = header_buf.Rx_NextPage; /* at least a try... */
+ de620_send_command(dev, W_DUMMY);
+ de620_set_register(dev, W_NPRF, next_rx_page);
+ ((struct netstats *)(dev->priv))->rx_over_errors++;
+ return 0;
+ }
+ next_rx_page = pagelink;
+
+ size = header_buf.Rx_ByteCount - 4;
+ if ((size < RUNT) || (GIANT < size)) {
+ printk("%s: Illegal packet size: %d!\n", dev->name, size);
+ }
+ else { /* Good packet? */
+ skb = dev_alloc_skb(size+2);
+ if (skb == NULL) { /* Yeah, but no place to put it... */
+ printk("%s: Couldn't allocate a sk_buff of size %d.\n",
+ dev->name, size);
+ ((struct netstats *)(dev->priv))->rx_dropped++;
+ }
+ else { /* Yep! Go get it! */
+ skb_reserve(skb,2); /* Align */
+ skb->dev = dev;
+ skb->free = 1;
+ /* skb->data points to the start of sk_buff data area */
+ buffer = skb_put(skb,size);
+ /* copy the packet into the buffer */
+ de620_read_block(dev, buffer, size);
+ PRINTK(("Read %d bytes\n", size));
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb); /* deliver it "upstairs" */
+ /* count all receives */
+ ((struct netstats *)(dev->priv))->rx_packets++;
+ }
+ }
+
+ /* Let's peek ahead to see if we have read the last current packet */
+ /* NOTE! We're _not_ checking the 'EMPTY'-flag! This seems better... */
+ curr_page = de620_get_register(dev, R_CPR);
+ de620_set_register(dev, W_NPRF, next_rx_page);
+ PRINTK(("next_rx_page=%d CPR=%d\n", next_rx_page, curr_page));
+
+ return (next_rx_page != curr_page); /* That was slightly tricky... */
+}
+
+/*********************************************
+ *
+ * Reset the adapter to a known state
+ *
+ */
+static int
+adapter_init(struct device *dev)
+{
+ int i;
+ static int was_down = 0;
+
+ if ((nic_data.Model == 3) || (nic_data.Model == 0)) { /* CT */
+ EIPRegister = NCTL0;
+ if (nic_data.Media != 1)
+ EIPRegister |= NIS0; /* not BNC */
+ }
+ else if (nic_data.Model == 2) { /* UTP */
+ EIPRegister = NCTL0 | NIS0;
+ }
+
+ if (utp)
+ EIPRegister = NCTL0 | NIS0;
+ if (bnc)
+ EIPRegister = NCTL0;
+
+ de620_send_command(dev, W_CR | RNOP | CLEAR);
+ de620_send_command(dev, W_CR | RNOP);
+
+ de620_set_register(dev, W_SCR, SCR_DEF);
+ /* disable recv to wait init */
+ de620_set_register(dev, W_TCR, RXOFF);
+
+ /* Set the node ID in the adapter */
+ for (i = 0; i < 6; ++i) { /* W_PARn = 0xaa + n */
+ de620_set_register(dev, W_PAR0 + i, dev->dev_addr[i]);
+ }
+
+ de620_set_register(dev, W_EIP, EIPRegister);
+
+ next_rx_page = first_rx_page = DE620_RX_START_PAGE;
+ if (nic_data.RAM_Size)
+ last_rx_page = nic_data.RAM_Size - 1;
+ else /* 64k RAM */
+ last_rx_page = 255;
+
+ de620_set_register(dev, W_SPR, first_rx_page); /* Start Page Register*/
+ de620_set_register(dev, W_EPR, last_rx_page); /* End Page Register */
+ de620_set_register(dev, W_CPR, first_rx_page);/*Current Page Register*/
+ de620_send_command(dev, W_NPR | first_rx_page); /* Next Page Register*/
+ de620_send_command(dev, W_DUMMY);
+ de620_set_delay(dev);
+
+ /* Final sanity check: Anybody out there? */
+ /* Let's hope some bits from the statusregister make a good check */
+#define CHECK_MASK ( 0 | TXSUC | T16 | 0 | RXCRC | RXSHORT | 0 | 0 )
+#define CHECK_OK ( 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 )
+ /* success: X 0 0 X 0 0 X X */
+ /* ignore: EEDI RXGOOD COLS LNKS*/
+
+ if (((i = de620_get_register(dev, R_STS)) & CHECK_MASK) != CHECK_OK) {
+ printk("Something has happened to the DE-620! Please check it"
+#ifdef SHUTDOWN_WHEN_LOST
+ " and do a new ifconfig"
+#endif
+ "! (%02x)\n", i);
+#ifdef SHUTDOWN_WHEN_LOST
+ /* Goodbye, cruel world... */
+ dev->flags &= ~IFF_UP;
+ de620_close(dev);
+#endif
+ was_down = 1;
+ return 1; /* failed */
+ }
+ if (was_down) {
+ printk("Thanks, I feel much better now!\n");
+ was_down = 0;
+ }
+
+ /* All OK, go ahead... */
+ de620_set_register(dev, W_TCR, TCR_DEF);
+
+ return 0; /* all ok */
+}
+
+/******************************************************************************
+ *
+ * Only start-up code below
+ *
+ */
+/****************************************
+ *
+ * Check if there is a DE-620 connected
+ */
+int
+de620_probe(struct device *dev)
+{
+ static struct netstats de620_netstats;
+ int i;
+ byte checkbyte = 0xa5;
+
+ /*
+ * This is where the base_addr and irq gets set.
+ * Tunable at compile-time and insmod-time
+ */
+ dev->base_addr = io;
+ dev->irq = irq;
+
+ if (de620_debug)
+ printk(version);
+
+ printk("D-Link DE-620 pocket adapter");
+
+ /* Initially, configure basic nibble mode, so we can read the EEPROM */
+ NIC_Cmd = DEF_NIC_CMD;
+ de620_set_register(dev, W_EIP, EIPRegister);
+
+ /* Anybody out there? */
+ de620_set_register(dev, W_CPR, checkbyte);
+ checkbyte = de620_get_register(dev, R_CPR);
+
+ if ((checkbyte != 0xa5) || (read_eeprom(dev) != 0)) {
+ printk(" not identified in the printer port\n");
+ return ENODEV;
+ }
+
+#if 0 /* Not yet */
+ if (check_region(dev->base_addr, 3)) {
+ printk(", port 0x%x busy\n", dev->base_addr);
+ return EBUSY;
+ }
+#endif
+ request_region(dev->base_addr, 3, "de620");
+
+ /* else, got it! */
+ printk(", Ethernet Address: %2.2X",
+ dev->dev_addr[0] = nic_data.NodeID[0]);
+ for (i = 1; i < ETH_ALEN; i++) {
+ printk(":%2.2X", dev->dev_addr[i] = nic_data.NodeID[i]);
+ dev->broadcast[i] = 0xff;
+ }
+
+ printk(" (%dk RAM,",
+ (nic_data.RAM_Size) ? (nic_data.RAM_Size >> 2) : 64);
+
+ if (nic_data.Media == 1)
+ printk(" BNC)\n");
+ else
+ printk(" UTP)\n");
+
+ /* Initialize the device structure. */
+ /*dev->priv = kmalloc(sizeof(struct netstats), GFP_KERNEL);*/
+ dev->priv = &de620_netstats;
+
+ memset(dev->priv, 0, sizeof(struct netstats));
+ dev->get_stats = get_stats;
+ dev->open = de620_open;
+ dev->stop = de620_close;
+ dev->hard_start_xmit = &de620_start_xmit;
+ dev->set_multicast_list = &de620_set_multicast_list;
+ /* base_addr and irq are already set, see above! */
+
+ ether_setup(dev);
+
+ /* dump eeprom */
+ if (de620_debug) {
+ printk("\nEEPROM contents:\n");
+ printk("RAM_Size = 0x%02X\n", nic_data.RAM_Size);
+ printk("NodeID = %02X:%02X:%02X:%02X:%02X:%02X\n",
+ nic_data.NodeID[0], nic_data.NodeID[1],
+ nic_data.NodeID[2], nic_data.NodeID[3],
+ nic_data.NodeID[4], nic_data.NodeID[5]);
+ printk("Model = %d\n", nic_data.Model);
+ printk("Media = %d\n", nic_data.Media);
+ printk("SCR = 0x%02x\n", nic_data.SCR);
+ }
+
+ return 0;
+}
+
+/**********************************
+ *
+ * Read info from on-board EEPROM
+ *
+ * Note: Bitwise serial I/O to/from the EEPROM vi the status _register_!
+ */
+#define sendit(dev,data) de620_set_register(dev, W_EIP, data | EIPRegister);
+
+static unsigned short
+ReadAWord(struct device *dev, int from)
+{
+ unsigned short data;
+ int nbits;
+
+ /* cs [__~~] SET SEND STATE */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 0); sendit(dev, 1); sendit(dev, 5); sendit(dev, 4);
+
+ /* Send the 9-bit address from where we want to read the 16-bit word */
+ for (nbits = 9; nbits > 0; --nbits, from <<= 1) {
+ if (from & 0x0100) { /* bit set? */
+ /* cs [~~~~] SEND 1 */
+ /* di [~~~~] */
+ /* sck [_~~_] */
+ sendit(dev, 6); sendit(dev, 7); sendit(dev, 7); sendit(dev, 6);
+ }
+ else {
+ /* cs [~~~~] SEND 0 */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4);
+ }
+ }
+
+ /* Shift in the 16-bit word. The bits appear serially in EEDI (=0x80) */
+ for (data = 0, nbits = 16; nbits > 0; --nbits) {
+ /* cs [~~~~] SEND 0 */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 4); sendit(dev, 5); sendit(dev, 5); sendit(dev, 4);
+ data = (data << 1) | ((de620_get_register(dev, R_STS) & EEDI) >> 7);
+ }
+ /* cs [____] RESET SEND STATE */
+ /* di [____] */
+ /* sck [_~~_] */
+ sendit(dev, 0); sendit(dev, 1); sendit(dev, 1); sendit(dev, 0);
+
+ return data;
+}
+
+static int
+read_eeprom(struct device *dev)
+{
+ unsigned short wrd;
+
+ /* D-Link Ethernet addresses are in the series 00:80:c8:7X:XX:XX:XX */
+ wrd = ReadAWord(dev, 0x1aa); /* bytes 0 + 1 of NodeID */
+ if (!clone && (wrd != htons(0x0080))) /* Valid D-Link ether sequence? */
+ return -1; /* Nope, not a DE-620 */
+ nic_data.NodeID[0] = wrd & 0xff;
+ nic_data.NodeID[1] = wrd >> 8;
+
+ wrd = ReadAWord(dev, 0x1ab); /* bytes 2 + 3 of NodeID */
+ if (!clone && ((wrd & 0xff) != 0xc8)) /* Valid D-Link ether sequence? */
+ return -1; /* Nope, not a DE-620 */
+ nic_data.NodeID[2] = wrd & 0xff;
+ nic_data.NodeID[3] = wrd >> 8;
+
+ wrd = ReadAWord(dev, 0x1ac); /* bytes 4 + 5 of NodeID */
+ nic_data.NodeID[4] = wrd & 0xff;
+ nic_data.NodeID[5] = wrd >> 8;
+
+ wrd = ReadAWord(dev, 0x1ad); /* RAM size in pages (256 bytes). 0 = 64k */
+ nic_data.RAM_Size = (wrd >> 8);
+
+ wrd = ReadAWord(dev, 0x1ae); /* hardware model (CT = 3) */
+ nic_data.Model = (wrd & 0xff);
+
+ wrd = ReadAWord(dev, 0x1af); /* media (indicates BNC/UTP) */
+ nic_data.Media = (wrd & 0xff);
+
+ wrd = ReadAWord(dev, 0x1a8); /* System Configuration Register */
+ nic_data.SCR = (wrd >> 8);
+
+ return 0; /* no errors */
+}
+
+/******************************************************************************
+ *
+ * Loadable module skeleton
+ *
+ */
+#ifdef MODULE
+static char nullname[8] = "";
+static struct device de620_dev = {
+ nullname, 0, 0, 0, 0, 0, 0, 0, 0, 0, NULL, de620_probe };
+
+int
+init_module(void)
+{
+ if (register_netdev(&de620_dev) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&de620_dev);
+ release_region(de620_dev.base_addr, 3);
+}
+#endif /* MODULE */
+
+/*
+ * (add '-DMODULE' when compiling as loadable module)
+ *
+ * compile-command:
+ * gcc -D__KERNEL__ -Wall -Wstrict-prototypes -O2 \
+ * -fomit-frame-pointer -m486 \
+ * -I/usr/src/linux/include -I../../net/inet -c de620.c
+*/
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
+ * compile-command: "gcc -D__KERNEL__ -DMODULE -Ilinux/include -I../../net/inet -Wall -Wstrict-prototypes -O2 -m486 -c de620.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/de620.h b/i386/i386at/gpl/linux/net/de620.h
new file mode 100644
index 00000000..e8d9a88f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/de620.h
@@ -0,0 +1,117 @@
+/*********************************************************
+ * *
+ * Definition of D-Link DE-620 Ethernet Pocket adapter *
+ * *
+ *********************************************************/
+
+/* DE-620's CMD port Command */
+#define CS0 0x08 /* 1->0 command strobe */
+#define ICEN 0x04 /* 0=enable DL3520 host interface */
+#define DS0 0x02 /* 1->0 data strobe 0 */
+#define DS1 0x01 /* 1->0 data strobe 1 */
+
+#define WDIR 0x20 /* general 0=read 1=write */
+#define RDIR 0x00 /* (not 100% confirm ) */
+#define PS2WDIR 0x00 /* ps/2 mode 1=read, 0=write */
+#define PS2RDIR 0x20
+
+#define IRQEN 0x10 /* 1 = enable printer IRQ line */
+#define SELECTIN 0x08 /* 1 = select printer */
+#define INITP 0x04 /* 0 = initial printer */
+#define AUTOFEED 0x02 /* 1 = printer auto form feed */
+#define STROBE 0x01 /* 0->1 data strobe */
+
+#define RESET 0x08
+#define NIS0 0x20 /* 0 = BNC, 1 = UTP */
+#define NCTL0 0x10
+
+/* DE-620 DIC Command */
+#define W_DUMMY 0x00 /* DIC reserved command */
+#define W_CR 0x20 /* DIC write command register */
+#define W_NPR 0x40 /* DIC write Next Page Register */
+#define W_TBR 0x60 /* DIC write Tx Byte Count 1 reg */
+#define W_RSA 0x80 /* DIC write Remote Start Addr 1 */
+
+/* DE-620's STAT port bits 7-4 */
+#define EMPTY 0x80 /* 1 = receive buffer empty */
+#define INTLEVEL 0x40 /* 1 = interrupt level is high */
+#define TXBF1 0x20 /* 1 = transmit buffer 1 is in use */
+#define TXBF0 0x10 /* 1 = transmit buffer 0 is in use */
+#define READY 0x08 /* 1 = h/w ready to accept cmd/data */
+
+/* IDC 1 Command */
+#define W_RSA1 0xa0 /* write remote start address 1 */
+#define W_RSA0 0xa1 /* write remote start address 0 */
+#define W_NPRF 0xa2 /* write next page register NPR15-NPR8 */
+#define W_DFR 0xa3 /* write delay factor register */
+#define W_CPR 0xa4 /* write current page register */
+#define W_SPR 0xa5 /* write start page register */
+#define W_EPR 0xa6 /* write end page register */
+#define W_SCR 0xa7 /* write system configuration register */
+#define W_TCR 0xa8 /* write Transceiver Configuration reg */
+#define W_EIP 0xa9 /* write EEPM Interface port */
+#define W_PAR0 0xaa /* write physical address register 0 */
+#define W_PAR1 0xab /* write physical address register 1 */
+#define W_PAR2 0xac /* write physical address register 2 */
+#define W_PAR3 0xad /* write physical address register 3 */
+#define W_PAR4 0xae /* write physical address register 4 */
+#define W_PAR5 0xaf /* write physical address register 5 */
+
+/* IDC 2 Command */
+#define R_STS 0xc0 /* read status register */
+#define R_CPR 0xc1 /* read current page register */
+#define R_BPR 0xc2 /* read boundary page register */
+#define R_TDR 0xc3 /* read time domain reflectometry reg */
+
+/* STATUS Register */
+#define EEDI 0x80 /* EEPM DO pin */
+#define TXSUC 0x40 /* tx success */
+#define T16 0x20 /* tx fail 16 times */
+#define TS1 0x40 /* 0=Tx success, 1=T16 */
+#define TS0 0x20 /* 0=Tx success, 1=T16 */
+#define RXGOOD 0x10 /* rx a good packet */
+#define RXCRC 0x08 /* rx a CRC error packet */
+#define RXSHORT 0x04 /* rx a short packet */
+#define COLS 0x02 /* coaxial collision status */
+#define LNKS 0x01 /* UTP link status */
+
+/* Command Register */
+#define CLEAR 0x10 /* reset part of hardware */
+#define NOPER 0x08 /* No Operation */
+#define RNOP 0x08
+#define RRA 0x06 /* After RR then auto-advance NPR & BPR(=NPR-1) */
+#define RRN 0x04 /* Normal Remote Read mode */
+#define RW1 0x02 /* Remote Write tx buffer 1 ( page 6 - 11 ) */
+#define RW0 0x00 /* Remote Write tx buffer 0 ( page 0 - 5 ) */
+#define TXEN 0x01 /* 0->1 tx enable */
+
+/* System Configuration Register */
+#define TESTON 0x80 /* test host data transfer reliability */
+#define SLEEP 0x40 /* sleep mode */
+#if 0
+#define FASTMODE 0x04 /* fast mode for intel 82360SL fast mode */
+#define BYTEMODE 0x02 /* byte mode */
+#else
+#define FASTMODE 0x20 /* fast mode for intel 82360SL fast mode */
+#define BYTEMODE 0x10 /* byte mode */
+#endif
+#define NIBBLEMODE 0x00 /* nibble mode */
+#define IRQINV 0x08 /* turn off IRQ line inverter */
+#define IRQNML 0x00 /* turn on IRQ line inverter */
+#define INTON 0x04
+#define AUTOFFSET 0x02 /* auto shift address to TPR+12 */
+#define AUTOTX 0x01 /* auto tx when leave RW mode */
+
+/* Transceiver Configuration Register */
+#define JABBER 0x80 /* generate jabber condition */
+#define TXSUCINT 0x40 /* enable tx success interrupt */
+#define T16INT 0x20 /* enable T16 interrupt */
+#define RXERRPKT 0x10 /* accept CRC error or short packet */
+#define EXTERNALB2 0x0C /* external loopback 2 */
+#define EXTERNALB1 0x08 /* external loopback 1 */
+#define INTERNALB 0x04 /* internal loopback */
+#define NMLOPERATE 0x00 /* normal operation */
+#define RXPBM 0x03 /* rx physical, broadcast, multicast */
+#define RXPB 0x02 /* rx physical, broadcast */
+#define RXALL 0x01 /* rx all packet */
+#define RXOFF 0x00 /* rx disable */
diff --git a/i386/i386at/gpl/linux/net/depca.c b/i386/i386at/gpl/linux/net/depca.c
new file mode 100644
index 00000000..ae9a8ca3
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/depca.c
@@ -0,0 +1,1901 @@
+/* depca.c: A DIGITAL DEPCA & EtherWORKS ethernet driver for linux.
+
+ Written 1994, 1995 by David C. Davies.
+
+
+ Copyright 1994 David C. Davies
+ and
+ United States Government
+ (as represented by the Director, National Security Agency).
+
+ Copyright 1995 Digital Equipment Corporation.
+
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of DEPCA and EtherWORKS ethernet cards:
+
+ DEPCA (the original)
+ DE100
+ DE101
+ DE200 Turbo
+ DE201 Turbo
+ DE202 Turbo (TP BNC)
+ DE210
+ DE422 (EISA)
+
+ The driver has been tested on DE100, DE200 and DE202 cards in a
+ relatively busy network. The DE422 has been tested a little.
+
+ This driver will NOT work for the DE203, DE204 and DE205 series of
+ cards, since they have a new custom ASIC in place of the AMD LANCE
+ chip. See the 'ewrk3.c' driver in the Linux source tree for running
+ those cards.
+
+ I have benchmarked the driver with a DE100 at 595kB/s to (542kB/s from)
+ a DECstation 5000/200.
+
+ The author may be reached at davies@wanton.lkg.dec.com or
+ davies@maniac.ultranet.com or Digital Equipment Corporation, 550 King
+ Street, Littleton MA 01460.
+
+ =========================================================================
+
+ The driver was originally based on the 'lance.c' driver from Donald
+ Becker which is included with the standard driver distribution for
+ linux. V0.4 is a complete re-write with only the kernel interface
+ remaining from the original code.
+
+ 1) Lance.c code in /linux/drivers/net/
+ 2) "Ethernet/IEEE 802.3 Family. 1992 World Network Data Book/Handbook",
+ AMD, 1992 [(800) 222-9323].
+ 3) "Am79C90 CMOS Local Area Network Controller for Ethernet (C-LANCE)",
+ AMD, Pub. #17881, May 1993.
+ 4) "Am79C960 PCnet-ISA(tm), Single-Chip Ethernet Controller for ISA",
+ AMD, Pub. #16907, May 1992
+ 5) "DEC EtherWORKS LC Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE100-OM.003
+ 6) "DEC EtherWORKS Turbo Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1990, Pub. #EK-DE200-OM.003
+ 7) "DEPCA Hardware Reference Manual", Pub. #EK-DEPCA-PR
+ Digital Equipment Corporation, 1989
+ 8) "DEC EtherWORKS Turbo_(TP BNC) Ethernet Controller Owners Manual",
+ Digital Equipment corporation, 1991, Pub. #EK-DE202-OM.001
+
+
+ Peter Bauer's depca.c (V0.5) was referred to when debugging V0.1 of this
+ driver.
+
+ The original DEPCA card requires that the ethernet ROM address counter
+ be enabled to count and has an 8 bit NICSR. The ROM counter enabling is
+ only done when a 0x08 is read as the first address octet (to minimise
+ the chances of writing over some other hardware's I/O register). The
+ NICSR accesses have been changed to byte accesses for all the cards
+ supported by this driver, since there is only one useful bit in the MSB
+ (remote boot timeout) and it is not used. Also, there is a maximum of
+ only 48kB network RAM for this card. My thanks to Torbjorn Lindh for
+ help debugging all this (and holding my feet to the fire until I got it
+ right).
+
+ The DE200 series boards have on-board 64kB RAM for use as a shared
+ memory network buffer. Only the DE100 cards make use of a 2kB buffer
+ mode which has not been implemented in this driver (only the 32kB and
+ 64kB modes are supported [16kB/48kB for the original DEPCA]).
+
+ At the most only 2 DEPCA cards can be supported on the ISA bus because
+ there is only provision for two I/O base addresses on each card (0x300
+ and 0x200). The I/O address is detected by searching for a byte sequence
+ in the Ethernet station address PROM at the expected I/O address for the
+ Ethernet PROM. The shared memory base address is 'autoprobed' by
+ looking for the self test PROM and detecting the card name. When a
+ second DEPCA is detected, information is placed in the base_addr
+ variable of the next device structure (which is created if necessary),
+ thus enabling ethif_probe initialization for the device. More than 2
+ EISA cards can be supported, but care will be needed assigning the
+ shared memory to ensure that each slot has the correct IRQ, I/O address
+ and shared memory address assigned.
+
+ ************************************************************************
+
+ NOTE: If you are using two ISA DEPCAs, it is important that you assign
+ the base memory addresses correctly. The driver autoprobes I/O 0x300
+ then 0x200. The base memory address for the first device must be less
+ than that of the second so that the auto probe will correctly assign the
+ I/O and memory addresses on the same card. I can't think of a way to do
+ this unambiguously at the moment, since there is nothing on the cards to
+ tie I/O and memory information together.
+
+ I am unable to test 2 cards together for now, so this code is
+ unchecked. All reports, good or bad, are welcome.
+
+ ************************************************************************
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. DEPCA and DE100 board IRQs are
+ {2,3,4,5,7}, whereas the DE200 is at {5,9,10,11,15}. Note that IRQ2 is
+ really IRQ9 in machines with 16 IRQ lines.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been added. To
+ utilise this ability, you have to do <8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy depca.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) if you wish, edit the source code near line 1530 to reflect the I/O
+ address and IRQ you're using (see also 5).
+ 3) compile depca.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the depca configuration turned off and reboot.
+ 5) insmod depca.o [irq=7] [io=0x200] [mem=0xd0000] [adapter_name=DE100]
+ [Alan Cox: Changed the code to allow command line irq/io assignments]
+ [Dave Davies: Changed the code to allow command line mem/name
+ assignments]
+ 6) run the net startup bits for your eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod depca'.
+
+ To assign a base memory address for the shared memory when running as a
+ loadable module, see 5 above. To include the adapter name (if you have
+ no PROM but know the card name) also see 5 above. Note that this last
+ option will not work with kernel built-in depca's.
+
+ The shared memory assignment for a loadable module makes sense to avoid
+ the 'memory autoprobe' picking the wrong shared memory (for the case of
+ 2 depca's in a PC).
+
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 25-jan-94 Initial writing.
+ 0.2 27-jan-94 Added LANCE TX hardware buffer chaining.
+ 0.3 1-feb-94 Added multiple DEPCA support.
+ 0.31 4-feb-94 Added DE202 recognition.
+ 0.32 19-feb-94 Tidy up. Improve multi-DEPCA support.
+ 0.33 25-feb-94 Fix DEPCA ethernet ROM counter enable.
+ Add jabber packet fix from murf@perftech.com
+ and becker@super.org
+ 0.34 7-mar-94 Fix DEPCA max network memory RAM & NICSR access.
+ 0.35 8-mar-94 Added DE201 recognition. Tidied up.
+ 0.351 30-apr-94 Added EISA support. Added DE422 recognition.
+ 0.36 16-may-94 DE422 fix released.
+ 0.37 22-jul-94 Added MODULE support
+ 0.38 15-aug-94 Added DBR ROM switch in depca_close().
+ Multi DEPCA bug fix.
+ 0.38axp 15-sep-94 Special version for Alpha AXP Linux V1.0.
+ 0.381 12-dec-94 Added DE101 recognition, fix multicast bug.
+ 0.382 9-feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.383 22-feb-95 Fix for conflict with VESA SCSI reported by
+ <stromain@alf.dec.com>
+ 0.384 17-mar-95 Fix a ring full bug reported by <bkm@star.rl.ac.uk>
+ 0.385 3-apr-95 Fix a recognition bug reported by
+ <ryan.niemi@lastfrontier.com>
+ 0.386 21-apr-95 Fix the last fix...sorry, must be galloping senility
+ 0.40 25-May-95 Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ 0.41 26-Jun-95 Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>
+ 0.42 27-Dec-95 Add 'mem' shared memory assigment for loadable
+ modules.
+ Add 'adapter_name' for loadable modules when no PROM.
+ Both above from a suggestion by
+ <pchen@woodruffs121.residence.gatech.edu>.
+ Add new multicasting code.
+
+ =========================================================================
+*/
+
+static const char *version = "depca.c:v0.42 95/12/27 davies@wanton.lkg.dec.com\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/segment.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "depca.h"
+
+#ifdef DEPCA_DEBUG
+static int depca_debug = DEPCA_DEBUG;
+#else
+static int depca_debug = 1;
+#endif
+
+#define DEPCA_NDA 0xffe0 /* No Device Address */
+
+/*
+** Ethernet PROM defines
+*/
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+/*
+** Set the number of Tx and Rx buffers. Ensure that the memory requested
+** here is <= to the amount of shared memory set up by the board switches.
+** The number of descriptors MUST BE A POWER OF 2.
+**
+** total_memory = NUM_RX_DESC*(8+RX_BUFF_SZ) + NUM_TX_DESC*(8+TX_BUFF_SZ)
+*/
+#define NUM_RX_DESC 8 /* Number of RX descriptors */
+#define NUM_TX_DESC 8 /* Number of TX descriptors */
+#define RX_BUFF_SZ 1536 /* Buffer size for each Rx buffer */
+#define TX_BUFF_SZ 1536 /* Buffer size for each Tx buffer */
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+/*
+** EISA bus defines
+*/
+#define DEPCA_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+
+/*
+** ISA Bus defines
+*/
+#define DEPCA_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0xe0000,0x00000}
+#define DEPCA_IO_PORTS {0x300, 0x200, 0}
+#define DEPCA_TOTAL_SIZE 0x10
+static short mem_chkd = 0;
+
+/*
+** Name <-> Adapter mapping
+*/
+#define DEPCA_SIGNATURE {"DEPCA",\
+ "DE100","DE101",\
+ "DE200","DE201","DE202",\
+ "DE210",\
+ "DE422",\
+ ""}
+static enum {DEPCA, de100, de101, de200, de201, de202, de210, de422, unknown} adapter;
+
+/*
+** Miscellaneous info...
+*/
+#define DEPCA_STRLEN 16
+#define MAX_NUM_DEPCAS 2
+
+/*
+** Memory Alignment. Each descriptor is 4 longwords long. To force a
+** particular alignment on the TX descriptor, adjust DESC_SKIP_LEN and
+** DESC_ALIGN. ALIGN aligns the start address of the private memory area
+** and hence the RX descriptor ring's first entry.
+*/
+#define ALIGN4 ((u_long)4 - 1) /* 1 longword align */
+#define ALIGN8 ((u_long)8 - 1) /* 2 longword (quadword) align */
+#define ALIGN ALIGN8 /* Keep the LANCE happy... */
+
+/*
+** The DEPCA Rx and Tx ring descriptors.
+*/
+struct depca_rx_desc {
+ volatile s32 base;
+ s16 buf_length; /* This length is negative 2's complement! */
+ s16 msg_length; /* This length is "normal". */
+};
+
+struct depca_tx_desc {
+ volatile s32 base;
+ s16 length; /* This length is negative 2's complement! */
+ s16 misc; /* Errors and TDR info */
+};
+
+#define LA_MASK 0x0000ffff /* LANCE address mask for mapping network RAM
+ to LANCE memory address space */
+
+/*
+** The Lance initialization block, described in databook, in common memory.
+*/
+struct depca_init {
+ u16 mode; /* Mode register */
+ u8 phys_addr[ETH_ALEN]; /* Physical ethernet address */
+ u8 mcast_table[8]; /* Multicast Hash Table. */
+ u32 rx_ring; /* Rx ring base pointer & ring length */
+ u32 tx_ring; /* Tx ring base pointer & ring length */
+};
+
+#define DEPCA_PKT_STAT_SZ 16
+#define DEPCA_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase DEPCA_PKT_STAT_SZ */
+struct depca_private {
+ char devname[DEPCA_STRLEN]; /* Device Product String */
+ char adapter_name[DEPCA_STRLEN];/* /proc/ioports string */
+ char adapter; /* Adapter type */
+ struct depca_rx_desc *rx_ring; /* Pointer to start of RX descriptor ring */
+ struct depca_tx_desc *tx_ring; /* Pointer to start of TX descriptor ring */
+ struct depca_init init_block;/* Shadow Initialization block */
+ char *rx_memcpy[NUM_RX_DESC]; /* CPU virt address of sh'd memory buffs */
+ char *tx_memcpy[NUM_TX_DESC]; /* CPU virt address of sh'd memory buffs */
+ u_long bus_offset; /* (E)ISA bus address offset vs LANCE */
+ u_long sh_mem; /* Physical start addr of shared mem area */
+ u_long dma_buffs; /* LANCE Rx and Tx buffers start address. */
+ int rx_new, tx_new; /* The next free ring entry */
+ int rx_old, tx_old; /* The ring entries to be free()ed. */
+ struct enet_statistics stats;
+ struct { /* Private stats counters */
+ u32 bins[DEPCA_PKT_STAT_SZ];
+ u32 unicast;
+ u32 multicast;
+ u32 broadcast;
+ u32 excessive_collisions;
+ u32 tx_underruns;
+ u32 excessive_underruns;
+ } pktStats;
+ int txRingMask; /* TX ring mask */
+ int rxRingMask; /* RX ring mask */
+ s32 rx_rlen; /* log2(rxRingMask+1) for the descriptors */
+ s32 tx_rlen; /* log2(txRingMask+1) for the descriptors */
+};
+
+/*
+** The transmit ring full condition is described by the tx_old and tx_new
+** pointers by:
+** tx_old = tx_new Empty ring
+** tx_old = tx_new+1 Full ring
+** tx_old+txRingMask = tx_new Full ring (wrapped condition)
+*/
+#define TX_BUFFS_AVAIL ((lp->tx_old<=lp->tx_new)?\
+ lp->tx_old+lp->txRingMask-lp->tx_new:\
+ lp->tx_old -lp->tx_new-1)
+
+/*
+** Public Functions
+*/
+static int depca_open(struct device *dev);
+static int depca_start_xmit(struct sk_buff *skb, struct device *dev);
+static void depca_interrupt(int irq, struct pt_regs * regs);
+static int depca_close(struct device *dev);
+static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+static struct enet_statistics *depca_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/*
+** Private functions
+*/
+static int depca_hw_init(struct device *dev, u_long ioaddr);
+static void depca_init_ring(struct device *dev);
+static int depca_rx(struct device *dev);
+static int depca_tx(struct device *dev);
+
+static void LoadCSRs(struct device *dev);
+static int InitRestartDepca(struct device *dev);
+static void DepcaSignature(char *name, u_long paddr);
+static int DevicePresent(u_long ioaddr);
+static int get_hw_addr(struct device *dev);
+static int EISA_signature(char *name, s32 eisa_id);
+static void SetMulticastFilter(struct device *dev);
+static void isa_probe(struct device *dev, u_long iobase);
+static void eisa_probe(struct device *dev, u_long iobase);
+static struct device *alloc_device(struct device *dev, u_long iobase);
+static int load_packet(struct device *dev, struct sk_buff *skb);
+static void depca_dbg_open(struct device *dev);
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+# else
+static u_char de1xx_irq[] = {2,3,4,5,7,0};
+static u_char de2xx_irq[] = {5,9,10,11,15,0};
+static u_char de422_irq[] = {5,9,10,11,0};
+static u_char *depca_irq;
+static int autoprobed = 0, loading_module = 0;
+#endif /* MODULE */
+
+static char name[DEPCA_STRLEN];
+static int num_depcas = 0, num_eth = 0;
+static int mem=0; /* For loadable module assignment
+ use insmod mem=0x????? .... */
+static char *adapter_name = '\0'; /* If no PROM when loadable module
+ use insmod adapter_name=DE??? ...
+ */
+/*
+** Miscellaneous defines...
+*/
+#define STOP_DEPCA \
+ outw(CSR0, DEPCA_ADDR);\
+ outw(STOP, DEPCA_DATA)
+
+
+
+int depca_probe(struct device *dev)
+{
+ int tmp = num_depcas, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else {
+ isa_probe(dev, iobase);
+ eisa_probe(dev, iobase);
+
+ if ((tmp == num_depcas) && (iobase != 0) && loading_module) {
+ printk("%s: depca_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+depca_hw_init(struct device *dev, u_long ioaddr)
+{
+ struct depca_private *lp;
+ int i, j, offset, netRAM, mem_len, status=0;
+ s16 nicsr;
+ u_long mem_start=0, mem_base[] = DEPCA_RAM_BASE_ADDRESSES;
+
+ STOP_DEPCA;
+
+ nicsr = inb(DEPCA_NICSR);
+ nicsr = ((nicsr & ~SHE & ~RBE & ~IEN) | IM);
+ outb(nicsr, DEPCA_NICSR);
+
+ if (inw(DEPCA_DATA) == STOP) {
+ if (mem == 0) {
+ for (; mem_base[mem_chkd]; mem_chkd++) {
+ mem_start = mem_base[mem_chkd];
+ DepcaSignature(name, mem_start);
+ if (*name != '\0') break;
+ }
+ } else {
+ mem_start = mem;
+ if (adapter_name) {
+ strcpy(name, adapter_name);
+ } else{
+ DepcaSignature(name, mem_start);
+ }
+ }
+
+ if ((*name != '\0') && mem_start) { /* found a DEPCA device */
+ dev->base_addr = ioaddr;
+
+ if ((ioaddr&0x0fff)==DEPCA_EISA_IO_PORTS) {/* EISA slot address */
+ printk("%s: %s at 0x%04lx (EISA slot %d)",
+ dev->name, name, ioaddr, (int)((ioaddr>>12)&0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at 0x%04lx", dev->name, name, ioaddr);
+ }
+
+ printk(", h/w address ");
+ status = get_hw_addr(dev);
+ for (i=0; i<ETH_ALEN - 1; i++) { /* get the ethernet address */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x", dev->dev_addr[i]);
+
+ if (status == 0) {
+ /* Set up the maximum amount of network RAM(kB) */
+ netRAM = ((adapter != DEPCA) ? 64 : 48);
+ if ((nicsr & _128KB) && (adapter == de422)) netRAM = 128;
+ offset = 0x0000;
+
+ /* Shared Memory Base Address */
+ if (nicsr & BUF) {
+ offset = 0x8000; /* 32kbyte RAM offset*/
+ nicsr &= ~BS; /* DEPCA RAM in top 32k */
+ netRAM -= 32;
+ }
+ mem_start += offset; /* (E)ISA start address */
+ if ((mem_len = (NUM_RX_DESC*(sizeof(struct depca_rx_desc)+RX_BUFF_SZ) +
+ NUM_TX_DESC*(sizeof(struct depca_tx_desc)+TX_BUFF_SZ) +
+ sizeof(struct depca_init))) <=
+ (netRAM<<10)) {
+ printk(",\n has %dkB RAM at 0x%.5lx", netRAM, mem_start);
+
+ /* Enable the shadow RAM. */
+ if (adapter != DEPCA) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ /* Define the device private memory */
+ dev->priv = (void *) kmalloc(sizeof(struct depca_private), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ lp = (struct depca_private *)dev->priv;
+ memset((char *)dev->priv, 0, sizeof(struct depca_private));
+ lp->adapter = adapter;
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(ioaddr, DEPCA_TOTAL_SIZE, lp->adapter_name);
+
+ /* Initialisation Block */
+ lp->sh_mem = mem_start;
+ mem_start += sizeof(struct depca_init);
+
+ /* Tx & Rx descriptors (aligned to a quadword boundary) */
+ mem_start = (mem_start + ALIGN) & ~ALIGN;
+ lp->rx_ring = (struct depca_rx_desc *)mem_start;
+
+ mem_start += (sizeof(struct depca_rx_desc) * NUM_RX_DESC);
+ lp->tx_ring = (struct depca_tx_desc *)mem_start;
+
+ mem_start += (sizeof(struct depca_tx_desc) * NUM_TX_DESC);
+ lp->bus_offset = mem_start & 0x00ff0000;
+ mem_start &= LA_MASK; /* LANCE re-mapped start address */
+
+ lp->dma_buffs = mem_start;
+
+ /* Finish initialising the ring information. */
+ lp->rxRingMask = NUM_RX_DESC - 1;
+ lp->txRingMask = NUM_TX_DESC - 1;
+
+ /* Calculate Tx/Rx RLEN size for the descriptors. */
+ for (i=0, j = lp->rxRingMask; j>0; i++) {
+ j >>= 1;
+ }
+ lp->rx_rlen = (s32)(i << 29);
+ for (i=0, j = lp->txRingMask; j>0; i++) {
+ j >>= 1;
+ }
+ lp->tx_rlen = (s32)(i << 29);
+
+ /* Load the initialisation block */
+ depca_init_ring(dev);
+
+ /* Initialise the control and status registers */
+ LoadCSRs(dev);
+
+ /* Enable DEPCA board interrupts for autoprobing */
+ nicsr = ((nicsr & ~IM)|IEN);
+ outb(nicsr, DEPCA_NICSR);
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
+#ifndef MODULE
+ unsigned char irqnum;
+ autoirq_setup(0);
+
+ /* Assign the correct irq list */
+ switch (lp->adapter) {
+ case DEPCA:
+ case de100:
+ case de101:
+ depca_irq = de1xx_irq;
+ break;
+ case de200:
+ case de201:
+ case de202:
+ case de210:
+ depca_irq = de2xx_irq;
+ break;
+ case de422:
+ depca_irq = de422_irq;
+ break;
+ }
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(INEA | INIT, DEPCA_DATA);
+
+ irqnum = autoirq_report(1);
+ if (!irqnum) {
+ printk(" and failed to detect IRQ line.\n");
+ status = -ENXIO;
+ } else {
+ for (dev->irq=0,i=0; (depca_irq[i]) && (!dev->irq); i++) {
+ if (irqnum == depca_irq[i]) {
+ dev->irq = irqnum;
+ printk(" and uses IRQ%d.\n", dev->irq);
+ }
+ }
+
+ if (!dev->irq) {
+ printk(" but incorrect IRQ line detected.\n");
+ status = -ENXIO;
+ }
+ }
+#endif /* MODULE */
+ } else {
+ printk(" and assigned IRQ%d.\n", dev->irq);
+ }
+ if (status) release_region(ioaddr, DEPCA_TOTAL_SIZE);
+ } else {
+ printk(",\n requests %dkB RAM: only %dkB is available!\n",
+ (mem_len>>10), netRAM);
+ status = -ENXIO;
+ }
+ } else {
+ printk(" which has an Ethernet PROM CRC error.\n");
+ status = -ENXIO;
+ }
+ }
+ if (!status) {
+ if (depca_debug > 0) {
+ printk(version);
+ }
+
+ /* The DEPCA-specific entries in the device structure. */
+ dev->open = &depca_open;
+ dev->hard_start_xmit = &depca_start_xmit;
+ dev->stop = &depca_close;
+ dev->get_stats = &depca_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &depca_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+ } else { /* Incorrectly initialised hardware */
+ if (dev->priv) {
+ kfree_s(dev->priv, sizeof(struct depca_private));
+ dev->priv = NULL;
+ }
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ return status;
+}
+
+
+static int
+depca_open(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ s16 nicsr;
+ int status = 0;
+
+ irq2dev_map[dev->irq] = dev;
+ STOP_DEPCA;
+ nicsr = inb(DEPCA_NICSR);
+
+ /* Make sure the shadow RAM is enabled */
+ if (adapter != DEPCA) {
+ nicsr |= SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ /* Re-initialize the DEPCA... */
+ depca_init_ring(dev);
+ LoadCSRs(dev);
+
+ depca_dbg_open(dev);
+
+ if (request_irq(dev->irq, &depca_interrupt, 0, lp->adapter_name)) {
+ printk("depca_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ /* Enable DEPCA board interrupts and turn off LED */
+ nicsr = ((nicsr & ~IM & ~LED)|IEN);
+ outb(nicsr, DEPCA_NICSR);
+ outw(CSR0,DEPCA_ADDR);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ status = InitRestartDepca(dev);
+
+ if (depca_debug > 1){
+ printk("CSR0: 0x%4.4x\n",inw(DEPCA_DATA));
+ printk("nicsr: 0x%02x\n",inb(DEPCA_NICSR));
+ }
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/* Initialize the lance Rx and Tx descriptor rings. */
+static void
+depca_init_ring(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_int i;
+ u_long p;
+
+ /* Lock out other processes whilst setting up the hardware */
+ set_bit(0, (void *)&dev->tbusy);
+
+ lp->rx_new = lp->tx_new = 0;
+ lp->rx_old = lp->tx_old = 0;
+
+ /* Initialize the base addresses and length of each buffer in the ring */
+ for (i = 0; i <= lp->rxRingMask; i++) {
+ writel((p=lp->dma_buffs+i*RX_BUFF_SZ) | R_OWN, &lp->rx_ring[i].base);
+ writew(-RX_BUFF_SZ, &lp->rx_ring[i].buf_length);
+ lp->rx_memcpy[i]=(char *)(p+lp->bus_offset);
+ }
+ for (i = 0; i <= lp->txRingMask; i++) {
+ writel((p=lp->dma_buffs+(i+lp->txRingMask+1)*TX_BUFF_SZ) & 0x00ffffff,
+ &lp->tx_ring[i].base);
+ lp->tx_memcpy[i]=(char *)(p+lp->bus_offset);
+ }
+
+ /* Set up the initialization block */
+ lp->init_block.rx_ring = ((u32)((u_long)lp->rx_ring)&LA_MASK) | lp->rx_rlen;
+ lp->init_block.tx_ring = ((u32)((u_long)lp->tx_ring)&LA_MASK) | lp->tx_rlen;
+
+ SetMulticastFilter(dev);
+
+ for (i = 0; i < ETH_ALEN; i++) {
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ }
+
+ lp->init_block.mode = 0x0000; /* Enable the Tx and Rx */
+
+ return;
+}
+
+/*
+** Writes a socket buffer to TX descriptor ring and starts transmission
+*/
+static int
+depca_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ int status = 0;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 1*HZ) {
+ status = -1;
+ } else {
+ printk("%s: transmit timed out, status %04x, resetting.\n",
+ dev->name, inw(DEPCA_DATA));
+
+ STOP_DEPCA;
+ depca_init_ring(dev);
+ LoadCSRs(dev);
+ dev->interrupt = UNMASK_INTERRUPTS;
+ dev->start = 1;
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ InitRestartDepca(dev);
+ }
+ return status;
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len > 0) {
+ /* Enforce 1 process per h/w access */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ status = -1;
+ } else {
+ if (TX_BUFFS_AVAIL) { /* Fill in a Tx ring entry */
+ status = load_packet(dev, skb);
+
+ if (!status) {
+ /* Trigger an immediate send demand. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(INEA | TDMD, DEPCA_DATA);
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb(skb, FREE_WRITE);
+ }
+ if (TX_BUFFS_AVAIL) {
+ dev->tbusy=0;
+ }
+ } else {
+ status = -1;
+ }
+ }
+ }
+
+ return status;
+}
+
+/*
+** The DEPCA interrupt handler.
+*/
+static void
+depca_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct depca_private *lp;
+ s16 csr0, nicsr;
+ u_long ioaddr;
+
+ if (dev == NULL) {
+ printk ("depca_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct depca_private *)dev->priv;
+ ioaddr = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = MASK_INTERRUPTS;
+
+ /* mask the DEPCA board interrupts and turn on the LED */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= (IM|LED);
+ outb(nicsr, DEPCA_NICSR);
+
+ outw(CSR0, DEPCA_ADDR);
+ csr0 = inw(DEPCA_DATA);
+
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & INTE, DEPCA_DATA);
+
+ if (csr0 & RINT) /* Rx interrupt (packet arrived) */
+ depca_rx(dev);
+
+ if (csr0 & TINT) /* Tx interrupt (packet sent) */
+ depca_tx(dev);
+
+ if ((TX_BUFFS_AVAIL >= 0) && dev->tbusy) { /* any resources available? */
+ dev->tbusy = 0; /* clear TX busy flag */
+ mark_bh(NET_BH);
+ }
+
+ /* Unmask the DEPCA board interrupts and turn off the LED */
+ nicsr = (nicsr & ~IM & ~LED);
+ outb(nicsr, DEPCA_NICSR);
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ }
+
+ return;
+}
+
+static int
+depca_rx(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ int i, entry;
+ s32 status;
+
+ for (entry=lp->rx_new;
+ !(readl(&lp->rx_ring[entry].base) & R_OWN);
+ entry=lp->rx_new){
+ status = readl(&lp->rx_ring[entry].base) >> 16 ;
+ if (status & R_STP) { /* Remember start of frame */
+ lp->rx_old = entry;
+ }
+ if (status & R_ENP) { /* Valid frame status */
+ if (status & R_ERR) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (status & R_FRAM) lp->stats.rx_frame_errors++;
+ if (status & R_OFLO) lp->stats.rx_over_errors++;
+ if (status & R_CRC) lp->stats.rx_crc_errors++;
+ if (status & R_BUFF) lp->stats.rx_fifo_errors++;
+ } else {
+ short len, pkt_len = readw(&lp->rx_ring[entry].msg_length);
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb != NULL) {
+ unsigned char *buf;
+ skb_reserve(skb,2); /* 16 byte align the IP header */
+ buf = skb_put(skb,pkt_len);
+ skb->dev = dev;
+ if (entry < lp->rx_old) { /* Wrapped buffer */
+ len = (lp->rxRingMask - lp->rx_old + 1) * RX_BUFF_SZ;
+ memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], len);
+ memcpy_fromio(buf + len, lp->rx_memcpy[0], pkt_len-len);
+ } else { /* Linear buffer */
+ memcpy_fromio(buf, lp->rx_memcpy[lp->rx_old], pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i=1; i<DEPCA_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < (i*DEPCA_PKT_BIN_SZ)) {
+ lp->pktStats.bins[i]++;
+ i = DEPCA_PKT_STAT_SZ;
+ }
+ }
+ if (buf[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s16 *)&buf[0] == -1) &&
+ (*(s16 *)&buf[2] == -1) &&
+ (*(s16 *)&buf[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s16 *)&buf[0] == *(s16 *)&dev->dev_addr[0]) &&
+ (*(s16 *)&buf[2] == *(s16 *)&dev->dev_addr[2]) &&
+ (*(s16 *)&buf[4] == *(s16 *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset((char *)&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+ /* Change buffer ownership for this last frame, back to the adapter */
+ for (; lp->rx_old!=entry; lp->rx_old=(++lp->rx_old)&lp->rxRingMask) {
+ writel(readl(&lp->rx_ring[lp->rx_old].base) | R_OWN,
+ &lp->rx_ring[lp->rx_old].base);
+ }
+ writel(readl(&lp->rx_ring[entry].base) | R_OWN, &lp->rx_ring[entry].base);
+ }
+
+ /*
+ ** Update entry information
+ */
+ lp->rx_new = (++lp->rx_new) & lp->rxRingMask;
+ }
+
+ return 0;
+}
+
+/*
+** Buffer sent - check for buffer errors.
+*/
+static int
+depca_tx(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ int entry;
+ s32 status;
+ u_long ioaddr = dev->base_addr;
+
+ for (entry = lp->tx_old; entry != lp->tx_new; entry = lp->tx_old) {
+ status = readl(&lp->tx_ring[entry].base) >> 16 ;
+
+ if (status < 0) { /* Packet not yet sent! */
+ break;
+ } else if (status & T_ERR) { /* An error occured. */
+ status = readl(&lp->tx_ring[entry].misc);
+ lp->stats.tx_errors++;
+ if (status & TMD3_RTRY) lp->stats.tx_aborted_errors++;
+ if (status & TMD3_LCAR) lp->stats.tx_carrier_errors++;
+ if (status & TMD3_LCOL) lp->stats.tx_window_errors++;
+ if (status & TMD3_UFLO) lp->stats.tx_fifo_errors++;
+ if (status & (TMD3_BUFF | TMD3_UFLO)) {
+ /* Trigger an immediate send demand. */
+ outw(CSR0, DEPCA_ADDR);
+ outw(INEA | TDMD, DEPCA_DATA);
+ }
+ } else if (status & (T_MORE | T_ONE)) {
+ lp->stats.collisions++;
+ } else {
+ lp->stats.tx_packets++;
+ }
+
+ /* Update all the pointers */
+ lp->tx_old = (++lp->tx_old) & lp->txRingMask;
+ }
+
+ return 0;
+}
+
+static int
+depca_close(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ s16 nicsr;
+ u_long ioaddr = dev->base_addr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ outw(CSR0, DEPCA_ADDR);
+
+ if (depca_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(DEPCA_DATA));
+ }
+
+ /*
+ ** We stop the DEPCA here -- it occasionally polls
+ ** memory if we don't.
+ */
+ outw(STOP, DEPCA_DATA);
+
+ /*
+ ** Give back the ROM in case the user wants to go to DOS
+ */
+ if (lp->adapter != DEPCA) {
+ nicsr = inb(DEPCA_NICSR);
+ nicsr &= ~SHE;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ /*
+ ** Free the associated irq
+ */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static void LoadCSRs(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+
+ outw(CSR1, DEPCA_ADDR); /* initialisation block address LSW */
+ outw((u16)(lp->sh_mem & LA_MASK), DEPCA_DATA);
+ outw(CSR2, DEPCA_ADDR); /* initialisation block address MSW */
+ outw((u16)((lp->sh_mem & LA_MASK) >> 16), DEPCA_DATA);
+ outw(CSR3, DEPCA_ADDR); /* ALE control */
+ outw(ACON, DEPCA_DATA);
+
+ outw(CSR0, DEPCA_ADDR); /* Point back to CSR0 */
+
+ return;
+}
+
+static int InitRestartDepca(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ int i, status=0;
+
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *)lp->sh_mem, &lp->init_block, sizeof(struct depca_init));
+
+ outw(CSR0, DEPCA_ADDR); /* point back to CSR0 */
+ outw(INIT, DEPCA_DATA); /* initialize DEPCA */
+
+ /* wait for lance to complete initialisation */
+ for (i=0;(i<100) && !(inw(DEPCA_DATA) & IDON); i++);
+
+ if (i!=100) {
+ /* clear IDON by writing a "1", enable interrupts and start lance */
+ outw(IDON | INEA | STRT, DEPCA_DATA);
+ if (depca_debug > 2) {
+ printk("%s: DEPCA open after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
+ dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+ }
+ } else {
+ printk("%s: DEPCA unopen after %d ticks, init block 0x%08lx csr0 %4.4x.\n",
+ dev->name, i, lp->sh_mem, inw(DEPCA_DATA));
+ status = -1;
+ }
+
+ return status;
+}
+
+static struct enet_statistics *
+depca_get_stats(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+
+ /* Null body since there is no framing error counter */
+
+ return &lp->stats;
+}
+
+/*
+** Set or clear the multicast filter for this adaptor.
+*/
+static void
+set_multicast_list(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+
+ if (irq2dev_map[dev->irq] != NULL) {
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+
+ if (dev->flags & IFF_PROMISC) { /* Set promiscuous mode */
+ lp->init_block.mode |= PROM;
+ } else {
+ SetMulticastFilter(dev);
+ lp->init_block.mode &= ~PROM; /* Unset promiscuous mode */
+ }
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ }
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Big endian crc one liner is mine, all mine, ha ha ha ha!
+** LANCE calculates its hash codes big endian.
+*/
+static void SetMulticastFilter(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ char *addrs;
+ int i, j, bit, byte;
+ u16 hashcode;
+ s32 crc, poly = CRC_POLYNOMIAL_BE;
+
+ if (dev->flags & IFF_ALLMULTI) { /* Set all multicast bits */
+ for (i=0; i<(HASH_TABLE_LEN>>3); i++) {
+ lp->init_block.mcast_table[i] = (char)0xff;
+ }
+ } else {
+ for (i=0; i<(HASH_TABLE_LEN>>3); i++){ /* Clear the multicast table */
+ lp->init_block.mcast_table[i]=0;
+ }
+ /* Add multicast addresses */
+ for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) {/* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc << 1) ^ ((((crc<0?1:0) ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = (crc & 1); /* hashcode is 6 LSb of CRC ... */
+ for (j=0;j<5;j++) { /* ... in reverse order. */
+ hashcode = (hashcode << 1) | ((crc>>=1) & 1);
+ }
+
+
+ byte = hashcode >> 3; /* bit[3-5] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+ lp->init_block.mcast_table[byte] |= bit;
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** ISA bus I/O device probe
+*/
+static void isa_probe(struct device *dev, u_long ioaddr)
+{
+ int i = num_depcas, maxSlots;
+ s32 ports[] = DEPCA_IO_PORTS;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if (ioaddr > 0x400) return; /* EISA Address */
+ if (i >= MAX_NUM_DEPCAS) return; /* Too many ISA adapters */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ maxSlots = MAX_NUM_DEPCAS;
+ } else { /* Probe a specific location */
+ ports[i] = ioaddr;
+ maxSlots = i + 1;
+ }
+
+ for (; (i<maxSlots) && (dev!=NULL) && ports[i]; i++) {
+ if (DevicePresent(ports[i]) == 0) {
+ if (check_region(ports[i], DEPCA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, ports[i])) != NULL) {
+ if (depca_hw_init(dev, ports[i]) == 0) {
+ num_depcas++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04x.\n", dev->name,ports[i]);
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard. Upto 15 EISA devices are supported.
+*/
+static void eisa_probe(struct device *dev, u_long ioaddr)
+{
+ int i, maxSlots;
+ u_long iobase;
+ char name[DEPCA_STRLEN];
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if ((ioaddr < 0x400) && (ioaddr > 0)) return; /* ISA Address */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+ if ((iobase & 0x0fff) == 0) iobase += DEPCA_EISA_IO_PORTS;
+
+ for (; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID)) {
+ if (DevicePresent(iobase) == 0) {
+ if (check_region(iobase, DEPCA_TOTAL_SIZE) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (depca_hw_init(dev, iobase) == 0) {
+ num_depcas++;
+ }
+ num_eth++;
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n",dev->name,iobase);
+ }
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == DEPCA_NDA) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current information.
+ */
+ if ((dev->base_addr == 0) && (num_depcas > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == DEPCA_NDA) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &depca_probe; /* initialisation routine */
+ num_depcas++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (;(tmp->next!=NULL) && (tmp->base_addr!=DEPCA_NDA); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == DEPCA_NDA)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Look for a particular board name in the on-board Remote Diagnostics
+** and Boot (readb) ROM. This will also give us a clue to the network RAM
+** base address.
+*/
+static void DepcaSignature(char *name, u_long paddr)
+{
+ u_int i,j,k;
+ const char *signatures[] = DEPCA_SIGNATURE;
+ char tmpstr[16];
+
+ for (i=0;i<16;i++) { /* copy the first 16 bytes of ROM to */
+ tmpstr[i] = readb(paddr+0xc000+i); /* a temporary string */
+ }
+
+ strcpy(name,"");
+ for (i=0;*signatures[i]!='\0' && *name=='\0';i++) {
+ for (j=0,k=0;j<16 && k<strlen(signatures[i]);j++) {
+ if (signatures[i][k] == tmpstr[j]) { /* track signature */
+ k++;
+ } else { /* lost signature; begin search again */
+ k=0;
+ }
+ }
+ if (k == strlen(signatures[i])) {
+ strcpy(name,signatures[i]);
+ }
+ }
+
+ adapter = i - 1;
+
+ return;
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all DEPCA products. Note that the original DEPCA needs
+** its ROM address counter to be initialized and enabled. Only enable
+** if the first address octet is a 0x08 - this minimises the chances of
+** messing around with some other hardware, but it assumes that this DEPCA
+** card initialized itself correctly.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+static int DevicePresent(u_long ioaddr)
+{
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ } dev;
+ short sigLength=0;
+ s8 data;
+ s16 nicsr;
+ int i, j, status = 0;
+
+ data = inb(DEPCA_PROM); /* clear counter on DEPCA */
+ data = inb(DEPCA_PROM); /* read data */
+
+ if (data == 0x08) { /* Enable counter on DEPCA */
+ nicsr = inb(DEPCA_NICSR);
+ nicsr |= AAC;
+ outb(nicsr, DEPCA_NICSR);
+ }
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+ data = inb(DEPCA_PROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) { /* rare case.... */
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ return status;
+}
+
+/*
+** The DE100 and DE101 PROM accesses were made non-standard for some bizarre
+** reason: access the upper half of the PROM with x=0; access the lower half
+** with x=1.
+*/
+static int get_hw_addr(struct device *dev)
+{
+ u_long ioaddr = dev->base_addr;
+ int i, k, tmp, status = 0;
+ u_short j, x, chksum;
+
+ x = (((adapter == de100) || (adapter == de101)) ? 1 : 0);
+
+ for (i=0,k=0,j=0;j<3;j++) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ k += (u_char) (tmp = inb(DEPCA_PROM + x));
+ dev->dev_addr[i++] = (u_char) tmp;
+ k += (u_short) ((tmp = inb(DEPCA_PROM + x)) << 8);
+ dev->dev_addr[i++] = (u_char) tmp;
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+
+ chksum = (u_char) inb(DEPCA_PROM + x);
+ chksum |= (u_short) (inb(DEPCA_PROM + x) << 8);
+ if (k != chksum) status = -1;
+
+ return status;
+}
+
+/*
+** Load a packet into the shared memory
+*/
+static int load_packet(struct device *dev, struct sk_buff *skb)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ int i, entry, end, len, status = 0;
+
+ entry = lp->tx_new; /* Ring around buffer number. */
+ end = (entry + (skb->len - 1) / TX_BUFF_SZ) & lp->txRingMask;
+ if (!(readl(&lp->tx_ring[end].base) & T_OWN)) {/* Enough room? */
+ /*
+ ** Caution: the write order is important here... don't set up the
+ ** ownership rights until all the other information is in place.
+ */
+ if (end < entry) { /* wrapped buffer */
+ len = (lp->txRingMask - entry + 1) * TX_BUFF_SZ;
+ memcpy_toio(lp->tx_memcpy[entry], skb->data, len);
+ memcpy_toio(lp->tx_memcpy[0], skb->data + len, skb->len - len);
+ } else { /* linear buffer */
+ memcpy_toio(lp->tx_memcpy[entry], skb->data, skb->len);
+ }
+
+ /* set up the buffer descriptors */
+ len = (skb->len < ETH_ZLEN) ? ETH_ZLEN : skb->len;
+ for (i = entry; i != end; i = (++i) & lp->txRingMask) {
+ /* clean out flags */
+ writel(readl(&lp->tx_ring[i].base) & ~T_FLAGS, &lp->tx_ring[i].base);
+ writew(0x0000, &lp->tx_ring[i].misc); /* clears other error flags */
+ writew(-TX_BUFF_SZ, &lp->tx_ring[i].length);/* packet length in buffer */
+ len -= TX_BUFF_SZ;
+ }
+ /* clean out flags */
+ writel(readl(&lp->tx_ring[end].base) & ~T_FLAGS, &lp->tx_ring[end].base);
+ writew(0x0000, &lp->tx_ring[end].misc); /* clears other error flags */
+ writew(-len, &lp->tx_ring[end].length); /* packet length in last buff */
+
+ /* start of packet */
+ writel(readl(&lp->tx_ring[entry].base) | T_STP, &lp->tx_ring[entry].base);
+ /* end of packet */
+ writel(readl(&lp->tx_ring[end].base) | T_ENP, &lp->tx_ring[end].base);
+
+ for (i=end; i!=entry; --i) {
+ /* ownership of packet */
+ writel(readl(&lp->tx_ring[i].base) | T_OWN, &lp->tx_ring[i].base);
+ if (i == 0) i=lp->txRingMask+1;
+ }
+ writel(readl(&lp->tx_ring[entry].base) | T_OWN, &lp->tx_ring[entry].base);
+
+ lp->tx_new = (++end) & lp->txRingMask; /* update current pointers */
+ } else {
+ status = -1;
+ }
+
+ return status;
+}
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(char *name, s32 eisa_id)
+{
+ u_int i;
+ const char *signatures[] = DEPCA_SIGNATURE;
+ char ManCode[DEPCA_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ Eisa.ID = inl(eisa_id);
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=(( Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status;
+}
+
+static void depca_dbg_open(struct device *dev)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ u_long ioaddr = dev->base_addr;
+ struct depca_init *p = (struct depca_init *)lp->sh_mem;
+ int i;
+
+ if (depca_debug > 1){
+ /* Copy the shadow init_block to shared memory */
+ memcpy_toio((char *)lp->sh_mem,&lp->init_block,sizeof(struct depca_init));
+
+ printk("%s: depca open with irq %d\n",dev->name,dev->irq);
+ printk("Descriptor head addresses:\n");
+ printk("\t0x%lx 0x%lx\n",(u_long)lp->rx_ring, (u_long)lp->tx_ring);
+ printk("Descriptor addresses:\nRX: ");
+ for (i=0;i<lp->rxRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (long) &lp->rx_ring[i].base);
+ }
+ }
+ printk("...0x%8.8lx\n", (long) &lp->rx_ring[i].base);
+ printk("TX: ");
+ for (i=0;i<lp->txRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8lx ", (long) &lp->tx_ring[i].base);
+ }
+ }
+ printk("...0x%8.8lx\n", (long) &lp->tx_ring[i].base);
+ printk("\nDescriptor buffers:\nRX: ");
+ for (i=0;i<lp->rxRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8x ", (u32) readl(&lp->rx_ring[i].base));
+ }
+ }
+ printk("...0x%8.8x\n", (u32) readl(&lp->rx_ring[i].base));
+ printk("TX: ");
+ for (i=0;i<lp->txRingMask;i++){
+ if (i < 3) {
+ printk("0x%8.8x ", (u32) readl(&lp->tx_ring[i].base));
+ }
+ }
+ printk("...0x%8.8x\n", (u32) readl(&lp->tx_ring[i].base));
+ printk("Initialisation block at 0x%8.8lx\n",lp->sh_mem);
+ printk("\tmode: 0x%4.4x\n", (u16) readw(&p->mode));
+ printk("\tphysical address: ");
+ for (i=0;i<ETH_ALEN-1;i++){
+ printk("%2.2x:",(u_char)readb(&p->phys_addr[i]));
+ }
+ printk("%2.2x\n",(u_char)readb(&p->phys_addr[i]));
+ printk("\tmulticast hash table: ");
+ for (i=0;i<(HASH_TABLE_LEN >> 3)-1;i++){
+ printk("%2.2x:",(u_char)readb(&p->mcast_table[i]));
+ }
+ printk("%2.2x\n",(u_char)readb(&p->mcast_table[i]));
+ printk("\trx_ring at: 0x%8.8x\n", (u32) readl(&p->rx_ring));
+ printk("\ttx_ring at: 0x%8.8x\n", (u32) readl(&p->tx_ring));
+ printk("dma_buffs: 0x%8.8lx\n",lp->dma_buffs);
+ printk("Ring size:\nRX: %d Log2(rxRingMask): 0x%8.8x\n",
+ (int)lp->rxRingMask + 1,
+ lp->rx_rlen);
+ printk("TX: %d Log2(txRingMask): 0x%8.8x\n",
+ (int)lp->txRingMask + 1,
+ lp->tx_rlen);
+ outw(CSR2,DEPCA_ADDR);
+ printk("CSR2&1: 0x%4.4x",inw(DEPCA_DATA));
+ outw(CSR1,DEPCA_ADDR);
+ printk("%4.4x\n",inw(DEPCA_DATA));
+ outw(CSR3,DEPCA_ADDR);
+ printk("CSR3: 0x%4.4x\n",inw(DEPCA_DATA));
+ }
+
+ return;
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+** All MCA IOCTLs will not work here and are for testing purposes only.
+*/
+static int depca_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct depca_private *lp = (struct depca_private *)dev->priv;
+ struct depca_ioctl *ioc = (struct depca_ioctl *) &rq->ifr_data;
+ int i, status = 0;
+ u_long ioaddr = dev->base_addr;
+ union {
+ u8 addr[(HASH_TABLE_LEN * ETH_ALEN)];
+ u16 sval[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ u32 lval[(HASH_TABLE_LEN * ETH_ALEN) >> 2];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case DEPCA_GET_HWADDR: /* Get the hardware address */
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case DEPCA_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
+ memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ }
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new);/* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ lp->init_block.mode |= PROM; /* Set promiscuous mode */
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ while(dev->tbusy); /* Stop ring access */
+ set_bit(0, (void*)&dev->tbusy);
+ while(lp->tx_old != lp->tx_new); /* Wait for the ring to empty */
+
+ STOP_DEPCA; /* Temporarily stop the depca. */
+ depca_init_ring(dev); /* Initialize the descriptor rings */
+ lp->init_block.mode &= ~PROM; /* Clear promiscuous mode */
+
+ LoadCSRs(dev); /* Reload CSR3 */
+ InitRestartDepca(dev); /* Resume normal operation. */
+ dev->tbusy = 0; /* Unlock the TX ring */
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case DEPCA_GET_MCA: /* Get the multicast address table */
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, lp->init_block.mcast_table, ioc->len);
+ }
+
+ break;
+ case DEPCA_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_MCA_EN: /* Enable pass all multicast addressing */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_GET_STATS: /* Get the driver statistics */
+ cli();
+ ioc->len = sizeof(lp->pktStats);
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ }
+ sti();
+
+ break;
+ case DEPCA_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case DEPCA_GET_REG: /* Get the DEPCA Registers */
+ i=0;
+ tmp.sval[i++] = inw(DEPCA_NICSR);
+ outw(CSR0, DEPCA_ADDR); /* status register */
+ tmp.sval[i++] = inw(DEPCA_DATA);
+ memcpy(&tmp.sval[i], &lp->init_block, sizeof(struct depca_init));
+ ioc->len = i+sizeof(struct depca_init);
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device thisDepca = {
+ devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x200, 7, /* I/O address, IRQ */
+ 0, 0, 0, NULL, depca_probe };
+
+static int irq=7; /* EDIT THESE LINE FOR YOUR CONFIGURATION */
+static int io=0x200; /* Or use the irq= io= options to insmod */
+
+/* See depca_probe() for autoprobe messages when a module */
+int
+init_module(void)
+{
+ thisDepca.irq=irq;
+ thisDepca.base_addr=io;
+
+ if (register_netdev(&thisDepca) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ release_region(thisDepca.base_addr, DEPCA_TOTAL_SIZE);
+ if (thisDepca.priv) {
+ kfree(thisDepca.priv);
+ thisDepca.priv = NULL;
+ }
+ thisDepca.irq=0;
+
+ unregister_netdev(&thisDepca);
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c depca.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c depca.c"
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/depca.h b/i386/i386at/gpl/linux/net/depca.h
new file mode 100644
index 00000000..012f7399
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/depca.h
@@ -0,0 +1,185 @@
+/*
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 David C. Davies. This software may be used and distributed
+ according to the terms of the GNU Public License, incorporated herein by
+ reference.
+*/
+
+/*
+** I/O addresses. Note that the 2k buffer option is not supported in
+** this driver.
+*/
+#define DEPCA_NICSR ioaddr+0x00 /* Network interface CSR */
+#define DEPCA_RBI ioaddr+0x02 /* RAM buffer index (2k buffer mode) */
+#define DEPCA_DATA ioaddr+0x04 /* LANCE registers' data port */
+#define DEPCA_ADDR ioaddr+0x06 /* LANCE registers' address port */
+#define DEPCA_HBASE ioaddr+0x08 /* EISA high memory base address reg. */
+#define DEPCA_PROM ioaddr+0x0c /* Ethernet address ROM data port */
+#define DEPCA_CNFG ioaddr+0x0c /* EISA Configuration port */
+#define DEPCA_RBSA ioaddr+0x0e /* RAM buffer starting address (2k buff.) */
+
+/*
+** These are LANCE registers addressable through DEPCA_ADDR
+*/
+#define CSR0 0
+#define CSR1 1
+#define CSR2 2
+#define CSR3 3
+
+/*
+** NETWORK INTERFACE CSR (NI_CSR) bit definitions
+*/
+
+#define TO 0x0100 /* Time Out for remote boot */
+#define SHE 0x0080 /* SHadow memory Enable */
+#define BS 0x0040 /* Bank Select */
+#define BUF 0x0020 /* BUFfer size (1->32k, 0->64k) */
+#define RBE 0x0010 /* Remote Boot Enable (1->net boot) */
+#define AAC 0x0008 /* Address ROM Address Counter (1->enable) */
+#define _128KB 0x0008 /* 128kB Network RAM (1->enable) */
+#define IM 0x0004 /* Interrupt Mask (1->mask) */
+#define IEN 0x0002 /* Interrupt tristate ENable (1->enable) */
+#define LED 0x0001 /* LED control */
+
+/*
+** Control and Status Register 0 (CSR0) bit definitions
+*/
+
+#define ERR 0x8000 /* Error summary */
+#define BABL 0x4000 /* Babble transmitter timeout error */
+#define CERR 0x2000 /* Collision Error */
+#define MISS 0x1000 /* Missed packet */
+#define MERR 0x0800 /* Memory Error */
+#define RINT 0x0400 /* Receiver Interrupt */
+#define TINT 0x0200 /* Transmit Interrupt */
+#define IDON 0x0100 /* Initialization Done */
+#define INTR 0x0080 /* Interrupt Flag */
+#define INEA 0x0040 /* Interrupt Enable */
+#define RXON 0x0020 /* Receiver on */
+#define TXON 0x0010 /* Transmitter on */
+#define TDMD 0x0008 /* Transmit Demand */
+#define STOP 0x0004 /* Stop */
+#define STRT 0x0002 /* Start */
+#define INIT 0x0001 /* Initialize */
+#define INTM 0xff00 /* Interrupt Mask */
+#define INTE 0xfff0 /* Interrupt Enable */
+
+/*
+** CONTROL AND STATUS REGISTER 3 (CSR3)
+*/
+
+#define BSWP 0x0004 /* Byte SWaP */
+#define ACON 0x0002 /* ALE control */
+#define BCON 0x0001 /* Byte CONtrol */
+
+/*
+** Initialization Block Mode Register
+*/
+
+#define PROM 0x8000 /* Promiscuous Mode */
+#define EMBA 0x0080 /* Enable Modified Back-off Algorithm */
+#define INTL 0x0040 /* Internal Loopback */
+#define DRTY 0x0020 /* Disable Retry */
+#define COLL 0x0010 /* Force Collision */
+#define DTCR 0x0008 /* Disable Transmit CRC */
+#define LOOP 0x0004 /* Loopback */
+#define DTX 0x0002 /* Disable the Transmitter */
+#define DRX 0x0001 /* Disable the Receiver */
+
+/*
+** Receive Message Descriptor 1 (RMD1) bit definitions.
+*/
+
+#define R_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define R_ERR 0x4000 /* Error Summary */
+#define R_FRAM 0x2000 /* Framing Error */
+#define R_OFLO 0x1000 /* Overflow Error */
+#define R_CRC 0x0800 /* CRC Error */
+#define R_BUFF 0x0400 /* Buffer Error */
+#define R_STP 0x0200 /* Start of Packet */
+#define R_ENP 0x0100 /* End of Packet */
+
+/*
+** Transmit Message Descriptor 1 (TMD1) bit definitions.
+*/
+
+#define T_OWN 0x80000000 /* Owner bit 0 = host, 1 = lance */
+#define T_ERR 0x4000 /* Error Summary */
+#define T_ADD_FCS 0x2000 /* More the 1 retry needed to Xmit */
+#define T_MORE 0x1000 /* >1 retry to transmit packet */
+#define T_ONE 0x0800 /* 1 try needed to transmit the packet */
+#define T_DEF 0x0400 /* Deferred */
+#define T_STP 0x02000000 /* Start of Packet */
+#define T_ENP 0x01000000 /* End of Packet */
+#define T_FLAGS 0xff000000 /* TX Flags Field */
+
+/*
+** Transmit Message Descriptor 3 (TMD3) bit definitions.
+*/
+
+#define TMD3_BUFF 0x8000 /* BUFFer error */
+#define TMD3_UFLO 0x4000 /* UnderFLOw error */
+#define TMD3_RES 0x2000 /* REServed */
+#define TMD3_LCOL 0x1000 /* Late COLlision */
+#define TMD3_LCAR 0x0800 /* Loss of CARrier */
+#define TMD3_RTRY 0x0400 /* ReTRY error */
+
+/*
+** EISA configuration Register (CNFG) bit definitions
+*/
+
+#define TIMEOUT 0x0100 /* 0:2.5 mins, 1: 30 secs */
+#define REMOTE 0x0080 /* Remote Boot Enable -> 1 */
+#define IRQ11 0x0040 /* Enable -> 1 */
+#define IRQ10 0x0020 /* Enable -> 1 */
+#define IRQ9 0x0010 /* Enable -> 1 */
+#define IRQ5 0x0008 /* Enable -> 1 */
+#define BUFF 0x0004 /* 0: 64kB or 128kB, 1: 32kB */
+#define PADR16 0x0002 /* RAM on 64kB boundary */
+#define PADR17 0x0001 /* RAM on 128kB boundary */
+
+/*
+** Miscellaneous
+*/
+#define HASH_TABLE_LEN 64 /* Bits */
+#define HASH_BITS 0x003f /* 6 LS bits */
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define EISA_EN 0x0001 /* Enable EISA bus buffers */
+#define EISA_ID iobase+0x0080 /* ID long word for EISA card */
+#define EISA_CTRL iobase+0x0084 /* Control word for EISA card */
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define DEPCAIOCTL SIOCDEVPRIVATE
+
+struct depca_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define DEPCA_GET_HWADDR 0x01 /* Get the hardware address */
+#define DEPCA_SET_HWADDR 0x02 /* Get the hardware address */
+#define DEPCA_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define DEPCA_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define DEPCA_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define DEPCA_GET_MCA 0x06 /* Get a multicast address */
+#define DEPCA_SET_MCA 0x07 /* Set a multicast address */
+#define DEPCA_CLR_MCA 0x08 /* Clear a multicast address */
+#define DEPCA_MCA_EN 0x09 /* Enable a multicast address group */
+#define DEPCA_GET_STATS 0x0a /* Get the driver statistics */
+#define DEPCA_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define DEPCA_GET_REG 0x0c /* Get the Register contents */
+#define DEPCA_SET_REG 0x0d /* Set the Register contents */
+#define DEPCA_DUMP 0x0f /* Dump the DEPCA Status */
+
diff --git a/i386/i386at/gpl/linux/net/dev.c b/i386/i386at/gpl/linux/net/dev.c
new file mode 100644
index 00000000..69d576ff
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/dev.c
@@ -0,0 +1,1413 @@
+/*
+ * NET3 Protocol independent device support routines.
+ *
+ * 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.
+ *
+ * Derived from the non IP parts of dev.c 1.0.19
+ * Authors: Ross Biro, <bir7@leland.Stanford.Edu>
+ * Fred N. van Kempen, <waltje@uWalt.NL.Mugnet.ORG>
+ * Mark Evans, <evansmp@uhura.aston.ac.uk>
+ *
+ * Additional Authors:
+ * Florian la Roche <rzsfl@rz.uni-sb.de>
+ * Alan Cox <gw4pts@gw4pts.ampr.org>
+ * David Hinds <dhinds@allegro.stanford.edu>
+ *
+ * Changes:
+ * Alan Cox : device private ioctl copies fields back.
+ * Alan Cox : Transmit queue code does relevant stunts to
+ * keep the queue safe.
+ * Alan Cox : Fixed double lock.
+ * Alan Cox : Fixed promisc NULL pointer trap
+ * ???????? : Support the full private ioctl range
+ * Alan Cox : Moved ioctl permission check into drivers
+ * Tim Kordas : SIOCADDMULTI/SIOCDELMULTI
+ * Alan Cox : 100 backlog just doesn't cut it when
+ * you start doing multicast video 8)
+ * Alan Cox : Rewrote net_bh and list manager.
+ * Alan Cox : Fix ETH_P_ALL echoback lengths.
+ * Alan Cox : Took out transmit every packet pass
+ * Saved a few bytes in the ioctl handler
+ * Alan Cox : Network driver sets packet type before calling netif_rx. Saves
+ * a function call a packet.
+ * Alan Cox : Hashed net_bh()
+ * Richard Kooijman: Timestamp fixes.
+ * Alan Cox : Wrong field in SIOCGIFDSTADDR
+ * Alan Cox : Device lock protection.
+ * Alan Cox : Fixed nasty side effect of device close changes.
+ * Rudi Cilibrasi : Pass the right thing to set_mac_address()
+ * Dave Miller : 32bit quantity for the device lock to make it work out
+ * on a Sparc.
+ * Bjorn Ekwall : Added KERNELD hack.
+ * Alan Cox : Cleaned up the backlog initialise.
+ * Craig Metz : SIOCGIFCONF fix if space for under
+ * 1 device.
+ *
+ */
+
+#include <asm/segment.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/socket.h>
+#include <linux/sockios.h>
+#include <linux/in.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/if_ether.h>
+#include <linux/inet.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/notifier.h>
+#include <net/ip.h>
+#include <net/route.h>
+#include <linux/skbuff.h>
+#include <net/sock.h>
+#include <net/arp.h>
+#include <net/slhc.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
+#ifdef CONFIG_KERNELD
+#include <linux/kerneld.h>
+#endif
+
+#ifndef MACH
+/*
+ * The list of packet types we will receive (as opposed to discard)
+ * and the routines to invoke.
+ */
+
+struct packet_type *ptype_base[16];
+struct packet_type *ptype_all = NULL; /* Taps */
+
+/*
+ * Device list lock
+ */
+
+int dev_lockct=0;
+
+/*
+ * Our notifier list
+ */
+
+struct notifier_block *netdev_chain=NULL;
+
+/*
+ * Device drivers call our routines to queue packets here. We empty the
+ * queue in the bottom half handler.
+ */
+
+static struct sk_buff_head backlog;
+
+/*
+ * We don't overdo the queue or we will thrash memory badly.
+ */
+
+static int backlog_size = 0;
+
+/*
+ * Return the lesser of the two values.
+ */
+
+static __inline__ unsigned long min(unsigned long a, unsigned long b)
+{
+ return (a < b)? a : b;
+}
+
+
+/******************************************************************************************
+
+ Protocol management and registration routines
+
+*******************************************************************************************/
+
+/*
+ * For efficiency
+ */
+
+static int dev_nit=0;
+
+/*
+ * Add a protocol ID to the list. Now that the input handler is
+ * smarter we can dispense with all the messy stuff that used to be
+ * here.
+ */
+
+void dev_add_pack(struct packet_type *pt)
+{
+ int hash;
+ if(pt->type==htons(ETH_P_ALL))
+ {
+ dev_nit++;
+ pt->next=ptype_all;
+ ptype_all=pt;
+ }
+ else
+ {
+ hash=ntohs(pt->type)&15;
+ pt->next = ptype_base[hash];
+ ptype_base[hash] = pt;
+ }
+}
+
+
+/*
+ * Remove a protocol ID from the list.
+ */
+
+void dev_remove_pack(struct packet_type *pt)
+{
+ struct packet_type **pt1;
+ if(pt->type==htons(ETH_P_ALL))
+ {
+ dev_nit--;
+ pt1=&ptype_all;
+ }
+ else
+ pt1=&ptype_base[ntohs(pt->type)&15];
+ for(; (*pt1)!=NULL; pt1=&((*pt1)->next))
+ {
+ if(pt==(*pt1))
+ {
+ *pt1=pt->next;
+ return;
+ }
+ }
+ printk("dev_remove_pack: %p not found.\n", pt);
+}
+
+/*****************************************************************************************
+
+ Device Interface Subroutines
+
+******************************************************************************************/
+
+/*
+ * Find an interface by name.
+ */
+
+struct device *dev_get(const char *name)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (strcmp(dev->name, name) == 0)
+ return(dev);
+ }
+ return NULL;
+}
+
+/*
+ * Find and possibly load an interface.
+ */
+
+#ifdef CONFIG_KERNELD
+
+extern __inline__ void dev_load(const char *name)
+{
+ char *sptr;
+
+ if(!dev_get(name)) {
+#ifdef CONFIG_NET_ALIAS
+ for (sptr=name ; *sptr ; sptr++) if(*sptr==':') break;
+ if (!(*sptr && *(sptr+1)))
+#endif
+ request_module(name);
+ }
+}
+
+#endif
+
+/*
+ * Prepare an interface for use.
+ */
+
+int dev_open(struct device *dev)
+{
+ int ret = 0;
+
+ /*
+ * Call device private open method
+ */
+ if (dev->open)
+ ret = dev->open(dev);
+
+ /*
+ * If it went open OK then set the flags
+ */
+
+ if (ret == 0)
+ {
+ dev->flags |= (IFF_UP | IFF_RUNNING);
+ /*
+ * Initialise multicasting status
+ */
+ dev_mc_upload(dev);
+ notifier_call_chain(&netdev_chain, NETDEV_UP, dev);
+ }
+ return(ret);
+}
+
+
+/*
+ * Completely shutdown an interface.
+ */
+
+int dev_close(struct device *dev)
+{
+ int ct=0;
+
+ /*
+ * Call the device specific close. This cannot fail.
+ * Only if device is UP
+ */
+
+ if ((dev->flags & IFF_UP) && dev->stop)
+ dev->stop(dev);
+
+ /*
+ * Device is now down.
+ */
+
+ dev->flags&=~(IFF_UP|IFF_RUNNING);
+
+ /*
+ * Tell people we are going down
+ */
+ notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev);
+ /*
+ * Flush the multicast chain
+ */
+ dev_mc_discard(dev);
+ /*
+ * Blank the IP addresses
+ */
+ dev->pa_addr = 0;
+ dev->pa_dstaddr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ /*
+ * Purge any queued packets when we down the link
+ */
+ while(ct<DEV_NUMBUFFS)
+ {
+ struct sk_buff *skb;
+ while((skb=skb_dequeue(&dev->buffs[ct]))!=NULL)
+ if(skb->free)
+ kfree_skb(skb,FREE_WRITE);
+ ct++;
+ }
+ return(0);
+}
+
+
+/*
+ * Device change register/unregister. These are not inline or static
+ * as we export them to the world.
+ */
+
+int register_netdevice_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_register(&netdev_chain, nb);
+}
+
+int unregister_netdevice_notifier(struct notifier_block *nb)
+{
+ return notifier_chain_unregister(&netdev_chain,nb);
+}
+
+/*
+ * Send (or queue for sending) a packet.
+ *
+ * IMPORTANT: When this is called to resend frames. The caller MUST
+ * already have locked the sk_buff. Apart from that we do the
+ * rest of the magic.
+ */
+
+void dev_queue_xmit(struct sk_buff *skb, struct device *dev, int pri)
+{
+ unsigned long flags;
+ struct sk_buff_head *list;
+ int retransmission = 0; /* used to say if the packet should go */
+ /* at the front or the back of the */
+ /* queue - front is a retransmit try */
+
+ if(pri>=0 && !skb_device_locked(skb))
+ skb_device_lock(skb); /* Shove a lock on the frame */
+#if CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ skb->dev = dev;
+
+ /*
+ * Negative priority is used to flag a frame that is being pulled from the
+ * queue front as a retransmit attempt. It therefore goes back on the queue
+ * start on a failure.
+ */
+
+ if (pri < 0)
+ {
+ pri = -pri-1;
+ retransmission = 1;
+ }
+
+#ifdef CONFIG_NET_DEBUG
+ if (pri >= DEV_NUMBUFFS)
+ {
+ printk("bad priority in dev_queue_xmit.\n");
+ pri = 1;
+ }
+#endif
+
+ /*
+ * If the address has not been resolved. Call the device header rebuilder.
+ * This can cover all protocols and technically not just ARP either.
+ */
+
+ if (!skb->arp && dev->rebuild_header(skb->data, dev, skb->raddr, skb)) {
+ return;
+ }
+
+ /*
+ *
+ * If dev is an alias, switch to its main device.
+ * "arp" resolution has been made with alias device, so
+ * arp entries refer to alias, not main.
+ *
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev))
+ skb->dev = dev = net_alias_main_dev(dev);
+#endif
+ list = dev->buffs + pri;
+
+ save_flags(flags);
+ /* if this isn't a retransmission, use the first packet instead... */
+ if (!retransmission) {
+ if (skb_queue_len(list)) {
+ /* avoid overrunning the device queue.. */
+ if (skb_queue_len(list) > dev->tx_queue_len) {
+ dev_kfree_skb(skb, FREE_WRITE);
+ return;
+ }
+ cli();
+ skb_device_unlock(skb); /* Buffer is on the device queue and can be freed safely */
+ __skb_queue_tail(list, skb);
+ skb = __skb_dequeue(list);
+ skb_device_lock(skb); /* New buffer needs locking down */
+ restore_flags(flags);
+ }
+
+ /* copy outgoing packets to any sniffer packet handlers */
+ if (dev_nit) {
+ struct packet_type *ptype;
+ skb->stamp=xtime;
+ for (ptype = ptype_all; ptype!=NULL; ptype = ptype->next)
+ {
+ /* Never send packets back to the socket
+ * they originated from - MvS (miquels@drinkel.ow.org)
+ */
+ if ((ptype->dev == dev || !ptype->dev) &&
+ ((struct sock *)ptype->data != skb->sk))
+ {
+ struct sk_buff *skb2;
+ if ((skb2 = skb_clone(skb, GFP_ATOMIC)) == NULL)
+ break;
+ skb2->h.raw = skb2->data + dev->hard_header_len;
+ skb2->mac.raw = skb2->data;
+ ptype->func(skb2, skb->dev, ptype);
+ }
+ }
+ }
+ }
+ start_bh_atomic();
+ if (dev->hard_start_xmit(skb, dev) == 0) {
+ /*
+ * Packet is now solely the responsibility of the driver
+ */
+ end_bh_atomic();
+ return;
+ }
+ end_bh_atomic();
+
+ /*
+ * Transmission failed, put skb back into a list. Once on the list it's safe and
+ * no longer device locked (it can be freed safely from the device queue)
+ */
+ cli();
+ skb_device_unlock(skb);
+ __skb_queue_head(list,skb);
+ restore_flags(flags);
+}
+
+/*
+ * Receive a packet from a device driver and queue it for the upper
+ * (protocol) levels. It always succeeds. This is the recommended
+ * interface to use.
+ */
+
+void netif_rx(struct sk_buff *skb)
+{
+ static int dropping = 0;
+
+ /*
+ * Any received buffers are un-owned and should be discarded
+ * when freed. These will be updated later as the frames get
+ * owners.
+ */
+
+ skb->sk = NULL;
+ skb->free = 1;
+ if(skb->stamp.tv_sec==0)
+ skb->stamp = xtime;
+
+ /*
+ * Check that we aren't overdoing things.
+ */
+
+ if (!backlog_size)
+ dropping = 0;
+ else if (backlog_size > 300)
+ dropping = 1;
+
+ if (dropping)
+ {
+ kfree_skb(skb, FREE_READ);
+ return;
+ }
+
+ /*
+ * Add it to the "backlog" queue.
+ */
+#if CONFIG_SKB_CHECK
+ IS_SKB(skb);
+#endif
+ skb_queue_tail(&backlog,skb);
+ backlog_size++;
+
+ /*
+ * If any packet arrived, mark it for processing after the
+ * hardware interrupt returns.
+ */
+
+#ifdef CONFIG_NET_RUNONIRQ /* Dont enable yet, needs some driver mods */
+ net_bh();
+#else
+ mark_bh(NET_BH);
+#endif
+ return;
+}
+
+/*
+ * This routine causes all interfaces to try to send some data.
+ */
+
+void dev_transmit(void)
+{
+ struct device *dev;
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if (dev->flags != 0 && !dev->tbusy) {
+ /*
+ * Kick the device
+ */
+ dev_tint(dev);
+ }
+ }
+}
+
+
+/**********************************************************************************
+
+ Receive Queue Processor
+
+***********************************************************************************/
+
+/*
+ * This is a single non-reentrant routine which takes the received packet
+ * queue and throws it at the networking layers in the hope that something
+ * useful will emerge.
+ */
+
+volatile unsigned long in_bh = 0; /* Non-reentrant remember */
+
+int in_net_bh() /* Used by timer.c */
+{
+ return(in_bh==0?0:1);
+}
+
+/*
+ * When we are called the queue is ready to grab, the interrupts are
+ * on and hardware can interrupt and queue to the receive queue a we
+ * run with no problems.
+ * This is run as a bottom half after an interrupt handler that does
+ * mark_bh(NET_BH);
+ */
+
+void net_bh(void *tmp)
+{
+ struct sk_buff *skb;
+ struct packet_type *ptype;
+ struct packet_type *pt_prev;
+ unsigned short type;
+
+ /*
+ * Atomically check and mark our BUSY state.
+ */
+
+ if (set_bit(1, (void*)&in_bh))
+ return;
+
+ /*
+ * Can we send anything now? We want to clear the
+ * decks for any more sends that get done as we
+ * process the input. This also minimises the
+ * latency on a transmit interrupt bh.
+ */
+
+ dev_transmit();
+
+ /*
+ * Any data left to process. This may occur because a
+ * mark_bh() is done after we empty the queue including
+ * that from the device which does a mark_bh() just after
+ */
+
+ cli();
+
+ /*
+ * While the queue is not empty
+ */
+
+ while((skb=__skb_dequeue(&backlog))!=NULL)
+ {
+ /*
+ * We have a packet. Therefore the queue has shrunk
+ */
+ backlog_size--;
+
+ sti();
+
+ /*
+ * Bump the pointer to the next structure.
+ *
+ * On entry to the protocol layer. skb->data and
+ * skb->h.raw point to the MAC and encapsulated data
+ */
+
+ skb->h.raw = skb->data;
+
+ /*
+ * Fetch the packet protocol ID.
+ */
+
+ type = skb->protocol;
+
+ /*
+ * We got a packet ID. Now loop over the "known protocols"
+ * list. There are two lists. The ptype_all list of taps (normally empty)
+ * and the main protocol list which is hashed perfectly for normal protocols.
+ */
+ pt_prev = NULL;
+ for (ptype = ptype_all; ptype!=NULL; ptype=ptype->next)
+ {
+ if(pt_prev)
+ {
+ struct sk_buff *skb2=skb_clone(skb, GFP_ATOMIC);
+ if(skb2)
+ pt_prev->func(skb2,skb->dev, pt_prev);
+ }
+ pt_prev=ptype;
+ }
+
+ for (ptype = ptype_base[ntohs(type)&15]; ptype != NULL; ptype = ptype->next)
+ {
+ if (ptype->type == type && (!ptype->dev || ptype->dev==skb->dev))
+ {
+ /*
+ * We already have a match queued. Deliver
+ * to it and then remember the new match
+ */
+ if(pt_prev)
+ {
+ struct sk_buff *skb2;
+
+ skb2=skb_clone(skb, GFP_ATOMIC);
+
+ /*
+ * Kick the protocol handler. This should be fast
+ * and efficient code.
+ */
+
+ if(skb2)
+ pt_prev->func(skb2, skb->dev, pt_prev);
+ }
+ /* Remember the current last to do */
+ pt_prev=ptype;
+ }
+ } /* End of protocol list loop */
+
+ /*
+ * Is there a last item to send to ?
+ */
+
+ if(pt_prev)
+ pt_prev->func(skb, skb->dev, pt_prev);
+ /*
+ * Has an unknown packet has been received ?
+ */
+
+ else
+ kfree_skb(skb, FREE_WRITE);
+
+ /*
+ * Again, see if we can transmit anything now.
+ * [Ought to take this out judging by tests it slows
+ * us down not speeds us up]
+ */
+#ifdef XMIT_EVERY
+ dev_transmit();
+#endif
+ cli();
+ } /* End of queue loop */
+
+ /*
+ * We have emptied the queue
+ */
+
+ in_bh = 0;
+ sti();
+
+ /*
+ * One last output flush.
+ */
+
+#ifdef XMIT_AFTER
+ dev_transmit();
+#endif
+}
+
+
+/*
+ * This routine is called when an device driver (i.e. an
+ * interface) is ready to transmit a packet.
+ */
+
+void dev_tint(struct device *dev)
+{
+ int i;
+ unsigned long flags;
+ struct sk_buff_head * head;
+
+ /*
+ * aliases do not transmit (for now :) )
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev)) return;
+#endif
+ head = dev->buffs;
+ save_flags(flags);
+ cli();
+
+ /*
+ * Work the queues in priority order
+ */
+ for(i = 0;i < DEV_NUMBUFFS; i++,head++)
+ {
+ struct sk_buff *skb = skb_peek(head);
+
+ if (skb) {
+ __skb_unlink(skb, head);
+ /*
+ * Stop anyone freeing the buffer while we retransmit it
+ */
+ skb_device_lock(skb);
+ restore_flags(flags);
+ /*
+ * Feed them to the output stage and if it fails
+ * indicate they re-queue at the front.
+ */
+ dev_queue_xmit(skb,dev,-i - 1);
+ /*
+ * If we can take no more then stop here.
+ */
+ if (dev->tbusy)
+ return;
+ cli();
+ }
+ }
+ restore_flags(flags);
+}
+
+
+/*
+ * Perform a SIOCGIFCONF call. This structure will change
+ * size shortly, and there is nothing I can do about it.
+ * Thus we will need a 'compatibility mode'.
+ */
+
+static int dev_ifconf(char *arg)
+{
+ struct ifconf ifc;
+ struct ifreq ifr;
+ struct device *dev;
+ char *pos;
+ int len;
+ int err;
+
+ /*
+ * Fetch the caller's info block.
+ */
+
+ err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifconf));
+ if(err)
+ return err;
+ memcpy_fromfs(&ifc, arg, sizeof(struct ifconf));
+ len = ifc.ifc_len;
+ pos = ifc.ifc_buf;
+
+ /*
+ * We now walk the device list filling each active device
+ * into the array.
+ */
+
+ err=verify_area(VERIFY_WRITE,pos,len);
+ if(err)
+ return err;
+
+ /*
+ * Loop over the interfaces, and write an info block for each.
+ */
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ if(!(dev->flags & IFF_UP)) /* Downed devices don't count */
+ continue;
+ /*
+ * Have we run out of space here ?
+ */
+
+ if (len < sizeof(struct ifreq))
+ break;
+
+ memset(&ifr, 0, sizeof(struct ifreq));
+ strcpy(ifr.ifr_name, dev->name);
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *) &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+
+
+ /*
+ * Write this block to the caller's space.
+ */
+
+ memcpy_tofs(pos, &ifr, sizeof(struct ifreq));
+ pos += sizeof(struct ifreq);
+ len -= sizeof(struct ifreq);
+ }
+
+ /*
+ * All done. Write the updated control block back to the caller.
+ */
+
+ ifc.ifc_len = (pos - ifc.ifc_buf);
+ ifc.ifc_req = (struct ifreq *) ifc.ifc_buf;
+ memcpy_tofs(arg, &ifc, sizeof(struct ifconf));
+
+ /*
+ * Report how much was filled in
+ */
+
+ return(pos - arg);
+}
+
+
+/*
+ * This is invoked by the /proc filesystem handler to display a device
+ * in detail.
+ */
+
+#ifdef CONFIG_PROC_FS
+static int sprintf_stats(char *buffer, struct device *dev)
+{
+ struct enet_statistics *stats = (dev->get_stats ? dev->get_stats(dev): NULL);
+ int size;
+
+ if (stats)
+ size = sprintf(buffer, "%6s:%7d %4d %4d %4d %4d %8d %4d %4d %4d %5d %4d\n",
+ dev->name,
+ stats->rx_packets, stats->rx_errors,
+ stats->rx_dropped + stats->rx_missed_errors,
+ stats->rx_fifo_errors,
+ stats->rx_length_errors + stats->rx_over_errors
+ + stats->rx_crc_errors + stats->rx_frame_errors,
+ stats->tx_packets, stats->tx_errors, stats->tx_dropped,
+ stats->tx_fifo_errors, stats->collisions,
+ stats->tx_carrier_errors + stats->tx_aborted_errors
+ + stats->tx_window_errors + stats->tx_heartbeat_errors);
+ else
+ size = sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+ return size;
+}
+
+/*
+ * Called from the PROCfs module. This now uses the new arbitrary sized /proc/net interface
+ * to create /proc/net/dev
+ */
+
+int dev_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len=0;
+ off_t begin=0;
+ off_t pos=0;
+ int size;
+
+ struct device *dev;
+
+
+ size = sprintf(buffer, "Inter-| Receive | Transmit\n"
+ " face |packets errs drop fifo frame|packets errs drop fifo colls carrier\n");
+
+ pos+=size;
+ len+=size;
+
+
+ for (dev = dev_base; dev != NULL; dev = dev->next)
+ {
+ size = sprintf_stats(buffer+len, dev);
+ len+=size;
+ pos=begin+len;
+
+ if(pos<offset)
+ {
+ len=0;
+ begin=pos;
+ }
+ if(pos>offset+length)
+ break;
+ }
+
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len=length; /* Ending slop */
+ return len;
+}
+#endif /* CONFIG_PROC_FS */
+
+
+/*
+ * This checks bitmasks for the ioctl calls for devices.
+ */
+
+static inline int bad_mask(unsigned long mask, unsigned long addr)
+{
+ if (addr & (mask = ~mask))
+ return 1;
+ mask = ntohl(mask);
+ if (mask & (mask+1))
+ return 1;
+ return 0;
+}
+
+/*
+ * Perform the SIOCxIFxxx calls.
+ *
+ * The socket layer has seen an ioctl the address family thinks is
+ * for the device. At this point we get invoked to make a decision
+ */
+
+static int dev_ifsioc(void *arg, unsigned int getset)
+{
+ struct ifreq ifr;
+ struct device *dev;
+ int ret;
+
+ /*
+ * Fetch the caller's info block into kernel space
+ */
+
+ int err=verify_area(VERIFY_WRITE, arg, sizeof(struct ifreq));
+ if(err)
+ return err;
+
+ memcpy_fromfs(&ifr, arg, sizeof(struct ifreq));
+
+ /*
+ * See which interface the caller is talking about.
+ */
+
+ /*
+ *
+ * net_alias_dev_get(): dev_get() with added alias naming magic.
+ * only allow alias creation/deletion if (getset==SIOCSIFADDR)
+ *
+ */
+
+#ifdef CONFIG_KERNELD
+ dev_load(ifr.ifr_name);
+#endif
+
+#ifdef CONFIG_NET_ALIAS
+ if ((dev = net_alias_dev_get(ifr.ifr_name, getset == SIOCSIFADDR, &err, NULL, NULL)) == NULL)
+ return(err);
+#else
+ if ((dev = dev_get(ifr.ifr_name)) == NULL)
+ return(-ENODEV);
+#endif
+ switch(getset)
+ {
+ case SIOCGIFFLAGS: /* Get interface flags */
+ ifr.ifr_flags = dev->flags;
+ goto rarok;
+
+ case SIOCSIFFLAGS: /* Set interface flags */
+ {
+ int old_flags = dev->flags;
+
+ /*
+ * We are not allowed to potentially close/unload
+ * a device until we get this lock.
+ */
+
+ dev_lock_wait();
+
+ /*
+ * Set the flags on our device.
+ */
+
+ dev->flags = (ifr.ifr_flags & (
+ IFF_BROADCAST | IFF_DEBUG | IFF_LOOPBACK |
+ IFF_POINTOPOINT | IFF_NOTRAILERS | IFF_RUNNING |
+ IFF_NOARP | IFF_PROMISC | IFF_ALLMULTI | IFF_SLAVE | IFF_MASTER
+ | IFF_MULTICAST)) | (dev->flags & IFF_UP);
+ /*
+ * Load in the correct multicast list now the flags have changed.
+ */
+
+ dev_mc_upload(dev);
+
+ /*
+ * Have we downed the interface. We handle IFF_UP ourselves
+ * according to user attempts to set it, rather than blindly
+ * setting it.
+ */
+
+ if ((old_flags^ifr.ifr_flags)&IFF_UP) /* Bit is different ? */
+ {
+ if(old_flags&IFF_UP) /* Gone down */
+ ret=dev_close(dev);
+ else /* Come up */
+ {
+ ret=dev_open(dev);
+ if(ret<0)
+ dev->flags&=~IFF_UP; /* Open failed */
+ }
+ }
+ else
+ ret=0;
+ /*
+ * Load in the correct multicast list now the flags have changed.
+ */
+
+ dev_mc_upload(dev);
+ }
+ break;
+
+ case SIOCGIFADDR: /* Get interface address (and family) */
+ if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+ {
+ memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family=dev->type;
+ goto rarok;
+ }
+ else
+ {
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr = dev->pa_addr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_port = 0;
+ }
+ goto rarok;
+
+ case SIOCSIFADDR: /* Set interface address (and family) */
+
+ /*
+ * BSDism. SIOCSIFADDR family=AF_UNSPEC sets the
+ * physical address. We can cope with this now.
+ */
+
+ if(ifr.ifr_addr.sa_family==AF_UNSPEC)
+ {
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ ret=dev->set_mac_address(dev,&ifr.ifr_addr);
+ }
+ else
+ {
+
+ /*
+ * if dev is an alias, must rehash to update
+ * address change
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ if (net_alias_is(dev))
+ net_alias_dev_rehash(dev ,&ifr.ifr_addr);
+#endif
+ dev->pa_addr = (*(struct sockaddr_in *)
+ &ifr.ifr_addr).sin_addr.s_addr;
+ dev->family = ifr.ifr_addr.sa_family;
+
+#ifdef CONFIG_INET
+ /* This is naughty. When net-032e comes out It wants moving into the net032
+ code not the kernel. Till then it can sit here (SIGH) */
+ dev->pa_mask = ip_get_mask(dev->pa_addr);
+#endif
+ dev->pa_brdaddr = dev->pa_addr | ~dev->pa_mask;
+ ret = 0;
+ }
+ break;
+
+ case SIOCGIFBRDADDR: /* Get the broadcast address */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr = dev->pa_brdaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_port = 0;
+ goto rarok;
+
+ case SIOCSIFBRDADDR: /* Set the broadcast address */
+ dev->pa_brdaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_broadaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+
+ case SIOCGIFDSTADDR: /* Get the destination address (for point-to-point links) */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr = dev->pa_dstaddr;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_port = 0;
+ goto rarok;
+
+ case SIOCSIFDSTADDR: /* Set the destination address (for point-to-point links) */
+ dev->pa_dstaddr = (*(struct sockaddr_in *)
+ &ifr.ifr_dstaddr).sin_addr.s_addr;
+ ret = 0;
+ break;
+
+ case SIOCGIFNETMASK: /* Get the netmask for the interface */
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr = dev->pa_mask;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_family = dev->family;
+ (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_port = 0;
+ goto rarok;
+
+ case SIOCSIFNETMASK: /* Set the netmask for the interface */
+ {
+ unsigned long mask = (*(struct sockaddr_in *)
+ &ifr.ifr_netmask).sin_addr.s_addr;
+ ret = -EINVAL;
+ /*
+ * The mask we set must be legal.
+ */
+ if (bad_mask(mask,0))
+ break;
+ dev->pa_mask = mask;
+ ret = 0;
+ }
+ break;
+
+ case SIOCGIFMETRIC: /* Get the metric on the interface (currently unused) */
+
+ ifr.ifr_metric = dev->metric;
+ goto rarok;
+
+ case SIOCSIFMETRIC: /* Set the metric on the interface (currently unused) */
+ dev->metric = ifr.ifr_metric;
+ ret=0;
+ break;
+
+ case SIOCGIFMTU: /* Get the MTU of a device */
+ ifr.ifr_mtu = dev->mtu;
+ goto rarok;
+
+ case SIOCSIFMTU: /* Set the MTU of a device */
+
+ /*
+ * MTU must be positive.
+ */
+
+ if(ifr.ifr_mtu<68)
+ return -EINVAL;
+ dev->mtu = ifr.ifr_mtu;
+ ret = 0;
+ break;
+
+ case SIOCGIFMEM: /* Get the per device memory space. We can add this but currently
+ do not support it */
+ ret = -EINVAL;
+ break;
+
+ case SIOCSIFMEM: /* Set the per device memory buffer space. Not applicable in our case */
+ ret = -EINVAL;
+ break;
+
+ case SIOCGIFHWADDR:
+ memcpy(ifr.ifr_hwaddr.sa_data,dev->dev_addr, MAX_ADDR_LEN);
+ ifr.ifr_hwaddr.sa_family=dev->type;
+ goto rarok;
+
+ case SIOCSIFHWADDR:
+ if(dev->set_mac_address==NULL)
+ return -EOPNOTSUPP;
+ if(ifr.ifr_hwaddr.sa_family!=dev->type)
+ return -EINVAL;
+ ret=dev->set_mac_address(dev,&ifr.ifr_hwaddr);
+ break;
+
+ case SIOCGIFMAP:
+ ifr.ifr_map.mem_start=dev->mem_start;
+ ifr.ifr_map.mem_end=dev->mem_end;
+ ifr.ifr_map.base_addr=dev->base_addr;
+ ifr.ifr_map.irq=dev->irq;
+ ifr.ifr_map.dma=dev->dma;
+ ifr.ifr_map.port=dev->if_port;
+ goto rarok;
+
+ case SIOCSIFMAP:
+ if(dev->set_config==NULL)
+ return -EOPNOTSUPP;
+ return dev->set_config(dev,&ifr.ifr_map);
+
+ case SIOCADDMULTI:
+ if(dev->set_multicast_list==NULL)
+ return -EINVAL;
+ if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+ return -EINVAL;
+ dev_mc_add(dev,ifr.ifr_hwaddr.sa_data, dev->addr_len, 1);
+ return 0;
+
+ case SIOCDELMULTI:
+ if(dev->set_multicast_list==NULL)
+ return -EINVAL;
+ if(ifr.ifr_hwaddr.sa_family!=AF_UNSPEC)
+ return -EINVAL;
+ dev_mc_delete(dev,ifr.ifr_hwaddr.sa_data,dev->addr_len, 1);
+ return 0;
+ /*
+ * Unknown or private ioctl
+ */
+
+ default:
+ if((getset >= SIOCDEVPRIVATE) &&
+ (getset <= (SIOCDEVPRIVATE + 15))) {
+ if(dev->do_ioctl==NULL)
+ return -EOPNOTSUPP;
+ ret=dev->do_ioctl(dev, &ifr, getset);
+ memcpy_tofs(arg,&ifr,sizeof(struct ifreq));
+ break;
+ }
+
+ ret = -EINVAL;
+ }
+ return(ret);
+/*
+ * The load of calls that return an ifreq and ok (saves memory).
+ */
+rarok:
+ memcpy_tofs(arg, &ifr, sizeof(struct ifreq));
+ return 0;
+}
+
+
+/*
+ * This function handles all "interface"-type I/O control requests. The actual
+ * 'doing' part of this is dev_ifsioc above.
+ */
+
+int dev_ioctl(unsigned int cmd, void *arg)
+{
+ switch(cmd)
+ {
+ case SIOCGIFCONF:
+ (void) dev_ifconf((char *) arg);
+ return 0;
+
+ /*
+ * Ioctl calls that can be done by all.
+ */
+
+ case SIOCGIFFLAGS:
+ case SIOCGIFADDR:
+ case SIOCGIFDSTADDR:
+ case SIOCGIFBRDADDR:
+ case SIOCGIFNETMASK:
+ case SIOCGIFMETRIC:
+ case SIOCGIFMTU:
+ case SIOCGIFMEM:
+ case SIOCGIFHWADDR:
+ case SIOCSIFHWADDR:
+ case SIOCGIFSLAVE:
+ case SIOCGIFMAP:
+ return dev_ifsioc(arg, cmd);
+
+ /*
+ * Ioctl calls requiring the power of a superuser
+ */
+
+ case SIOCSIFFLAGS:
+ case SIOCSIFADDR:
+ case SIOCSIFDSTADDR:
+ case SIOCSIFBRDADDR:
+ case SIOCSIFNETMASK:
+ case SIOCSIFMETRIC:
+ case SIOCSIFMTU:
+ case SIOCSIFMEM:
+ case SIOCSIFMAP:
+ case SIOCSIFSLAVE:
+ case SIOCADDMULTI:
+ case SIOCDELMULTI:
+ if (!suser())
+ return -EPERM;
+ return dev_ifsioc(arg, cmd);
+
+ case SIOCSIFLINK:
+ return -EINVAL;
+
+ /*
+ * Unknown or private ioctl.
+ */
+
+ default:
+ if((cmd >= SIOCDEVPRIVATE) &&
+ (cmd <= (SIOCDEVPRIVATE + 15))) {
+ return dev_ifsioc(arg, cmd);
+ }
+ return -EINVAL;
+ }
+}
+#endif /* ! MACH */
+
+/*
+ * Initialize the DEV module. At boot time this walks the device list and
+ * unhooks any devices that fail to initialise (normally hardware not
+ * present) and leaves us with a valid list of present and active devices.
+ *
+ */
+extern int lance_init(void);
+extern int pi_init(void);
+extern int dec21040_init(void);
+
+int net_dev_init(void)
+{
+ struct device *dev, **dp;
+
+#ifndef MACH
+ /*
+ * Initialise the packet receive queue.
+ */
+
+ skb_queue_head_init(&backlog);
+#endif
+
+ /*
+ * This is VeryUgly(tm).
+ *
+ * Some devices want to be initialized eary..
+ */
+#if defined(CONFIG_LANCE)
+ lance_init();
+#endif
+#if defined(CONFIG_PI)
+ pi_init();
+#endif
+#if defined(CONFIG_PT)
+ pt_init();
+#endif
+#if defined(CONFIG_DEC_ELCP)
+ dec21040_init();
+#endif
+ /*
+ * SLHC if present needs attaching so other people see it
+ * even if not opened.
+ */
+#if (defined(CONFIG_SLIP_COMPRESSED) || defined(CONFIG_PPP)) && defined(CONFIG_SLHC_BUILTIN)
+ slhc_install();
+#endif
+
+ /*
+ * Add the devices.
+ * If the call to dev->init fails, the dev is removed
+ * from the chain disconnecting the device until the
+ * next reboot.
+ */
+
+ dp = &dev_base;
+ while ((dev = *dp) != NULL)
+ {
+ int i;
+ for (i = 0; i < DEV_NUMBUFFS; i++) {
+ skb_queue_head_init(dev->buffs + i);
+ }
+
+ if (dev->init && dev->init(dev))
+ {
+ /*
+ * It failed to come up. Unhook it.
+ */
+ *dp = dev->next;
+ }
+ else
+ {
+ dp = &dev->next;
+ }
+ }
+
+#ifdef CONFIG_PROC_FS
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_DEV, 3, "dev",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ dev_get_info
+ });
+#endif
+
+ /*
+ * Initialise net_alias engine
+ *
+ * - register net_alias device notifier
+ * - register proc entries: /proc/net/alias_types
+ * /proc/net/aliases
+ */
+
+#ifdef CONFIG_NET_ALIAS
+ net_alias_init();
+#endif
+
+ bh_base[NET_BH].routine = net_bh;
+ enable_bh(NET_BH);
+ return 0;
+}
diff --git a/i386/i386at/gpl/linux/net/e2100.c b/i386/i386at/gpl/linux/net/e2100.c
new file mode 100644
index 00000000..fb0f1de6
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/e2100.c
@@ -0,0 +1,456 @@
+/* e2100.c: A Cabletron E2100 series ethernet driver for linux. */
+/*
+ Written 1993-1994 by Donald Becker.
+
+ Copyright 1994 by Donald Becker.
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency. This software may be used and
+ distributed according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ This is a driver for the Cabletron E2100 series ethercards.
+
+ The Author may be reached as becker@cesdis.gsfc.nasa.gov, or
+ C/O Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ The E2100 series ethercard is a fairly generic shared memory 8390
+ implementation. The only unusual aspect is the way the shared memory
+ registers are set: first you do an inb() in what is normally the
+ station address region, and the low three bits of next outb() *address*
+ is used as the write value for that register. Either someone wasn't
+ too used to dem bit en bites, or they were trying to obfuscate the
+ programming interface.
+
+ There is an additional complication when setting the window on the packet
+ buffer. You must first do a read into the packet buffer region with the
+ low 8 address bits the address setting the page for the start of the packet
+ buffer window, and then do the above operation. See mem_on() for details.
+
+ One bug on the chip is that even a hard reset won't disable the memory
+ window, usually resulting in a hung machine if mem_off() isn't called.
+ If this happens, you must power down the machine for about 30 seconds.
+*/
+
+static const char *version =
+ "e2100.c:v1.01 7/21/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "8390.h"
+
+static int e21_probe_list[] = {0x300, 0x280, 0x380, 0x220, 0};
+
+/* Offsets from the base_addr.
+ Read from the ASIC register, and the low three bits of the next outb()
+ address is used to set the corresponding register. */
+#define E21_NIC_OFFSET 0 /* Offset to the 8390 NIC. */
+#define E21_ASIC 0x10
+#define E21_MEM_ENABLE 0x10
+#define E21_MEM_ON 0x05 /* Enable memory in 16 bit mode. */
+#define E21_MEM_ON_8 0x07 /* Enable memory in 8 bit mode. */
+#define E21_MEM_BASE 0x11
+#define E21_IRQ_LOW 0x12 /* The low three bits of the IRQ number. */
+#define E21_IRQ_HIGH 0x14 /* The high IRQ bit and media select ... */
+#define E21_MEDIA 0x14 /* (alias). */
+#define E21_ALT_IFPORT 0x02 /* Set to use the other (BNC,AUI) port. */
+#define E21_BIG_MEM 0x04 /* Use a bigger (64K) buffer (we don't) */
+#define E21_SAPROM 0x10 /* Offset to station address data. */
+#define E21_IO_EXTENT 0x20
+
+extern inline void mem_on(short port, volatile char *mem_base,
+ unsigned char start_page )
+{
+ /* This is a little weird: set the shared memory window by doing a
+ read. The low address bits specify the starting page. */
+ mem_base[start_page];
+ inb(port + E21_MEM_ENABLE);
+ outb(E21_MEM_ON, port + E21_MEM_ENABLE + E21_MEM_ON);
+}
+
+extern inline void mem_off(short port)
+{
+ inb(port + E21_MEM_ENABLE);
+ outb(0x00, port + E21_MEM_ENABLE);
+}
+
+/* In other drivers I put the TX pages first, but the E2100 window circuitry
+ is designed to have a 4K Tx region last. The windowing circuitry wraps the
+ window at 0x2fff->0x0000 so that the packets at e.g. 0x2f00 in the RX ring
+ appear contiguously in the window. */
+#define E21_RX_START_PG 0x00 /* First page of RX buffer */
+#define E21_RX_STOP_PG 0x30 /* Last page +1 of RX ring */
+#define E21_BIG_RX_STOP_PG 0xF0 /* Last page +1 of RX ring */
+#define E21_TX_START_PG E21_RX_STOP_PG /* First page of TX buffer */
+
+int e2100_probe(struct device *dev);
+int e21_probe1(struct device *dev, int ioaddr);
+
+static int e21_open(struct device *dev);
+static void e21_reset_8390(struct device *dev);
+static void e21_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void e21_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+static int e21_close(struct device *dev);
+
+
+/* Probe for the E2100 series ethercards. These cards have an 8390 at the
+ base address and the station address at both offset 0x10 and 0x18. I read
+ the station address from offset 0x18 to avoid the dataport of NE2000
+ ethercards, and look for Ctron's unique ID (first three octets of the
+ station address).
+ */
+
+int e2100_probe(struct device *dev)
+{
+ int *port;
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return e21_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = e21_probe_list; *port; port++) {
+ if (check_region(*port, E21_IO_EXTENT))
+ continue;
+ if (e21_probe1(dev, *port) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int e21_probe1(struct device *dev, int ioaddr)
+{
+ int i, status;
+ unsigned char *station_addr = dev->dev_addr;
+ static unsigned version_printed = 0;
+
+ /* First check the station address for the Ctron prefix. */
+ if (inb(ioaddr + E21_SAPROM + 0) != 0x00
+ || inb(ioaddr + E21_SAPROM + 1) != 0x00
+ || inb(ioaddr + E21_SAPROM + 2) != 0x1d)
+ return ENODEV;
+
+ /* Verify by making certain that there is a 8390 at there. */
+ outb(E8390_NODMA + E8390_STOP, ioaddr);
+ SLOW_DOWN_IO;
+ status = inb(ioaddr);
+ if (status != 0x21 && status != 0x23)
+ return ENODEV;
+
+ /* Read the station address PROM. */
+ for (i = 0; i < 6; i++)
+ station_addr[i] = inb(ioaddr + E21_SAPROM + i);
+
+ inb(ioaddr + E21_MEDIA); /* Point to media selection. */
+ outb(0, ioaddr + E21_ASIC); /* and disable the secondary interface. */
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("e2100.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ printk("%s: E21** at %#3x,", dev->name, ioaddr);
+ for (i = 0; i < 6; i++)
+ printk(" %02X", station_addr[i]);
+
+ if (dev->irq < 2) {
+ int irqlist[] = {15,11,10,12,5,9,3,4}, i;
+ for (i = 0; i < 8; i++)
+ if (request_irq (irqlist[i], NULL, 0, "bogus") != -EBUSY) {
+ dev->irq = irqlist[i];
+ break;
+ }
+ if (i >= 8) {
+ printk(" unable to get IRQ %d.\n", dev->irq);
+ return EAGAIN;
+ }
+ } else if (dev->irq == 2) /* Fixup luser bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ /* Grab the region so we can find a different board if IRQ select fails. */
+ request_region(ioaddr, E21_IO_EXTENT, "e2100");
+
+ /* The 8390 is at the base address. */
+ dev->base_addr = ioaddr;
+
+ ei_status.name = "E2100";
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = E21_TX_START_PG;
+ ei_status.rx_start_page = E21_RX_START_PG;
+ ei_status.stop_page = E21_RX_STOP_PG;
+ ei_status.saved_irq = dev->irq;
+
+ /* Check the media port used. The port can be passed in on the
+ low mem_end bits. */
+ if (dev->mem_end & 15)
+ dev->if_port = dev->mem_end & 7;
+ else {
+ dev->if_port = 0;
+ inb(ioaddr + E21_MEDIA); /* Turn automatic media detection on. */
+ for(i = 0; i < 6; i++)
+ if (station_addr[i] != inb(ioaddr + E21_SAPROM + 8 + i)) {
+ dev->if_port = 1;
+ break;
+ }
+ }
+
+ /* Never map in the E21 shared memory unless you are actively using it.
+ Also, the shared memory has effective only one setting -- spread all
+ over the 128K region! */
+ if (dev->mem_start == 0)
+ dev->mem_start = 0xd0000;
+
+#ifdef notdef
+ /* These values are unused. The E2100 has a 2K window into the packet
+ buffer. The window can be set to start on any page boundary. */
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end = dev->mem_start + 2*1024;
+#endif
+
+ printk(", IRQ %d, %s media, memory @ %#lx.\n", dev->irq,
+ dev->if_port ? "secondary" : "primary", dev->mem_start);
+
+ ei_status.reset_8390 = &e21_reset_8390;
+ ei_status.block_input = &e21_block_input;
+ ei_status.block_output = &e21_block_output;
+ ei_status.get_8390_hdr = &e21_get_8390_hdr;
+ dev->open = &e21_open;
+ dev->stop = &e21_close;
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+static int
+e21_open(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (request_irq(dev->irq, ei_interrupt, 0, "e2100")) {
+ return EBUSY;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Set the interrupt line and memory base on the hardware. */
+ inb(ioaddr + E21_IRQ_LOW);
+ outb(0, ioaddr + E21_ASIC + (dev->irq & 7));
+ inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb(0, ioaddr + E21_ASIC + (dev->irq > 7 ? 1:0)
+ + (dev->if_port ? E21_ALT_IFPORT : 0));
+ inb(ioaddr + E21_MEM_BASE);
+ outb(0, ioaddr + E21_ASIC + ((dev->mem_start >> 17) & 7));
+
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void
+e21_reset_8390(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outb(0x01, ioaddr);
+ if (ei_debug > 1) printk("resetting the E2180x3 t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Grab the 8390 specific header. We put the 2k window so the header page
+ appears at the start of the shared memory. */
+
+static void
+e21_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ short ioaddr = dev->base_addr;
+ char *shared_mem = (char *)dev->mem_start;
+
+ mem_on(ioaddr, shared_mem, ring_page);
+
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, shared_mem, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(shared_mem);
+#endif
+
+ /* Turn off memory access: we would need to reprogram the window anyway. */
+ mem_off(ioaddr);
+
+}
+
+/* Block input and output are easy on shared memory ethercards.
+ The E21xx makes block_input() especially easy by wrapping the top
+ ring buffer to the bottom automatically. */
+static void
+e21_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ short ioaddr = dev->base_addr;
+ char *shared_mem = (char *)dev->mem_start;
+
+ mem_on(ioaddr, shared_mem, (ring_offset>>8));
+
+ /* Packet is always in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, dev->mem_start + (ring_offset & 0xff), count, 0);
+
+ mem_off(ioaddr);
+}
+
+static void
+e21_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ short ioaddr = dev->base_addr;
+ volatile char *shared_mem = (char *)dev->mem_start;
+
+ /* Set the shared memory window start by doing a read, with the low address
+ bits specifying the starting page. */
+ readb(shared_mem + start_page);
+ mem_on(ioaddr, shared_mem, start_page);
+
+ memcpy_toio(shared_mem, buf, count);
+ mem_off(ioaddr);
+}
+
+static int
+e21_close(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ free_irq(dev->irq);
+ dev->irq = ei_status.saved_irq;
+
+ /* Shut off the interrupt line and secondary interface. */
+ inb(ioaddr + E21_IRQ_LOW);
+ outb(0, ioaddr + E21_ASIC);
+ inb(ioaddr + E21_IRQ_HIGH); /* High IRQ bit, and if_port. */
+ outb(0, ioaddr + E21_ASIC);
+
+ irq2dev_map[dev->irq] = NULL;
+
+ ei_close(dev);
+
+ /* Double-check that the memory has been turned off, because really
+ really bad things happen if it isn't. */
+ mem_off(ioaddr);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry e21_drv =
+{"e21", e21_probe1, E21_IO_EXTENT, e21_probe_list};
+#endif
+
+
+#ifdef MODULE
+#define MAX_E21_CARDS 4 /* Max number of E21 cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_E21_CARDS] = { 0, };
+static struct device dev_e21[MAX_E21_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_E21_CARDS] = { 0, };
+static int irq[MAX_E21_CARDS] = { 0, };
+static int mem[MAX_E21_CARDS] = { 0, };
+static int xcvr[MAX_E21_CARDS] = { 0, }; /* choose int. or ext. xcvr */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+ struct device *dev = &dev_e21[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = xcvr[this_dev]; /* low 4bits = xcvr sel. */
+ dev->init = e2100_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "e2100.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "e2100.c: No E2100 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_E21_CARDS; this_dev++) {
+ struct device *dev = &dev_e21[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: e21_close() handles free_irq + irq2dev map */
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(dev->base_addr, E21_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c e2100.c"
+ * version-control: t
+ * tab-width: 4
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/eepro.c b/i386/i386at/gpl/linux/net/eepro.c
new file mode 100644
index 00000000..2aa2bd14
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/eepro.c
@@ -0,0 +1,1169 @@
+/* eepro.c: Intel EtherExpress Pro/10 device driver for Linux. */
+/*
+ Written 1994, 1995 by Bao C. Ha.
+
+ Copyright (C) 1994, 1995 by Bao C. Ha.
+
+ This software may be used and distributed
+ according to the terms of the GNU Public License,
+ incorporated herein by reference.
+
+ The author may be reached at bao@saigon.async.com
+ or 418 Hastings Place, Martinez, GA 30907.
+
+ Things remaining to do:
+ Better record keeping of errors.
+ Eliminate transmit interrupt to reduce overhead.
+ Implement "concurrent processing". I won't be doing it!
+ Allow changes to the partition of the transmit and receive
+ buffers, currently the ratio is 3:1 of receive to transmit
+ buffer ratio.
+
+ Bugs:
+
+ If you have a problem of not detecting the 82595 during a
+ reboot (warm reset), disable the FLASH memory should fix it.
+ This is a compatibility hardware problem.
+
+ Versions:
+
+ 0.07a Fix a stat report which counts every packet as a
+ heart-beat failure. (BCH, 6/3/95)
+
+ 0.07 Modified to support all other 82595-based lan cards.
+ The IRQ vector of the EtherExpress Pro will be set
+ according to the value saved in the EEPROM. For other
+ cards, I will do autoirq_request() to grab the next
+ available interrupt vector. (BCH, 3/17/95)
+
+ 0.06a,b Interim released. Minor changes in the comments and
+ print out format. (BCH, 3/9/95 and 3/14/95)
+
+ 0.06 First stable release that I am comfortable with. (BCH,
+ 3/2/95)
+
+ 0.05 Complete testing of multicast. (BCH, 2/23/95)
+
+ 0.04 Adding multicast support. (BCH, 2/14/95)
+
+ 0.03 First widely alpha release for public testing.
+ (BCH, 2/14/95)
+
+*/
+
+static const char *version =
+ "eepro.c: v0.07a 6/5/95 Bao C. Ha (bao@saigon.async.com)\n";
+
+#include <linux/module.h>
+
+/*
+ Sources:
+
+ This driver wouldn't have been written without the availability
+ of the Crynwr's Lan595 driver source code. It helps me to
+ familiarize with the 82595 chipset while waiting for the Intel
+ documentation. I also learned how to detect the 82595 using
+ the packet driver's technique.
+
+ This driver is written by cutting and pasting the skeleton.c driver
+ provided by Donald Becker. I also borrowed the EEPROM routine from
+ Donald Becker's 82586 driver.
+
+ Datasheet for the Intel 82595. It provides just enough info that
+ the casual reader might think that it documents the i82595.
+
+ The User Manual for the 82595. It provides a lot of the missing
+ information.
+
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+
+/* First, a few definitions that the brave might change. */
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int eepro_portlist[] =
+ { 0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0x360, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* The number of low I/O ports used by the ethercard. */
+#define EEPRO_IO_EXTENT 16
+
+/* Information that need to be kept for each board. */
+struct eepro_local {
+ struct enet_statistics stats;
+ unsigned rx_start;
+ unsigned tx_start; /* start of the transmit chain */
+ int tx_last; /* pointer to last packet in the transmit chain */
+ unsigned tx_end; /* end of the transmit chain (plus 1) */
+ int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10,
+ FALSE = 0 for other 82595-based lan cards. */
+};
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00
+#define SA_ADDR1 0xaa
+#define SA_ADDR2 0x00
+
+/* Index to functions, as function prototypes. */
+
+extern int eepro_probe(struct device *dev);
+
+static int eepro_probe1(struct device *dev, short ioaddr);
+static int eepro_open(struct device *dev);
+static int eepro_send_packet(struct sk_buff *skb, struct device *dev);
+static void eepro_interrupt(int irq, struct pt_regs *regs);
+static void eepro_rx(struct device *dev);
+static void eepro_transmit_interrupt(struct device *dev);
+static int eepro_close(struct device *dev);
+static struct enet_statistics *eepro_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+static int read_eeprom(int ioaddr, int location);
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+static int eepro_grab_irq(struct device *dev);
+
+/*
+ Details of the i82595.
+
+You will need either the datasheet or the user manual to understand what
+is going on here. The 82595 is very different from the 82586, 82593.
+
+The receive algorithm in eepro_rx() is just an implementation of the
+RCV ring structure that the Intel 82595 imposes at the hardware level.
+The receive buffer is set at 24K, and the transmit buffer is 8K. I
+am assuming that the total buffer memory is 32K, which is true for the
+Intel EtherExpress Pro/10. If it is less than that on a generic card,
+the driver will be broken.
+
+The transmit algorithm in the hardware_send_packet() is similar to the
+one in the eepro_rx(). The transmit buffer is a ring linked list.
+I just queue the next available packet to the end of the list. In my
+system, the 82595 is so fast that the list seems to always contain a
+single packet. In other systems with faster computers and more congested
+network traffics, the ring linked list should improve performance by
+allowing up to 8K worth of packets to be queued.
+
+*/
+#define RAM_SIZE 0x8000
+#define RCV_HEADER 8
+#define RCV_RAM 0x6000 /* 24KB for RCV buffer */
+#define RCV_LOWER_LIMIT 0x00 /* 0x0000 */
+#define RCV_UPPER_LIMIT ((RCV_RAM - 2) >> 8) /* 0x5ffe */
+#define XMT_RAM (RAM_SIZE - RCV_RAM) /* 8KB for XMT buffer */
+#define XMT_LOWER_LIMIT (RCV_RAM >> 8) /* 0x6000 */
+#define XMT_UPPER_LIMIT ((RAM_SIZE - 2) >> 8) /* 0x7ffe */
+#define XMT_HEADER 8
+
+#define RCV_DONE 0x0008
+#define RX_OK 0x2000
+#define RX_ERROR 0x0d81
+
+#define TX_DONE_BIT 0x0080
+#define CHAIN_BIT 0x8000
+#define XMT_STATUS 0x02
+#define XMT_CHAIN 0x04
+#define XMT_COUNT 0x06
+
+#define BANK0_SELECT 0x00
+#define BANK1_SELECT 0x40
+#define BANK2_SELECT 0x80
+
+/* Bank 0 registers */
+#define COMMAND_REG 0x00 /* Register 0 */
+#define MC_SETUP 0x03
+#define XMT_CMD 0x04
+#define DIAGNOSE_CMD 0x07
+#define RCV_ENABLE_CMD 0x08
+#define RCV_DISABLE_CMD 0x0a
+#define STOP_RCV_CMD 0x0b
+#define RESET_CMD 0x0e
+#define POWER_DOWN_CMD 0x18
+#define RESUME_XMT_CMD 0x1c
+#define SEL_RESET_CMD 0x1e
+#define STATUS_REG 0x01 /* Register 1 */
+#define RX_INT 0x02
+#define TX_INT 0x04
+#define EXEC_STATUS 0x30
+#define ID_REG 0x02 /* Register 2 */
+#define R_ROBIN_BITS 0xc0 /* round robin counter */
+#define ID_REG_MASK 0x2c
+#define ID_REG_SIG 0x24
+#define AUTO_ENABLE 0x10
+#define INT_MASK_REG 0x03 /* Register 3 */
+#define RX_STOP_MASK 0x01
+#define RX_MASK 0x02
+#define TX_MASK 0x04
+#define EXEC_MASK 0x08
+#define ALL_MASK 0x0f
+#define RCV_BAR 0x04 /* The following are word (16-bit) registers */
+#define RCV_STOP 0x06
+#define XMT_BAR 0x0a
+#define HOST_ADDRESS_REG 0x0c
+#define IO_PORT 0x0e
+
+/* Bank 1 registers */
+#define REG1 0x01
+#define WORD_WIDTH 0x02
+#define INT_ENABLE 0x80
+#define INT_NO_REG 0x02
+#define RCV_LOWER_LIMIT_REG 0x08
+#define RCV_UPPER_LIMIT_REG 0x09
+#define XMT_LOWER_LIMIT_REG 0x0a
+#define XMT_UPPER_LIMIT_REG 0x0b
+
+/* Bank 2 registers */
+#define XMT_Chain_Int 0x20 /* Interrupt at the end of the transmit chain */
+#define XMT_Chain_ErrStop 0x40 /* Interrupt at the end of the chain even if there are errors */
+#define RCV_Discard_BadFrame 0x80 /* Throw bad frames away, and continue to receive others */
+#define REG2 0x02
+#define PRMSC_Mode 0x01
+#define Multi_IA 0x20
+#define REG3 0x03
+#define TPE_BIT 0x04
+#define BNC_BIT 0x20
+
+#define I_ADD_REG0 0x04
+#define I_ADD_REG1 0x05
+#define I_ADD_REG2 0x06
+#define I_ADD_REG3 0x07
+#define I_ADD_REG4 0x08
+#define I_ADD_REG5 0x09
+
+#define EEPROM_REG 0x0a
+#define EESK 0x01
+#define EECS 0x02
+#define EEDI 0x04
+#define EEDO 0x08
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry netcard_drv =
+{"eepro", eepro_probe1, EEPRO_IO_EXTENT, eepro_portlist};
+#else
+int
+eepro_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return eepro_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; eepro_portlist[i]; i++) {
+ int ioaddr = eepro_portlist[i];
+ if (check_region(ioaddr, EEPRO_IO_EXTENT))
+ continue;
+ if (eepro_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* This is the real probe routine. Linux has a history of friendly device
+ probes on the ISA bus. A good device probes avoids doing writes, and
+ verifies that the correct device exists and functions. */
+
+int eepro_probe1(struct device *dev, short ioaddr)
+{
+ unsigned short station_addr[6], id, counter;
+ int i;
+ int eepro; /* a flag, TRUE=1 for the EtherExpress Pro/10,
+ FALSE = 0 for other 82595-based lan cards. */
+ const char *ifmap[] = {"AUI", "10Base2", "10BaseT"};
+ enum iftype { AUI=0, BNC=1, TPE=2 };
+
+ /* Now, we are going to check for the signature of the
+ ID_REG (register 2 of bank 0) */
+
+ if (((id=inb(ioaddr + ID_REG)) & ID_REG_MASK) == ID_REG_SIG) {
+
+ /* We seem to have the 82595 signature, let's
+ play with its counter (last 2 bits of
+ register 2 of bank 0) to be sure. */
+
+ counter = (id & R_ROBIN_BITS);
+ if (((id=inb(ioaddr+ID_REG)) & R_ROBIN_BITS) ==
+ (counter + 0x40)) {
+
+ /* Yes, the 82595 has been found */
+
+ /* Now, get the ethernet hardware address from
+ the EEPROM */
+
+ station_addr[0] = read_eeprom(ioaddr, 2);
+ station_addr[1] = read_eeprom(ioaddr, 3);
+ station_addr[2] = read_eeprom(ioaddr, 4);
+
+ /* Check the station address for the manufacturer's code */
+
+ if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
+ eepro = 0;
+ printk("%s: Intel 82595-based lan card at %#x,",
+ dev->name, ioaddr);
+ }
+ else {
+ eepro = 1;
+ printk("%s: Intel EtherExpress Pro/10 at %#x,",
+ dev->name, ioaddr);
+ }
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ for (i=0; i < 6; i++) {
+ dev->dev_addr[i] = ((unsigned char *) station_addr)[5-i];
+ printk("%c%02x", i ? ':' : ' ', dev->dev_addr[i]);
+ }
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ id = inb(ioaddr + REG3);
+ if (id & TPE_BIT)
+ dev->if_port = TPE;
+ else dev->if_port = BNC;
+
+ if (dev->irq < 2 && eepro) {
+ i = read_eeprom(ioaddr, 1);
+ switch (i & 0x07) {
+ case 0: dev->irq = 9; break;
+ case 1: dev->irq = 3; break;
+ case 2: dev->irq = 5; break;
+ case 3: dev->irq = 10; break;
+ case 4: dev->irq = 11; break;
+ default: /* should never get here !!!!! */
+ printk(" illegal interrupt vector stored in EEPROM.\n");
+ return ENODEV;
+ }
+ }
+ else if (dev->irq == 2)
+ dev->irq = 9;
+
+ if (dev->irq > 2) {
+ printk(", IRQ %d, %s.\n", dev->irq,
+ ifmap[dev->if_port]);
+ if (request_irq(dev->irq, &eepro_interrupt, 0, "eepro")) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+ }
+ else printk(", %s.\n", ifmap[dev->if_port]);
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+ if (net_debug > 3) {
+ i = read_eeprom(ioaddr, 5);
+ if (i & 0x2000) /* bit 13 of EEPROM word 5 */
+ printk("%s: Concurrent Processing is enabled but not used!\n",
+ dev->name);
+ }
+
+ if (net_debug)
+ printk(version);
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr, EEPRO_IO_EXTENT, "eepro");
+
+ /* Initialize the device structure */
+ dev->priv = kmalloc(sizeof(struct eepro_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct eepro_local));
+
+ dev->open = eepro_open;
+ dev->stop = eepro_close;
+ dev->hard_start_xmit = eepro_send_packet;
+ dev->get_stats = eepro_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with
+ ethernet generic values */
+
+ ether_setup(dev);
+
+ outb(RESET_CMD, ioaddr); /* RESET the 82595 */
+
+ return 0;
+ }
+ else return ENODEV;
+ }
+ else if (net_debug > 3)
+ printk ("EtherExpress Pro probed failed!\n");
+ return ENODEV;
+}
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+
+static char irqrmap[] = {-1,-1,0,1,-1,2,-1,-1,-1,0,3,4,-1,-1,-1,-1};
+static int eepro_grab_irq(struct device *dev)
+{
+ int irqlist[] = { 5, 9, 10, 11, 4, 3, 0};
+ int *irqp = irqlist, temp_reg, ioaddr = dev->base_addr;
+
+ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+ /* Enable the interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* be CAREFUL, BANK 0 now */
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+ /* Let EXEC event to interrupt */
+ outb(ALL_MASK & ~(EXEC_MASK), ioaddr + INT_MASK_REG);
+
+ do {
+ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[*irqp], ioaddr + INT_NO_REG);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ if (request_irq (*irqp, NULL, 0, "bogus") != EBUSY) {
+ /* Twinkle the interrupt, and check if it's seen */
+ autoirq_setup(0);
+
+ outb(DIAGNOSE_CMD, ioaddr); /* RESET the 82595 */
+
+ if (*irqp == autoirq_report(2) && /* It's a good IRQ line */
+ (request_irq(dev->irq = *irqp, &eepro_interrupt, 0, "eepro") == 0))
+ break;
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+ }
+ } while (*++irqp);
+
+ outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Mask all the interrupts. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ return dev->irq;
+}
+
+static int
+eepro_open(struct device *dev)
+{
+ unsigned short temp_reg;
+ int i, ioaddr = dev->base_addr;
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+
+ if (net_debug > 3)
+ printk("eepro: entering eepro_open routine.\n");
+
+ if (dev->dev_addr[0] == SA_ADDR0 &&
+ dev->dev_addr[1] == SA_ADDR1 &&
+ dev->dev_addr[2] == SA_ADDR2)
+ lp->eepro = 1; /* Yes, an Intel EtherExpress Pro/10 */
+ else lp->eepro = 0; /* No, it is a generic 82585 lan card */
+
+ /* Get the interrupt vector for the 82595 */
+ if (dev->irq < 2 && eepro_grab_irq(dev) == 0) {
+ printk("%s: unable to get IRQ %d.\n", dev->name, dev->irq);
+ return -EAGAIN;
+ }
+
+ if (irq2dev_map[dev->irq] != 0
+ || (irq2dev_map[dev->irq] = dev) == 0)
+ return -EAGAIN;
+
+ /* Initialize the 82595. */
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ temp_reg = inb(ioaddr + EEPROM_REG);
+ if (temp_reg & 0x10) /* Check the TurnOff Enable bit */
+ outb(temp_reg & 0xef, ioaddr + EEPROM_REG);
+ for (i=0; i < 6; i++)
+ outb(dev->dev_addr[i] , ioaddr + I_ADD_REG0 + i);
+
+ temp_reg = inb(ioaddr + REG1); /* Setup Transmit Chaining */
+ outb(temp_reg | XMT_Chain_Int | XMT_Chain_ErrStop /* and discard bad RCV frames */
+ | RCV_Discard_BadFrame, ioaddr + REG1);
+
+ temp_reg = inb(ioaddr + REG2); /* Match broadcast */
+ outb(temp_reg | 0x14, ioaddr + REG2);
+
+ temp_reg = inb(ioaddr + REG3);
+ outb(temp_reg & 0x3f, ioaddr + REG3); /* clear test mode */
+
+ /* Set the receiving mode */
+ outb(BANK1_SELECT, ioaddr); /* be CAREFUL, BANK 1 now */
+
+ temp_reg = inb(ioaddr + INT_NO_REG);
+ outb((temp_reg & 0xf8) | irqrmap[dev->irq], ioaddr + INT_NO_REG);
+
+ /* Initialize the RCV and XMT upper and lower limits */
+ outb(RCV_LOWER_LIMIT, ioaddr + RCV_LOWER_LIMIT_REG);
+ outb(RCV_UPPER_LIMIT, ioaddr + RCV_UPPER_LIMIT_REG);
+ outb(XMT_LOWER_LIMIT, ioaddr + XMT_LOWER_LIMIT_REG);
+ outb(XMT_UPPER_LIMIT, ioaddr + XMT_UPPER_LIMIT_REG);
+
+ /* Enable the interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg | INT_ENABLE, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Let RX and TX events to interrupt */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ /* Initialize RCV */
+ outw(RCV_LOWER_LIMIT << 8, ioaddr + RCV_BAR);
+ lp->rx_start = (RCV_LOWER_LIMIT << 8) ;
+ outw((RCV_UPPER_LIMIT << 8) | 0xfe, ioaddr + RCV_STOP);
+
+ /* Initialize XMT */
+ outw(XMT_LOWER_LIMIT << 8, ioaddr + XMT_BAR);
+
+ outb(SEL_RESET_CMD, ioaddr);
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ lp->tx_start = lp->tx_end = XMT_LOWER_LIMIT << 8; /* or = RCV_RAM */
+ lp->tx_last = 0;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ if (net_debug > 3)
+ printk("eepro: exiting eepro_open routine.\n");
+
+ outb(RCV_ENABLE_CMD, ioaddr);
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+eepro_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_send_packet routine.\n");
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ "network cable problem");
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor. */
+ outb(SEL_RESET_CMD, ioaddr);
+ /* We are supposed to wait for 2 us after a SEL_RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ /* Do I also need to flush the transmit buffers here? YES? */
+ lp->tx_start = lp->tx_end = RCV_RAM;
+ lp->tx_last = 0;
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+
+ outb(RCV_ENABLE_CMD, ioaddr);
+
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+ /* lp->stats.tx_aborted_errors++; */
+
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_send_packet routine.\n");
+
+ return 0;
+}
+
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+eepro_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ int ioaddr, status, boguscount = 0;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_interrupt routine.\n");
+
+ if (dev == NULL) {
+ printk ("eepro_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+
+ do {
+ status = inb(ioaddr + STATUS_REG);
+
+ if (status & RX_INT) {
+ if (net_debug > 4)
+ printk("eepro: packet received interrupt.\n");
+
+ /* Acknowledge the RX_INT */
+ outb(RX_INT, ioaddr + STATUS_REG);
+
+ /* Get the received packets */
+ eepro_rx(dev);
+ }
+ else if (status & TX_INT) {
+ if (net_debug > 4)
+ printk("eepro: packet transmit interrupt.\n");
+
+ /* Acknowledge the TX_INT */
+ outb(TX_INT, ioaddr + STATUS_REG);
+
+ /* Process the status of transmitted packets */
+ eepro_transmit_interrupt(dev);
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ } while ((++boguscount < 10) && (status & 0x06));
+
+ dev->interrupt = 0;
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_interrupt routine.\n");
+
+ return;
+}
+
+static int
+eepro_close(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ short temp_reg;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(BANK1_SELECT, ioaddr); /* Switch back to Bank 1 */
+
+ /* Disable the physical interrupt line. */
+ temp_reg = inb(ioaddr + REG1);
+ outb(temp_reg & 0x7f, ioaddr + REG1);
+
+ outb(BANK0_SELECT, ioaddr); /* Switch back to Bank 0 */
+
+ /* Flush the Tx and disable Rx. */
+ outb(STOP_RCV_CMD, ioaddr);
+ lp->tx_start = lp->tx_end = RCV_RAM ;
+ lp->tx_last = 0;
+
+ /* Mask all the interrupts. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ /* clear all interrupts */
+ outb(ALL_MASK, ioaddr + STATUS_REG);
+
+ /* Reset the 82595 */
+ outb(RESET_CMD, ioaddr);
+
+ /* release the interrupt */
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. What statistics? */
+
+ /* We are supposed to wait for 200 us after a RESET */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO; /* May not be enough? */
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+eepro_get_stats(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned short mode;
+ struct dev_mc_list *dmi=dev->mc_list;
+
+ if (dev->flags&(IFF_ALLMULTI|IFF_PROMISC) || dev->mc_count > 63)
+ {
+ /*
+ * We must make the kernel realise we had to move
+ * into promisc mode or we start all out war on
+ * the cable. If it was a promisc rewquest the
+ * flag is already set. If not we assert it.
+ */
+ dev->flags|=IFF_PROMISC;
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | PRMSC_Mode, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ printk("%s: promiscuous mode enabled.\n", dev->name);
+ }
+ else if (dev->mc_count==0 )
+ {
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode & 0xd6, ioaddr + REG2); /* Turn off Multi-IA and PRMSC_Mode bits */
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ }
+ else
+ {
+ unsigned short status, *eaddrs;
+ int i, boguscount = 0;
+
+ /* Disable RX and TX interrupts. Neccessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ outb(BANK2_SELECT, ioaddr); /* be CAREFUL, BANK 2 now */
+ mode = inb(ioaddr + REG2);
+ outb(mode | Multi_IA, ioaddr + REG2);
+ mode = inb(ioaddr + REG3);
+ outb(mode, ioaddr + REG3); /* writing reg. 3 to complete the update */
+ outb(BANK0_SELECT, ioaddr); /* Return to BANK 0 now */
+ outw(lp->tx_end, ioaddr + HOST_ADDRESS_REG);
+ outw(MC_SETUP, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(6*(dev->mc_count + 1), ioaddr + IO_PORT);
+ for (i = 0; i < dev->mc_count; i++)
+ {
+ eaddrs=(unsigned short *)dmi->dmi_addr;
+ dmi=dmi->next;
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ outw(*eaddrs++, ioaddr + IO_PORT);
+ }
+ eaddrs = (unsigned short *) dev->dev_addr;
+ outw(eaddrs[0], ioaddr + IO_PORT);
+ outw(eaddrs[1], ioaddr + IO_PORT);
+ outw(eaddrs[2], ioaddr + IO_PORT);
+ outw(lp->tx_end, ioaddr + XMT_BAR);
+ outb(MC_SETUP, ioaddr);
+
+ /* Update the transmit queue */
+ i = lp->tx_end + XMT_HEADER + 6*(dev->mc_count + 1);
+ if (lp->tx_start != lp->tx_end)
+ {
+ /* update the next address and the chain bit in the
+ last packet */
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(i, ioaddr + IO_PORT);
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ lp->tx_end = i ;
+ }
+ else lp->tx_start = lp->tx_end = i ;
+
+ /* Acknowledge that the MC setup is done */
+ do { /* We should be doing this in the eepro_interrupt()! */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ if (inb(ioaddr + STATUS_REG) & 0x08)
+ {
+ i = inb(ioaddr);
+ outb(0x08, ioaddr + STATUS_REG);
+ if (i & 0x20) { /* command ABORTed */
+ printk("%s: multicast setup failed.\n",
+ dev->name);
+ break;
+ } else if ((i & 0x0f) == 0x03) { /* MC-Done */
+ printk("%s: set Rx mode to %d addresses.\n",
+ dev->name, dev->mc_count);
+ break;
+ }
+ }
+ } while (++boguscount < 100);
+
+ /* Re-enable RX and TX interrupts */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+
+ }
+ outb(RCV_ENABLE_CMD, ioaddr);
+}
+
+/* The horrible routine to read a word from the serial EEPROM. */
+/* IMPORTANT - the 82595 will be set to Bank 0 after the eeprom is read */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}
+#define EE_READ_CMD (6 << 6)
+
+int
+read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_REG;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EECS ;
+
+ outb(BANK2_SELECT, ioaddr);
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EEDI
+ : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EESK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(outval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EESK, ee_addr); eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EEDO) ? 1 : 0);
+ outb(ctrl_val, ee_addr); eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EECS;
+ outb(ctrl_val | EESK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ outb(BANK0_SELECT, ioaddr);
+ return retval;
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ unsigned status, tx_available, last, end, boguscount = 10;
+
+ if (net_debug > 5)
+ printk("eepro: entering hardware_send_packet routine.\n");
+
+ while (boguscount-- > 0) {
+
+ /* determine how much of the transmit buffer space is available */
+ if (lp->tx_end > lp->tx_start)
+ tx_available = XMT_RAM - (lp->tx_end - lp->tx_start);
+ else if (lp->tx_end < lp->tx_start)
+ tx_available = lp->tx_start - lp->tx_end;
+ else tx_available = XMT_RAM;
+
+ /* Disable RX and TX interrupts. Neccessary to avoid
+ corruption of the HOST_ADDRESS_REG by interrupt
+ service routines. */
+ outb(ALL_MASK, ioaddr + INT_MASK_REG);
+
+ if (((((length + 1) >> 1) << 1) + 2*XMT_HEADER)
+ >= tx_available) /* No space available ??? */
+ continue;
+
+ last = lp->tx_end;
+ end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+
+ if (end >= RAM_SIZE) { /* the transmit buffer is wrapped around */
+ if ((RAM_SIZE - last) <= XMT_HEADER) {
+ /* Arrrr!!!, must keep the xmt header together,
+ several days were lost to chase this one down. */
+ last = RCV_RAM;
+ end = last + (((length + 1) >> 1) << 1) + XMT_HEADER;
+ }
+ else end = RCV_RAM + (end - RAM_SIZE);
+ }
+
+ outw(last, ioaddr + HOST_ADDRESS_REG);
+ outw(XMT_CMD, ioaddr + IO_PORT);
+ outw(0, ioaddr + IO_PORT);
+ outw(end, ioaddr + IO_PORT);
+ outw(length, ioaddr + IO_PORT);
+ outsw(ioaddr + IO_PORT, buf, (length + 1) >> 1);
+
+ if (lp->tx_start != lp->tx_end) {
+ /* update the next address and the chain bit in the
+ last packet */
+ if (lp->tx_end != last) {
+ outw(lp->tx_last + XMT_CHAIN, ioaddr + HOST_ADDRESS_REG);
+ outw(last, ioaddr + IO_PORT);
+ }
+ outw(lp->tx_last + XMT_COUNT, ioaddr + HOST_ADDRESS_REG);
+ status = inw(ioaddr + IO_PORT);
+ outw(status | CHAIN_BIT, ioaddr + IO_PORT);
+ }
+
+ /* A dummy read to flush the DRAM write pipeline */
+ status = inw(ioaddr + IO_PORT);
+
+ /* Enable RX and TX interrupts */
+ outb(ALL_MASK & ~(RX_MASK | TX_MASK), ioaddr + INT_MASK_REG);
+
+ if (lp->tx_start == lp->tx_end) {
+ outw(last, ioaddr + XMT_BAR);
+ outb(XMT_CMD, ioaddr);
+ lp->tx_start = last; /* I don't like to change tx_start here */
+ }
+ else outb(RESUME_XMT_CMD, ioaddr);
+
+ lp->tx_last = last;
+ lp->tx_end = end;
+
+ if (dev->tbusy) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (net_debug > 5)
+ printk("eepro: exiting hardware_send_packet routine.\n");
+ return;
+ }
+ dev->tbusy = 1;
+ if (net_debug > 5)
+ printk("eepro: exiting hardware_send_packet routine.\n");
+}
+
+static void
+eepro_rx(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short boguscount = 20;
+ short rcv_car = lp->rx_start;
+ unsigned rcv_event, rcv_status, rcv_next_frame, rcv_size;
+
+ if (net_debug > 5)
+ printk("eepro: entering eepro_rx routine.\n");
+
+ /* Set the read pointer to the start of the RCV */
+ outw(rcv_car, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ while (rcv_event == RCV_DONE) {
+ rcv_status = inw(ioaddr + IO_PORT);
+ rcv_next_frame = inw(ioaddr + IO_PORT);
+ rcv_size = inw(ioaddr + IO_PORT);
+
+ if ((rcv_status & (RX_OK | RX_ERROR)) == RX_OK) {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ rcv_size &= 0x3fff;
+ skb = dev_alloc_skb(rcv_size+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2);
+
+ insw(ioaddr+IO_PORT, skb_put(skb,rcv_size), (rcv_size + 1) >> 1);
+
+ skb->protocol = eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ else { /* Not sure will ever reach here,
+ I set the 595 to discard bad received frames */
+ lp->stats.rx_errors++;
+ if (rcv_status & 0x0100)
+ lp->stats.rx_over_errors++;
+ else if (rcv_status & 0x0400)
+ lp->stats.rx_frame_errors++;
+ else if (rcv_status & 0x0800)
+ lp->stats.rx_crc_errors++;
+ printk("%s: event = %#x, status = %#x, next = %#x, size = %#x\n",
+ dev->name, rcv_event, rcv_status, rcv_next_frame, rcv_size);
+ }
+ if (rcv_status & 0x1000)
+ lp->stats.rx_length_errors++;
+ if (--boguscount == 0)
+ break;
+
+ rcv_car = lp->rx_start + RCV_HEADER + rcv_size;
+ lp->rx_start = rcv_next_frame;
+ outw(rcv_next_frame, ioaddr + HOST_ADDRESS_REG);
+ rcv_event = inw(ioaddr + IO_PORT);
+
+ }
+ if (rcv_car == 0)
+ rcv_car = (RCV_UPPER_LIMIT << 8) | 0xff;
+ outw(rcv_car - 1, ioaddr + RCV_STOP);
+
+ if (net_debug > 5)
+ printk("eepro: exiting eepro_rx routine.\n");
+}
+
+static void
+eepro_transmit_interrupt(struct device *dev)
+{
+ struct eepro_local *lp = (struct eepro_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short boguscount = 10;
+ short xmt_status;
+
+ while (lp->tx_start != lp->tx_end) {
+
+ outw(lp->tx_start, ioaddr + HOST_ADDRESS_REG);
+ xmt_status = inw(ioaddr+IO_PORT);
+ if ((xmt_status & TX_DONE_BIT) == 0) break;
+ xmt_status = inw(ioaddr+IO_PORT);
+ lp->tx_start = inw(ioaddr+IO_PORT);
+
+ if (dev->tbusy) {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ if (xmt_status & 0x2000)
+ lp->stats.tx_packets++;
+ else {
+ lp->stats.tx_errors++;
+ if (xmt_status & 0x0400)
+ lp->stats.tx_carrier_errors++;
+ printk("%s: XMT status = %#x\n",
+ dev->name, xmt_status);
+ }
+ if (xmt_status & 0x000f)
+ lp->stats.collisions += (xmt_status & 0x000f);
+ if ((xmt_status & 0x0040) == 0x0)
+ lp->stats.tx_heartbeat_errors++;
+
+ if (--boguscount == 0)
+ break;
+ }
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_eepro = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, eepro_probe };
+
+static int io = 0x200;
+static int irq = 0;
+
+int
+init_module(void)
+{
+ if (io == 0)
+ printk("eepro: You should not use auto-probing with insmod!\n");
+ dev_eepro.base_addr = io;
+ dev_eepro.irq = irq;
+
+ if (register_netdev(&dev_eepro) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_eepro);
+ kfree_s(dev_eepro.priv,sizeof(struct eepro_local));
+ dev_eepro.priv=NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_eepro.base_addr, EEPRO_IO_EXTENT);
+}
+#endif /* MODULE */
diff --git a/i386/i386at/gpl/linux/net/eexpress.c b/i386/i386at/gpl/linux/net/eexpress.c
new file mode 100644
index 00000000..2f641d68
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/eexpress.c
@@ -0,0 +1,1034 @@
+/* eexpress.c: Intel EtherExpress device driver for Linux. */
+/*
+ Written 1993 by Donald Becker.
+ Copyright 1993 United States Government as represented by the Director,
+ National Security Agency. This software may only be used and distributed
+ according to the terms of the GNU Public License as modified by SRC,
+ incorporated herein by reference.
+
+ The author may be reached as becker@super.org or
+ C/O Supercomputing Research Ctr., 17100 Science Dr., Bowie MD 20715
+
+ Things remaining to do:
+ Check that the 586 and ASIC are reset/unreset at the right times.
+ Check tx and rx buffer setup.
+ The current Tx is single-buffer-only.
+ Move the theory of operation and memory map documentation.
+ Rework the board error reset
+ The statistics need to be updated correctly.
+
+ Modularized by Pauline Middelink <middelin@polyware.iaf.nl>
+ Changed to support io= irq= by Alan Cox <Alan.Cox@linux.org>
+*/
+
+static const char *version =
+ "eexpress.c:v0.07 1/19/94 Donald Becker (becker@super.org)\n";
+
+/*
+ Sources:
+ This driver wouldn't have been written with the availability of the
+ Crynwr driver source code. It provided a known-working implementation
+ that filled in the gaping holes of the Intel documentation. Three cheers
+ for Russ Nelson.
+
+ Intel Microcommunications Databook, Vol. 1, 1990. It provides just enough
+ info that the casual reader might think that it documents the i82586.
+*/
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <linux/in.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/malloc.h>
+
+/* use 0 for production, 1 for verification, 2..7 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/*
+ Details of the i82586.
+
+ You'll really need the databook to understand the details of this part,
+ but the outline is that the i82586 has two separate processing units.
+
+ The Rx unit uses a list of frame descriptors and a list of data buffer
+ descriptors. We use full-sized (1518 byte) data buffers, so there is
+ a one-to-one pairing of frame descriptors to buffer descriptors.
+
+ The Tx ("command") unit executes a list of commands that look like:
+ Status word Written by the 82586 when the command is done.
+ Command word Command in lower 3 bits, post-command action in upper 3
+ Link word The address of the next command.
+ Parameters (as needed).
+
+ Some definitions related to the Command Word are:
+ */
+#define CMD_EOL 0x8000 /* The last command of the list, stop. */
+#define CMD_SUSP 0x4000 /* Suspend after doing cmd. */
+#define CMD_INTR 0x2000 /* Interrupt after doing cmd. */
+
+enum commands {
+ CmdNOp = 0, CmdSASetup = 1, CmdConfigure = 2, CmdMulticastList = 3,
+ CmdTx = 4, CmdTDR = 5, CmdDump = 6, CmdDiagnose = 7};
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ int last_restart;
+ short rx_head;
+ short rx_tail;
+ short tx_head;
+ short tx_cmd_link;
+ short tx_reap;
+};
+
+/*
+ Details of the EtherExpress Implementation
+ The EtherExpress takes an unusual approach to host access to packet buffer
+ memory. The host can use either the Dataport, with independent
+ autoincrementing read and write pointers, or it can I/O map 32 bytes of the
+ memory using the "Shadow Memory Pointer" (SMB) as follows:
+ ioaddr Normal EtherExpress registers
+ ioaddr+0x4000...0x400f Buffer Memory at SMB...SMB+15
+ ioaddr+0x8000...0x800f Buffer Memory at SMB+16...SMB+31
+ ioaddr+0xC000...0xC007 "" SMB+16...SMB+23 (hardware flaw?)
+ ioaddr+0xC008...0xC00f Buffer Memory at 0x0008...0x000f
+ The last I/O map set is useful if you put the i82586 System Command Block
+ (the command mailbox) exactly at 0x0008. (There seems to be some
+ undocumented init structure at 0x0000-7, so I had to use the Crywnr memory
+ setup verbatim for those four words anyway.)
+
+ A problem with using either one of these mechanisms is that you must run
+ single-threaded, or the interrupt handler must restore a changed value of
+ the read, write, or SMB pointers.
+
+ Unlike the Crynwr driver, my driver mostly ignores the I/O mapped "feature"
+ and relies heavily on the dataport for buffer memory access. To minimize
+ switching, the read_pointer is dedicated to the Rx interrupt handler, and
+ the write_pointer is used by the send_packet() routine (it's carefully saved
+ and restored when it's needed by the interrupt handler).
+ */
+
+/* Offsets from the base I/O address. */
+#define DATAPORT 0 /* Data Transfer Register. */
+#define WRITE_PTR 2 /* Write Address Pointer. */
+#define READ_PTR 4 /* Read Address Pointer. */
+#define SIGNAL_CA 6 /* Frob the 82586 Channel Attention line. */
+#define SET_IRQ 7 /* IRQ Select. */
+#define SHADOW_PTR 8 /* Shadow Memory Bank Pointer. */
+#define MEM_Ctrl 11
+#define MEM_Page_Ctrl 12
+#define Config 13
+#define EEPROM_Ctrl 14
+#define ID_PORT 15
+
+#define EEXPRESS_IO_EXTENT 16
+
+/* EEPROM_Ctrl bits. */
+
+#define EE_SHIFT_CLK 0x01 /* EEPROM shift clock. */
+#define EE_CS 0x02 /* EEPROM chip select. */
+#define EE_DATA_WRITE 0x04 /* EEPROM chip data in. */
+#define EE_DATA_READ 0x08 /* EEPROM chip data out. */
+#define EE_CTRL_BITS (EE_SHIFT_CLK | EE_CS | EE_DATA_WRITE | EE_DATA_READ)
+#define ASIC_RESET 0x40
+#define _586_RESET 0x80
+
+/* Offsets to elements of the System Control Block structure. */
+#define SCB_STATUS 0xc008
+#define SCB_CMD 0xc00A
+#define CUC_START 0x0100
+#define CUC_RESUME 0x0200
+#define CUC_SUSPEND 0x0300
+#define RX_START 0x0010
+#define RX_RESUME 0x0020
+#define RX_SUSPEND 0x0030
+#define SCB_CBL 0xc00C /* Command BLock offset. */
+#define SCB_RFA 0xc00E /* Rx Frame Area offset. */
+
+/*
+ What follows in 'init_words[]' is the "program" that is downloaded to the
+ 82586 memory. It's mostly tables and command blocks, and starts at the
+ reset address 0xfffff6.
+
+ Even with the additional "don't care" values, doing it this way takes less
+ program space than initializing the individual tables, and I feel it's much
+ cleaner.
+
+ The databook is particularly useless for the first two structures; they are
+ completely undocumented. I had to use the Crynwr driver as an example.
+
+ The memory setup is as follows:
+ */
+
+#define CONFIG_CMD 0x0018
+#define SET_SA_CMD 0x0024
+#define SA_OFFSET 0x002A
+#define IDLELOOP 0x30
+#define TDR_CMD 0x38
+#define TDR_TIME 0x3C
+#define DUMP_CMD 0x40
+#define DIAG_CMD 0x48
+#define SET_MC_CMD 0x4E
+#define DUMP_DATA 0x56 /* A 170 byte buffer for dump and Set-MC into. */
+
+#define TX_BUF_START 0x0100
+#define NUM_TX_BUFS 4
+#define TX_BUF_SIZE 0x0680 /* packet+header+TBD+extra (1518+14+20+16) */
+#define TX_BUF_END 0x2000
+
+#define RX_BUF_START 0x2000
+#define RX_BUF_SIZE (0x640) /* packet+header+RBD+extra */
+#define RX_BUF_END 0x4000
+
+/*
+ That's it: only 86 bytes to set up the beast, including every extra
+ command available. The 170 byte buffer at DUMP_DATA is shared between the
+ Dump command (called only by the diagnostic program) and the SetMulticastList
+ command.
+
+ To complete the memory setup you only have to write the station address at
+ SA_OFFSET and create the Tx & Rx buffer lists.
+
+ The Tx command chain and buffer list is setup as follows:
+ A Tx command table, with the data buffer pointing to...
+ A Tx data buffer descriptor. The packet is in a single buffer, rather than
+ chaining together several smaller buffers.
+ A NoOp command, which initially points to itself,
+ And the packet data.
+
+ A transmit is done by filling in the Tx command table and data buffer,
+ re-writing the NoOp command, and finally changing the offset of the last
+ command to point to the current Tx command. When the Tx command is finished,
+ it jumps to the NoOp, when it loops until the next Tx command changes the
+ "link offset" in the NoOp. This way the 82586 never has to go through the
+ slow restart sequence.
+
+ The Rx buffer list is set up in the obvious ring structure. We have enough
+ memory (and low enough interrupt latency) that we can avoid the complicated
+ Rx buffer linked lists by alway associating a full-size Rx data buffer with
+ each Rx data frame.
+
+ I current use four transmit buffers starting at TX_BUF_START (0x0100), and
+ use the rest of memory, from RX_BUF_START to RX_BUF_END, for Rx buffers.
+
+ */
+
+static short init_words[] = {
+ 0x0000, /* Set bus size to 16 bits. */
+ 0x0000,0x0000, /* Set control mailbox (SCB) addr. */
+ 0,0, /* pad to 0x000000. */
+ 0x0001, /* Status word that's cleared when init is done. */
+ 0x0008,0,0, /* SCB offset, (skip, skip) */
+
+ 0,0xf000|RX_START|CUC_START, /* SCB status and cmd. */
+ CONFIG_CMD, /* Command list pointer, points to Configure. */
+ RX_BUF_START, /* Rx block list. */
+ 0,0,0,0, /* Error count: CRC, align, buffer, overrun. */
+
+ /* 0x0018: Configure command. Change to put MAC data with packet. */
+ 0, CmdConfigure, /* Status, command. */
+ SET_SA_CMD, /* Next command is Set Station Addr. */
+ 0x0804, /* "4" bytes of config data, 8 byte FIFO. */
+ 0x2e40, /* Magic values, including MAC data location. */
+ 0, /* Unused pad word. */
+
+ /* 0x0024: Setup station address command. */
+ 0, CmdSASetup,
+ SET_MC_CMD, /* Next command. */
+ 0xaa00,0xb000,0x0bad, /* Station address (to be filled in) */
+
+ /* 0x0030: NOP, looping back to itself. Point to first Tx buffer to Tx. */
+ 0, CmdNOp, IDLELOOP, 0 /* pad */,
+
+ /* 0x0038: A unused Time-Domain Reflectometer command. */
+ 0, CmdTDR, IDLELOOP, 0,
+
+ /* 0x0040: An unused Dump State command. */
+ 0, CmdDump, IDLELOOP, DUMP_DATA,
+
+ /* 0x0048: An unused Diagnose command. */
+ 0, CmdDiagnose, IDLELOOP,
+
+ /* 0x004E: An empty set-multicast-list command. */
+#ifdef initial_text_tx
+ 0, CmdMulticastList, DUMP_DATA, 0,
+#else
+ 0, CmdMulticastList, IDLELOOP, 0,
+#endif
+
+ /* 0x0056: A continuous transmit command, only here for testing. */
+ 0, CmdTx, DUMP_DATA, DUMP_DATA+8, 0x83ff, -1, DUMP_DATA, 0,
+};
+
+/* Index to functions, as function prototypes. */
+
+extern int express_probe(struct device *dev); /* Called from Space.c */
+
+static int eexp_probe1(struct device *dev, short ioaddr);
+static int eexp_open(struct device *dev);
+static int eexp_send_packet(struct sk_buff *skb, struct device *dev);
+static void eexp_interrupt(int irq, struct pt_regs *regs);
+static void eexp_rx(struct device *dev);
+static int eexp_close(struct device *dev);
+static struct enet_statistics *eexp_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+static int read_eeprom(int ioaddr, int location);
+static void hardware_send_packet(struct device *dev, void *buf, short length);
+static void init_82586_mem(struct device *dev);
+static void init_rx_bufs(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, (detachable devices only) allocate space for the
+ device and return success.
+ */
+int
+express_probe(struct device *dev)
+{
+ /* Don't probe all settable addresses, 0x[23][0-7]0, just common ones. */
+ int *port, ports[] = {0x300, 0x270, 0x320, 0x340, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return eexp_probe1(dev, base_addr);
+ else if (base_addr > 0)
+ return ENXIO; /* Don't probe at all. */
+
+ for (port = &ports[0]; *port; port++) {
+ short id_addr = *port + ID_PORT;
+ unsigned short sum = 0;
+ int i;
+#ifdef notdef
+ for (i = 16; i > 0; i--)
+ sum += inb(id_addr);
+ printk("EtherExpress ID checksum is %04x.\n", sum);
+#else
+ for (i = 4; i > 0; i--) {
+ short id_val = inb(id_addr);
+ sum |= (id_val >> 4) << ((id_val & 3) << 2);
+ }
+#endif
+ if (sum == 0xbaba
+ && eexp_probe1(dev, *port) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+
+int eexp_probe1(struct device *dev, short ioaddr)
+{
+ unsigned short station_addr[3];
+ int i;
+
+ printk("%s: EtherExpress at %#x,", dev->name, ioaddr);
+
+ /* The station address is stored !backwards! in the EEPROM, reverse
+ after reading. (Hmmm, a little brain-damage there at Intel, eh?) */
+ station_addr[0] = read_eeprom(ioaddr, 2);
+ station_addr[1] = read_eeprom(ioaddr, 3);
+ station_addr[2] = read_eeprom(ioaddr, 4);
+
+ /* Check the first three octets of the S.A. for the manufacturer's code. */
+ if (station_addr[2] != 0x00aa || (station_addr[1] & 0xff00) != 0x0000) {
+ printk(" rejected (invalid address %04x%04x%04x).\n",
+ station_addr[2], station_addr[1], station_addr[0]);
+ return ENODEV;
+ }
+
+ /* We've committed to using the board, and can start filling in *dev. */
+ request_region(ioaddr, EEXPRESS_IO_EXTENT, "eexpress");
+ dev->base_addr = ioaddr;
+
+ for (i = 0; i < 6; i++) {
+ dev->dev_addr[i] = ((unsigned char*)station_addr)[5-i];
+ printk(" %02x", dev->dev_addr[i]);
+ }
+
+ /* There is no reason for the driver to care, but I print out the
+ interface to minimize bogus bug reports. */
+ {
+ char irqmap[] = {0, 9, 3, 4, 5, 10, 11, 0};
+ const char *ifmap[] = {"AUI", "BNC", "10baseT"};
+ enum iftype {AUI=0, BNC=1, TP=2};
+ unsigned short setupval = read_eeprom(ioaddr, 0);
+
+ dev->irq = irqmap[setupval >> 13];
+ dev->if_port = (setupval & 0x1000) == 0 ? AUI :
+ read_eeprom(ioaddr, 5) & 0x1 ? TP : BNC;
+ printk(", IRQ %d, Interface %s.\n", dev->irq, ifmap[dev->if_port]);
+ /* Release the IRQ line so that it can be shared if we don't use the
+ ethercard. */
+ outb(0x00, ioaddr + SET_IRQ);
+ }
+
+ /* It's now OK to leave the board in reset, pending the open(). */
+ outb(ASIC_RESET, ioaddr + EEPROM_Ctrl);
+
+ if ((dev->mem_start & 0xf) > 0)
+ net_debug = dev->mem_start & 7;
+
+ if (net_debug)
+ printk(version);
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = eexp_open;
+ dev->stop = eexp_close;
+ dev->hard_start_xmit = eexp_send_packet;
+ dev->get_stats = eexp_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with ethernet-generic values. */
+
+ ether_setup(dev);
+
+ dev->flags&=~IFF_MULTICAST;
+
+ return 0;
+}
+
+
+/* Reverse IRQ map: the value to put in the SET_IRQ reg. for IRQ<index>. */
+static char irqrmap[]={0,0,1,2,3,4,0,0,0,1,5,6,0,0,0,0};
+
+static int
+eexp_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (dev->irq == 0 || irqrmap[dev->irq] == 0)
+ return -ENXIO;
+
+ if (irq2dev_map[dev->irq] != 0
+ /* This is always true, but avoid the false IRQ. */
+ || (irq2dev_map[dev->irq] = dev) == 0
+ || request_irq(dev->irq, &eexp_interrupt, 0, "EExpress")) {
+ return -EAGAIN;
+ }
+
+ /* Initialize the 82586 memory and start it. */
+ init_82586_mem(dev);
+
+ /* Enable the interrupt line. */
+ outb(irqrmap[dev->irq] | 0x08, ioaddr + SET_IRQ);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+eexp_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ if (net_debug > 1)
+ printk("%s: transmit timed out, %s? ", dev->name,
+ inw(ioaddr+SCB_STATUS) & 0x8000 ? "IRQ conflict" :
+ "network cable problem");
+ lp->stats.tx_errors++;
+ /* Try to restart the adaptor. */
+ if (lp->last_restart == lp->stats.tx_packets) {
+ if (net_debug > 1) printk("Resetting board.\n");
+ /* Completely reset the adaptor. */
+ init_82586_mem(dev);
+ } else {
+ /* Issue the channel attention signal and hope it "gets better". */
+ if (net_debug > 1) printk("Kicking board.\n");
+ outw(0xf000|CUC_START|RX_START, ioaddr + SCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+ lp->last_restart = lp->stats.tx_packets;
+ }
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(irqrmap[dev->irq], ioaddr + SET_IRQ);
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ /* Enable the 82586 interrupt input. */
+ outb(0x08 | irqrmap[dev->irq], ioaddr + SET_IRQ);
+ }
+
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+ lp->stats.tx_aborted_errors++;
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+eexp_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+ short ack_cmd;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ status = inw(ioaddr + SCB_STATUS);
+
+ if (net_debug > 4) {
+ printk("%s: EExp interrupt, status %4.4x.\n", dev->name, status);
+ }
+
+ /* Disable the 82586's input to the interrupt line. */
+ outb(irqrmap[dev->irq], ioaddr + SET_IRQ);
+
+ /* Reap the Tx packet buffers. */
+ while (lp->tx_reap != lp->tx_head) { /* if (status & 0x8000) */
+ unsigned short tx_status;
+ outw(lp->tx_reap, ioaddr + READ_PTR);
+ tx_status = inw(ioaddr);
+ if (tx_status == 0) {
+ if (net_debug > 5) printk("Couldn't reap %#x.\n", lp->tx_reap);
+ break;
+ }
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ } else {
+ lp->stats.tx_errors++;
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ }
+ if (net_debug > 5)
+ printk("Reaped %x, Tx status %04x.\n" , lp->tx_reap, tx_status);
+ lp->tx_reap += TX_BUF_SIZE;
+ if (lp->tx_reap > TX_BUF_END - TX_BUF_SIZE)
+ lp->tx_reap = TX_BUF_START;
+ if (++boguscount > 4)
+ break;
+ }
+
+ if (status & 0x4000) { /* Packet received. */
+ if (net_debug > 5)
+ printk("Received packet, rx_head %04x.\n", lp->rx_head);
+ eexp_rx(dev);
+ }
+
+ /* Acknowledge the interrupt sources. */
+ ack_cmd = status & 0xf000;
+
+ if ((status & 0x0700) != 0x0200 && dev->start) {
+ short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ if (net_debug > 1)
+ printk("%s: Command unit stopped, status %04x, restarting.\n",
+ dev->name, status);
+ /* If this ever occurs we must re-write the idle loop, reset
+ the Tx list, and do a complete restart of the command unit. */
+ outw(IDLELOOP, ioaddr + WRITE_PTR);
+ outw(0, ioaddr);
+ outw(CmdNOp, ioaddr);
+ outw(IDLELOOP, ioaddr);
+ outw(IDLELOOP, SCB_CBL);
+ lp->tx_cmd_link = IDLELOOP + 4;
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+ /* Restore the saved write pointer. */
+ outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ ack_cmd |= CUC_START;
+ }
+
+ if ((status & 0x0070) != 0x0040 && dev->start) {
+ short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ /* The Rx unit is not ready, it must be hung. Restart the receiver by
+ initializing the rx buffers, and issuing an Rx start command. */
+ lp->stats.rx_errors++;
+ if (net_debug > 1) {
+ int cur_rxbuf = RX_BUF_START;
+ printk("%s: Rx unit stopped status %04x rx head %04x tail %04x.\n",
+ dev->name, status, lp->rx_head, lp->rx_tail);
+ while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE) {
+ int i;
+ printk(" Rx buf at %04x:", cur_rxbuf);
+ outw(cur_rxbuf, ioaddr + READ_PTR);
+ for (i = 0; i < 0x20; i += 2)
+ printk(" %04x", inw(ioaddr));
+ printk(".\n");
+ cur_rxbuf += RX_BUF_SIZE;
+ }
+ }
+ init_rx_bufs(dev);
+ outw(RX_BUF_START, SCB_RFA);
+ outw(saved_write_ptr, ioaddr + WRITE_PTR);
+ ack_cmd |= RX_START;
+ }
+
+ outw(ack_cmd, ioaddr + SCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+
+ if (net_debug > 5) {
+ printk("%s: EExp exiting interrupt, status %4.4x.\n", dev->name,
+ inw(ioaddr + SCB_CMD));
+ }
+ /* Enable the 82586's input to the interrupt line. */
+ outb(irqrmap[dev->irq] | 0x08, ioaddr + SET_IRQ);
+ return;
+}
+
+static int
+eexp_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx. */
+ outw(RX_SUSPEND | CUC_SUSPEND, ioaddr + SCB_CMD);
+ outb(0, ioaddr + SIGNAL_CA);
+
+ /* Disable the physical interrupt line. */
+ outb(0, ioaddr + SET_IRQ);
+
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. */
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+eexp_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ /* ToDo: decide if there are any useful statistics from the SCB. */
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+/* This doesn't work yet */
+#if 0
+ short ioaddr = dev->base_addr;
+ if (num_addrs < 0) {
+ /* Not written yet, this requires expanding the init_words config
+ cmd. */
+ } else if (num_addrs > 0) {
+ /* Fill in the SET_MC_CMD with the number of address bytes, followed
+ by the list of multicast addresses to be accepted. */
+ outw(SET_MC_CMD + 6, ioaddr + WRITE_PTR);
+ outw(num_addrs * 6, ioaddr);
+ outsw(ioaddr, addrs, num_addrs*3); /* 3 = addr len in words */
+ /* We must trigger a whole 586 reset due to a bug. */
+ } else {
+ /* Not written yet, this requires expanding the init_words config
+ cmd. */
+ outw(99, ioaddr); /* Disable promiscuous mode, use normal mode */
+ }
+#endif
+}
+
+/* The horrible routine to read a word from the serial EEPROM. */
+
+/* The delay between EEPROM clock transitions. */
+#define eeprom_delay() { int _i = 40; while (--_i > 0) { __SLOW_DOWN_IO; }}
+#define EE_READ_CMD (6 << 6)
+
+int
+read_eeprom(int ioaddr, int location)
+{
+ int i;
+ unsigned short retval = 0;
+ short ee_addr = ioaddr + EEPROM_Ctrl;
+ int read_cmd = location | EE_READ_CMD;
+ short ctrl_val = EE_CS | _586_RESET;
+
+ outb(ctrl_val, ee_addr);
+
+ /* Shift the read command bits out. */
+ for (i = 8; i >= 0; i--) {
+ short outval = (read_cmd & (1 << i)) ? ctrl_val | EE_DATA_WRITE
+ : ctrl_val;
+ outb(outval, ee_addr);
+ outb(outval | EE_SHIFT_CLK, ee_addr); /* EEPROM clock tick. */
+ eeprom_delay();
+ outb(outval, ee_addr); /* Finish EEPROM a clock tick. */
+ eeprom_delay();
+ }
+ outb(ctrl_val, ee_addr);
+
+ for (i = 16; i > 0; i--) {
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr); eeprom_delay();
+ retval = (retval << 1) | ((inb(ee_addr) & EE_DATA_READ) ? 1 : 0);
+ outb(ctrl_val, ee_addr); eeprom_delay();
+ }
+
+ /* Terminate the EEPROM access. */
+ ctrl_val &= ~EE_CS;
+ outb(ctrl_val | EE_SHIFT_CLK, ee_addr);
+ eeprom_delay();
+ outb(ctrl_val, ee_addr);
+ eeprom_delay();
+ return retval;
+}
+
+static void
+init_82586_mem(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ /* Enable loopback to protect the wire while starting up.
+ This is Superstition From Crynwr. */
+ outb(inb(ioaddr + Config) | 0x02, ioaddr + Config);
+
+ /* Hold the 586 in reset during the memory initialization. */
+ outb(_586_RESET, ioaddr + EEPROM_Ctrl);
+
+ /* Place the write pointer at 0xfff6 (address-aliased to 0xfffff6). */
+ outw(0xfff6, ioaddr + WRITE_PTR);
+ outsw(ioaddr, init_words, sizeof(init_words)>>1);
+
+ /* Fill in the station address. */
+ outw(SA_OFFSET, ioaddr + WRITE_PTR);
+ outsw(ioaddr, dev->dev_addr, 3);
+
+ /* The Tx-block list is written as needed. We just set up the values. */
+#ifdef initial_text_tx
+ lp->tx_cmd_link = DUMP_DATA + 4;
+#else
+ lp->tx_cmd_link = IDLELOOP + 4;
+#endif
+ lp->tx_head = lp->tx_reap = TX_BUF_START;
+
+ init_rx_bufs(dev);
+
+ /* Start the 586 by releasing the reset line. */
+ outb(0x00, ioaddr + EEPROM_Ctrl);
+
+ /* This was time consuming to track down: you need to give two channel
+ attention signals to reliably start up the i82586. */
+ outb(0, ioaddr + SIGNAL_CA);
+
+ {
+ int boguscnt = 50;
+ while (inw(ioaddr + SCB_STATUS) == 0)
+ if (--boguscnt == 0) {
+ printk("%s: i82586 initialization timed out with status %04x, cmd %04x.\n",
+ dev->name, inw(ioaddr + SCB_STATUS), inw(ioaddr + SCB_CMD));
+ break;
+ }
+ /* Issue channel-attn -- the 82586 won't start without it. */
+ outb(0, ioaddr + SIGNAL_CA);
+ }
+
+ /* Disable loopback. */
+ outb(inb(ioaddr + Config) & ~0x02, ioaddr + Config);
+ if (net_debug > 4)
+ printk("%s: Initialized 82586, status %04x.\n", dev->name,
+ inw(ioaddr + SCB_STATUS));
+ return;
+}
+
+/* Initialize the Rx-block list. */
+static void init_rx_bufs(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ int cur_rxbuf = lp->rx_head = RX_BUF_START;
+
+ /* Initialize each Rx frame + data buffer. */
+ do { /* While there is room for one more. */
+ outw(cur_rxbuf, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr); /* Status */
+ outw(0x0000, ioaddr); /* Command */
+ outw(cur_rxbuf + RX_BUF_SIZE, ioaddr); /* Link */
+ outw(cur_rxbuf + 22, ioaddr); /* Buffer offset */
+ outw(0xFeed, ioaddr); /* Pad for dest addr. */
+ outw(0xF00d, ioaddr);
+ outw(0xF001, ioaddr);
+ outw(0x0505, ioaddr); /* Pad for source addr. */
+ outw(0x2424, ioaddr);
+ outw(0x6565, ioaddr);
+ outw(0xdeaf, ioaddr); /* Pad for protocol. */
+
+ outw(0x0000, ioaddr); /* Buffer: Actual count */
+ outw(-1, ioaddr); /* Buffer: Next (none). */
+ outw(cur_rxbuf + 0x20, ioaddr); /* Buffer: Address low */
+ outw(0x0000, ioaddr);
+ /* Finally, the number of bytes in the buffer. */
+ outw(0x8000 + RX_BUF_SIZE-0x20, ioaddr);
+
+ lp->rx_tail = cur_rxbuf;
+ cur_rxbuf += RX_BUF_SIZE;
+ } while (cur_rxbuf <= RX_BUF_END - RX_BUF_SIZE);
+
+ /* Terminate the list by setting the EOL bit, and wrap the pointer to make
+ the list a ring. */
+ outw(lp->rx_tail + 2, ioaddr + WRITE_PTR);
+ outw(0xC000, ioaddr); /* Command, mark as last. */
+ outw(lp->rx_head, ioaddr); /* Link */
+}
+
+static void
+hardware_send_packet(struct device *dev, void *buf, short length)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short tx_block = lp->tx_head;
+
+ /* Set the write pointer to the Tx block, and put out the header. */
+ outw(tx_block, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr); /* Tx status */
+ outw(CMD_INTR|CmdTx, ioaddr); /* Tx command */
+ outw(tx_block+16, ioaddr); /* Next command is a NoOp. */
+ outw(tx_block+8, ioaddr); /* Data Buffer offset. */
+
+ /* Output the data buffer descriptor. */
+ outw(length | 0x8000, ioaddr); /* Byte count parameter. */
+ outw(-1, ioaddr); /* No next data buffer. */
+ outw(tx_block+22, ioaddr); /* Buffer follows the NoOp command. */
+ outw(0x0000, ioaddr); /* Buffer address high bits (always zero). */
+
+ /* Output the Loop-back NoOp command. */
+ outw(0x0000, ioaddr); /* Tx status */
+ outw(CmdNOp, ioaddr); /* Tx command */
+ outw(tx_block+16, ioaddr); /* Next is myself. */
+
+ /* Output the packet using the write pointer.
+ Hmmm, it feels a little like a 3c501! */
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+
+ /* Set the old command link pointing to this send packet. */
+ outw(lp->tx_cmd_link, ioaddr + WRITE_PTR);
+ outw(tx_block, ioaddr);
+ lp->tx_cmd_link = tx_block + 20;
+
+ /* Set the next free tx region. */
+ lp->tx_head = tx_block + TX_BUF_SIZE;
+ if (lp->tx_head > TX_BUF_END - TX_BUF_SIZE)
+ lp->tx_head = TX_BUF_START;
+
+ if (net_debug > 4) {
+ printk("%s: EExp @%x send length = %d, tx_block %3x, next %3x, "
+ "reap %4x status %4.4x.\n", dev->name, ioaddr, length,
+ tx_block, lp->tx_head, lp->tx_reap, inw(ioaddr + SCB_STATUS));
+ }
+
+ if (lp->tx_head != lp->tx_reap)
+ dev->tbusy = 0;
+}
+
+static void
+eexp_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short saved_write_ptr = inw(ioaddr + WRITE_PTR);
+ short rx_head = lp->rx_head;
+ short rx_tail = lp->rx_tail;
+ short boguscount = 10;
+ short frame_status;
+
+ /* Set the read pointer to the Rx frame. */
+ outw(rx_head, ioaddr + READ_PTR);
+ while ((frame_status = inw(ioaddr)) < 0) { /* Command complete */
+ short rfd_cmd = inw(ioaddr);
+ short next_rx_frame = inw(ioaddr);
+ short data_buffer_addr = inw(ioaddr);
+ short pkt_len;
+
+ /* Set the read pointer the data buffer. */
+ outw(data_buffer_addr, ioaddr + READ_PTR);
+ pkt_len = inw(ioaddr);
+
+ if (rfd_cmd != 0 || data_buffer_addr != rx_head + 22
+ || (pkt_len & 0xC000) != 0xC000) {
+ printk("%s: Rx frame at %#x corrupted, status %04x cmd %04x"
+ "next %04x data-buf @%04x %04x.\n", dev->name, rx_head,
+ frame_status, rfd_cmd, next_rx_frame, data_buffer_addr,
+ pkt_len);
+ } else if ((frame_status & 0x2000) == 0) {
+ /* Frame Rxed, but with error. */
+ lp->stats.rx_errors++;
+ if (frame_status & 0x0800) lp->stats.rx_crc_errors++;
+ if (frame_status & 0x0400) lp->stats.rx_frame_errors++;
+ if (frame_status & 0x0200) lp->stats.rx_fifo_errors++;
+ if (frame_status & 0x0100) lp->stats.rx_over_errors++;
+ if (frame_status & 0x0080) lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ pkt_len &= 0x3fff;
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2);
+
+ outw(data_buffer_addr + 10, ioaddr + READ_PTR);
+
+ insw(ioaddr, skb_put(skb,pkt_len), (pkt_len + 1) >> 1);
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+
+ /* Clear the status word and set End-of-List on the rx frame. */
+ outw(rx_head, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr);
+ outw(0xC000, ioaddr);
+#ifndef final_version
+ if (next_rx_frame != rx_head + RX_BUF_SIZE
+ && next_rx_frame != RX_BUF_START) {
+ printk("%s: Rx next frame at %#x is %#x instead of %#x.\n", dev->name,
+ rx_head, next_rx_frame, rx_head + RX_BUF_SIZE);
+ next_rx_frame = rx_head + RX_BUF_SIZE;
+ if (next_rx_frame >= RX_BUF_END - RX_BUF_SIZE)
+ next_rx_frame = RX_BUF_START;
+ }
+#endif
+ outw(rx_tail+2, ioaddr + WRITE_PTR);
+ outw(0x0000, ioaddr); /* Clear the end-of-list on the prev. RFD. */
+
+#ifndef final_version
+ outw(rx_tail+4, ioaddr + READ_PTR);
+ if (inw(ioaddr) != rx_head) {
+ printk("%s: Rx buf link mismatch, at %04x link %04x instead of %04x.\n",
+ dev->name, rx_tail, (outw(rx_tail+4, ioaddr + READ_PTR),inw(ioaddr)),
+ rx_head);
+ outw(rx_head, ioaddr);
+ }
+#endif
+
+ rx_tail = rx_head;
+ rx_head = next_rx_frame;
+ if (--boguscount == 0)
+ break;
+ outw(rx_head, ioaddr + READ_PTR);
+ }
+
+ lp->rx_head = rx_head;
+ lp->rx_tail = rx_tail;
+
+ /* Restore the original write pointer. */
+ outw(saved_write_ptr, ioaddr + WRITE_PTR);
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_eexpress = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, express_probe };
+
+
+static int irq=0x300;
+static int io=0;
+
+int
+init_module(void)
+{
+ if (io == 0)
+ printk("eexpress: You should not use auto-probing with insmod!\n");
+ dev_eexpress.base_addr=io;
+ dev_eexpress.irq=irq;
+ if (register_netdev(&dev_eexpress) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_eexpress);
+ kfree_s(dev_eexpress.priv,sizeof(struct net_local));
+ dev_eexpress.priv=NULL;
+
+ /* If we don't do this, we can't re-insmod it later. */
+ release_region(dev_eexpress.base_addr, EEXPRESS_IO_EXTENT);
+}
+#endif /* MODULE */
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -I/usr/src/linux/drivers/net -Wall -Wstrict-prototypes -O6 -m486 -c eexpress.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/eth16i.c b/i386/i386at/gpl/linux/net/eth16i.c
new file mode 100644
index 00000000..b21e4f33
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/eth16i.c
@@ -0,0 +1,1214 @@
+/* eth16i.c An ICL EtherTeam 16i and 32 EISA ethernet driver for Linux
+
+ Written 1994-95 by Mika Kuoppala
+
+ Copyright (C) 1994, 1995 by Mika Kuoppala
+ Based on skeleton.c and at1700.c by Donald Becker
+
+ This software may be used and distributed according to the terms
+ of the GNU Public Licence, incorporated herein by reference.
+
+ The author may be reached as miku@elt.icl.fi
+
+ This driver supports following cards :
+ - ICL EtherTeam 16i
+ - ICL EtherTeam 32 EISA
+
+ Sources:
+ - skeleton.c a sample network driver core for linux,
+ written by Donald Becker <becker@CESDIS.gsfc.nasa.gov>
+ - at1700.c a driver for Allied Telesis AT1700, written
+ by Donald Becker.
+ - e16iSRV.asm a Netware 3.X Server Driver for ICL EtherTeam16i
+ written by Markku Viima
+ - The Fujitsu MB86965 databook.
+
+ Valuable assistance from:
+ Markku Viima (ICL)
+ Ari Valve (ICL)
+
+ Revision history:
+
+ Version Date Description
+
+ 0.01 15.12-94 Initial version (card detection)
+ 0.02 23.01-95 Interrupt is now hooked correctly
+ 0.03 01.02-95 Rewrote initialization part
+ 0.04 07.02-95 Base skeleton done...
+ Made a few changes to signature checking
+ to make it a bit reliable.
+ - fixed bug in tx_buf mapping
+ - fixed bug in initialization (DLC_EN
+ wasn't enabled when initialization
+ was done.)
+ 0.05 08.02-95 If there were more than one packet to send,
+ transmit was jammed due to invalid
+ register write...now fixed
+ 0.06 19.02-95 Rewrote interrupt handling
+ 0.07 13.04-95 Wrote EEPROM read routines
+ Card configuration now set according to
+ data read from EEPROM
+ 0.08 23.06-95 Wrote part that tries to probe used interface
+ port if AUTO is selected
+
+ 0.09 01.09-95 Added module support
+
+ 0.10 04.09-95 Fixed receive packet allocation to work
+ with kernels > 1.3.x
+
+ 0.20 20.09-95 Added support for EtherTeam32 EISA
+
+ 0.21 17.10-95 Removed the unnecessary extern
+ init_etherdev() declaration. Some
+ other cleanups.
+ Bugs:
+ In some cases the interface autoprobing code doesn't find
+ the correct interface type. In this case you can
+ manually choose the interface type in DOS with E16IC.EXE which is
+ configuration software for EtherTeam16i and EtherTeam32 cards.
+
+ To do:
+ - Real multicast support
+*/
+
+static char *version =
+ "eth16i.c: v0.21 17-10-95 Mika Kuoppala (miku@elt.icl.fi)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+/* Few macros */
+#define BIT(a) ( (1 << (a)) )
+#define BITSET(ioaddr, bnum) ((outb(((inb(ioaddr)) | (bnum)), ioaddr)))
+#define BITCLR(ioaddr, bnum) ((outb(((inb(ioaddr)) & (~(bnum))), ioaddr)))
+
+/* This is the I/O address space for Etherteam 16i adapter. */
+#define ETH16I_IO_EXTENT 32
+
+/* Ticks before deciding that transmit has timed out */
+#define TIMEOUT_TICKS 30
+
+/* Maximum loop count when receiving packets */
+#define MAX_RX_LOOP 40
+
+/* Some interrupt masks */
+#define ETH16I_INTR_ON 0x8f82
+#define ETH16I_INTR_OFF 0x0000
+
+/* Buffers header status byte meanings */
+#define PKT_GOOD BIT(5)
+#define PKT_GOOD_RMT BIT(4)
+#define PKT_SHORT BIT(3)
+#define PKT_ALIGN_ERR BIT(2)
+#define PKT_CRC_ERR BIT(1)
+#define PKT_RX_BUF_OVERFLOW BIT(0)
+
+/* Transmit status register (DLCR0) */
+#define TX_STATUS_REG 0
+#define TX_DONE BIT(7)
+#define NET_BUSY BIT(6)
+#define TX_PKT_RCD BIT(5)
+#define CR_LOST BIT(4)
+#define COLLISION BIT(2)
+#define COLLISIONS_16 BIT(1)
+
+/* Receive status register (DLCR1) */
+#define RX_STATUS_REG 1
+#define RX_PKT BIT(7) /* Packet received */
+#define BUS_RD_ERR BIT(6)
+#define SHORT_PKT_ERR BIT(3)
+#define ALIGN_ERR BIT(2)
+#define CRC_ERR BIT(1)
+#define RX_BUF_OVERFLOW BIT(0)
+
+/* Transmit Interrupt Enable Register (DLCR2) */
+#define TX_INTR_REG 2
+#define TX_INTR_DONE BIT(7)
+#define TX_INTR_COL BIT(2)
+#define TX_INTR_16_COL BIT(1)
+
+/* Receive Interrupt Enable Register (DLCR3) */
+#define RX_INTR_REG 3
+#define RX_INTR_RECEIVE BIT(7)
+#define RX_INTR_SHORT_PKT BIT(3)
+#define RX_INTR_CRC_ERR BIT(1)
+#define RX_INTR_BUF_OVERFLOW BIT(0)
+
+/* Transmit Mode Register (DLCR4) */
+#define TRANSMIT_MODE_REG 4
+#define LOOPBACK_CONTROL BIT(1)
+#define CONTROL_OUTPUT BIT(2)
+
+/* Receive Mode Register (DLCR5) */
+#define RECEIVE_MODE_REG 5
+#define RX_BUFFER_EMPTY BIT(6)
+#define ACCEPT_BAD_PACKETS BIT(5)
+#define RECEIVE_SHORT_ADDR BIT(4)
+#define ACCEPT_SHORT_PACKETS BIT(3)
+#define REMOTE_RESET BIT(2)
+
+#define ADDRESS_FILTER_MODE BIT(1) | BIT(0)
+#define REJECT_ALL 0
+#define ACCEPT_ALL 3
+#define MODE_1 1 /* NODE ID, BC, MC, 2-24th bit */
+#define MODE_2 2 /* NODE ID, BC, MC, Hash Table */
+
+/* Configuration Register 0 (DLCR6) */
+#define CONFIG_REG_0 6
+#define DLC_EN BIT(7)
+#define SRAM_CYCLE_TIME_100NS BIT(6)
+#define SYSTEM_BUS_WIDTH_8 BIT(5) /* 1 = 8bit, 0 = 16bit */
+#define BUFFER_WIDTH_8 BIT(4) /* 1 = 8bit, 0 = 16bit */
+#define TBS1 BIT(3)
+#define TBS0 BIT(2)
+#define BS1 BIT(1) /* 00=8kb, 01=16kb */
+#define BS0 BIT(0) /* 10=32kb, 11=64kb */
+
+#ifndef ETH16I_TX_BUF_SIZE /* 0 = 2kb, 1 = 4kb */
+#define ETH16I_TX_BUF_SIZE 2 /* 2 = 8kb, 3 = 16kb */
+#endif
+#define TX_BUF_1x2048 0
+#define TX_BUF_2x2048 1
+#define TX_BUF_2x4098 2
+#define TX_BUF_2x8192 3
+
+/* Configuration Register 1 (DLCR7) */
+#define CONFIG_REG_1 7
+#define POWERUP BIT(5)
+
+/* Transmit start register */
+#define TRANSMIT_START_REG 10
+#define TRANSMIT_START_RB 2
+#define TX_START BIT(7) /* Rest of register bit indicate*/
+ /* number of packets in tx buffer*/
+/* Node ID registers (DLCR8-13) */
+#define NODE_ID_0 8
+#define NODE_ID_RB 0
+
+/* Hash Table registers (HT8-15) */
+#define HASH_TABLE_0 8
+#define HASH_TABLE_RB 1
+
+/* Buffer memory ports */
+#define BUFFER_MEM_PORT_LB 8
+#define DATAPORT BUFFER_MEM_PORT_LB
+#define BUFFER_MEM_PORT_HB 9
+
+/* 16 Collision control register (BMPR11) */
+#define COL_16_REG 11
+#define HALT_ON_16 0x00
+#define RETRANS_AND_HALT_ON_16 0x02
+
+/* DMA Burst and Transceiver Mode Register (BMPR13) */
+#define TRANSCEIVER_MODE_REG 13
+#define TRANSCEIVER_MODE_RB 2
+#define IO_BASE_UNLOCK BIT(7)
+#define LOWER_SQUELCH_TRESH BIT(6)
+#define LINK_TEST_DISABLE BIT(5)
+#define AUI_SELECT BIT(4)
+#define DIS_AUTO_PORT_SEL BIT(3)
+
+/* Filter Self Receive Register (BMPR14) */
+#define FILTER_SELF_RX_REG 14
+#define SKIP_RECEIVE_PACKET BIT(2)
+#define FILTER_SELF_RECEIVE BIT(0)
+#define RX_BUF_SKIP_PACKET SKIP_RECEIVE_PACKET | FILTER_SELF_RECEIVE
+
+/* EEPROM Control Register (BMPR 16) */
+#define EEPROM_CTRL_REG 16
+
+/* EEPROM Data Register (BMPR 17) */
+#define EEPROM_DATA_REG 17
+
+/* NMC93CSx6 EEPROM Control Bits */
+#define CS_0 0x00
+#define CS_1 0x20
+#define SK_0 0x00
+#define SK_1 0x40
+#define DI_0 0x00
+#define DI_1 0x80
+
+/* NMC93CSx6 EEPROM Instructions */
+#define EEPROM_READ 0x80
+
+/* NMC93CSx6 EEPROM Addresses */
+#define E_NODEID_0 0x02
+#define E_NODEID_1 0x03
+#define E_NODEID_2 0x04
+#define E_PORT_SELECT 0x14
+ #define E_PORT_BNC 0
+ #define E_PORT_DIX 1
+ #define E_PORT_TP 2
+ #define E_PORT_AUTO 3
+#define E_PRODUCT_CFG 0x30
+
+
+/* Macro to slow down io between EEPROM clock transitions */
+#define eeprom_slow_io() do { int _i = 40; while(--_i > 0) { __SLOW_DOWN_IO; }}while(0)
+
+/* Jumperless Configuration Register (BMPR19) */
+#define JUMPERLESS_CONFIG 19
+
+/* ID ROM registers, writing to them also resets some parts of chip */
+#define ID_ROM_0 24
+#define ID_ROM_7 31
+#define RESET ID_ROM_0
+
+/* This is the I/O address list to be probed when seeking the card */
+static unsigned int eth16i_portlist[] =
+ { 0x260, 0x280, 0x2A0, 0x240, 0x340, 0x320, 0x380, 0x300, 0 };
+
+static unsigned int eth32i_portlist[] =
+ { 0x1000, 0x2000, 0x3000, 0x4000, 0x5000, 0x6000, 0x7000, 0x8000,
+ 0x9000, 0xA000, 0xB000, 0xC000, 0xD000, 0xE000, 0xF000, 0 };
+
+/* This is the Interrupt lookup table for Eth16i card */
+static unsigned int eth16i_irqmap[] = { 9, 10, 5, 15 };
+
+/* This is the Interrupt lookup table for Eth32i card */
+static unsigned int eth32i_irqmap[] = { 3, 5, 7, 9, 10, 11, 12, 15 };
+#define EISA_IRQ_REG 0xc89
+
+static unsigned int eth16i_tx_buf_map[] = { 2048, 2048, 4096, 8192 };
+unsigned int boot = 1;
+
+/* Use 0 for production, 1 for verification, >2 for debug */
+#ifndef ETH16I_DEBUG
+#define ETH16I_DEBUG 0
+#endif
+static unsigned int eth16i_debug = ETH16I_DEBUG;
+
+/* Information for each board */
+struct eth16i_local {
+ struct enet_statistics stats;
+ unsigned int tx_started:1;
+ unsigned char tx_queue; /* Number of packets in transmit buffer */
+ unsigned short tx_queue_len;
+ unsigned int tx_buf_size;
+ unsigned long open_time;
+};
+
+/* Function prototypes */
+
+extern int eth16i_probe(struct device *dev);
+
+static int eth16i_probe1(struct device *dev, short ioaddr);
+static int eth16i_check_signature(short ioaddr);
+static int eth16i_probe_port(short ioaddr);
+static void eth16i_set_port(short ioaddr, int porttype);
+static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l);
+static int eth16i_receive_probe_packet(short ioaddr);
+static int eth16i_get_irq(short ioaddr);
+static int eth16i_read_eeprom(int ioaddr, int offset);
+static int eth16i_read_eeprom_word(int ioaddr);
+static void eth16i_eeprom_cmd(int ioaddr, unsigned char command);
+static int eth16i_open(struct device *dev);
+static int eth16i_close(struct device *dev);
+static int eth16i_tx(struct sk_buff *skb, struct device *dev);
+static void eth16i_rx(struct device *dev);
+static void eth16i_interrupt(int irq, struct pt_regs *regs);
+static void eth16i_multicast(struct device *dev, int num_addrs, void *addrs);
+static void eth16i_select_regbank(unsigned char regbank, short ioaddr);
+static void eth16i_initialize(struct device *dev);
+static struct enet_statistics *eth16i_get_stats(struct device *dev);
+
+static char *cardname = "ICL EtherTeam 16i/32";
+
+#ifdef HAVE_DEVLIST
+/* Support for alternate probe manager */
+/struct netdev_entry eth16i_drv =
+ {"eth16i", eth16i_probe1, ETH16I_IO_EXTENT, eth16i_probe_list};
+
+#else /* Not HAVE_DEVLIST */
+int eth16i_probe(struct device *dev)
+{
+ int i;
+ int ioaddr;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if(eth16i_debug > 4)
+ printk("Probing started for %s\n", cardname);
+
+ if(base_addr > 0x1ff) /* Check only single location */
+ return eth16i_probe1(dev, base_addr);
+ else if(base_addr != 0) /* Don't probe at all */
+ return ENXIO;
+
+ /* Seek card from the ISA io address space */
+ for(i = 0; (ioaddr = eth16i_portlist[i]) ; i++) {
+ if(check_region(ioaddr, ETH16I_IO_EXTENT))
+ continue;
+ if(eth16i_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ /* Seek card from the EISA io address space */
+ for(i = 0; (ioaddr = eth32i_portlist[i]) ; i++) {
+ if(check_region(ioaddr, ETH16I_IO_EXTENT))
+ continue;
+ if(eth16i_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif /* Not HAVE_DEVLIST */
+
+static int eth16i_probe1(struct device *dev, short ioaddr)
+{
+ static unsigned version_printed = 0;
+ unsigned int irq = 0;
+ boot = 1; /* To inform initilization that we are in boot probe */
+
+ /*
+ The MB86985 chip has on register which holds information in which
+ io address the chip lies. First read this register and compare
+ it to our current io address and if match then this could
+ be our chip.
+ */
+
+ if(ioaddr < 0x1000) {
+ if(eth16i_portlist[(inb(ioaddr + JUMPERLESS_CONFIG) & 0x07)] != ioaddr)
+ return -ENODEV;
+ }
+
+ /* Now we will go a bit deeper and try to find the chip's signature */
+
+ if(eth16i_check_signature(ioaddr) != 0) /* Can we find the signature here */
+ return -ENODEV;
+
+ /*
+ Now it seems that we have found a ethernet chip in this particular
+ ioaddr. The MB86985 chip has this feature, that when you read a
+ certain register it will increase it's io base address to next
+ configurable slot. Now when we have found the chip, first thing is
+ to make sure that the chip's ioaddr will hold still here.
+ */
+
+ eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+ outb(0x00, ioaddr + TRANSCEIVER_MODE_REG);
+
+ outb(0x00, ioaddr + RESET); /* Will reset some parts of chip */
+ BITSET(ioaddr + CONFIG_REG_0, BIT(7)); /* This will disable the data link */
+
+ if(dev == NULL)
+ dev = init_etherdev(0, sizeof(struct eth16i_local));
+
+ if( (eth16i_debug & version_printed++) == 0)
+ printk(version);
+
+ dev->base_addr = ioaddr;
+
+ irq = eth16i_get_irq(ioaddr);
+ dev->irq = irq;
+
+ /* Try to obtain interrupt vector */
+ if(request_irq(dev->irq, &eth16i_interrupt, 0, "eth16i")) {
+ printk("%s: %s at %#3x, but is unusable due
+ conflict on IRQ %d.\n", dev->name, cardname, ioaddr, irq);
+ return EAGAIN;
+ }
+
+ printk("%s: %s at %#3x, IRQ %d, ",
+ dev->name, cardname, ioaddr, dev->irq);
+
+ /* Let's grab the region */
+ request_region(ioaddr, ETH16I_IO_EXTENT, "eth16i");
+
+ /* Now we will have to lock the chip's io address */
+ eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+ outb(0x38, ioaddr + TRANSCEIVER_MODE_REG);
+
+ eth16i_initialize(dev); /* Initialize rest of the chip's registers */
+
+ /* Now let's same some energy by shutting down the chip ;) */
+ BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
+
+ /* Initialize the device structure */
+ if(dev->priv == NULL)
+ dev->priv = kmalloc(sizeof(struct eth16i_local), GFP_KERNEL);
+ memset(dev->priv, 0, sizeof(struct eth16i_local));
+
+ dev->open = eth16i_open;
+ dev->stop = eth16i_close;
+ dev->hard_start_xmit = eth16i_tx;
+ dev->get_stats = eth16i_get_stats;
+ dev->set_multicast_list = &eth16i_multicast;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ boot = 0;
+
+ return 0;
+}
+
+
+static void eth16i_initialize(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int i, node_w = 0;
+ unsigned char node_byte = 0;
+
+ /* Setup station address */
+ eth16i_select_regbank(NODE_ID_RB, ioaddr);
+ for(i = 0 ; i < 3 ; i++) {
+ unsigned short node_val = eth16i_read_eeprom(ioaddr, E_NODEID_0 + i);
+ ((unsigned short *)dev->dev_addr)[i] = ntohs(node_val);
+ }
+
+ for(i = 0; i < 6; i++) {
+ outb( ((unsigned char *)dev->dev_addr)[i], ioaddr + NODE_ID_0 + i);
+ if(boot) {
+ printk("%02x", inb(ioaddr + NODE_ID_0 + i));
+ if(i != 5)
+ printk(":");
+ }
+ }
+
+ /* Now we will set multicast addresses to accept none */
+ eth16i_select_regbank(HASH_TABLE_RB, ioaddr);
+ for(i = 0; i < 8; i++)
+ outb(0x00, ioaddr + HASH_TABLE_0 + i);
+
+ /*
+ Now let's disable the transmitter and receiver, set the buffer ram
+ cycle time, bus width and buffer data path width. Also we shall
+ set transmit buffer size and total buffer size.
+ */
+
+ eth16i_select_regbank(2, ioaddr);
+
+ node_byte = 0;
+ node_w = eth16i_read_eeprom(ioaddr, E_PRODUCT_CFG);
+
+ if( (node_w & 0xFF00) == 0x0800)
+ node_byte |= BUFFER_WIDTH_8;
+
+ node_byte |= BS1;
+
+ if( (node_w & 0x00FF) == 64)
+ node_byte |= BS0;
+
+ node_byte |= DLC_EN | SRAM_CYCLE_TIME_100NS | (ETH16I_TX_BUF_SIZE << 2);
+
+ outb(node_byte, ioaddr + CONFIG_REG_0);
+
+ /* We shall halt the transmitting, if 16 collisions are detected */
+ outb(RETRANS_AND_HALT_ON_16, ioaddr + COL_16_REG);
+
+ if(boot) /* Now set port type */
+ {
+ char *porttype[] = {"BNC", "DIX", "TP", "AUTO"};
+
+ ushort ptype = eth16i_read_eeprom(ioaddr, E_PORT_SELECT);
+ dev->if_port = (ptype & 0x00FF);
+
+ printk(" %s interface.\n", porttype[dev->if_port]);
+
+ if(ptype == E_PORT_AUTO)
+ ptype = eth16i_probe_port(ioaddr);
+
+ eth16i_set_port(ioaddr, ptype);
+ }
+
+ /* Set Receive Mode to normal operation */
+ outb(MODE_2, ioaddr + RECEIVE_MODE_REG);
+}
+
+static int eth16i_probe_port(short ioaddr)
+{
+ int i;
+ int retcode;
+ unsigned char dummy_packet[64] = { 0 };
+
+ /* Powerup the chip */
+ outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
+
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ eth16i_select_regbank(NODE_ID_RB, ioaddr);
+
+ for(i = 0; i < 6; i++) {
+ dummy_packet[i] = inb(ioaddr + NODE_ID_0 + i);
+ dummy_packet[i+6] = inb(ioaddr + NODE_ID_0 + i);
+ }
+
+ dummy_packet[12] = 0x00;
+ dummy_packet[13] = 0x04;
+
+ eth16i_select_regbank(2, ioaddr);
+
+ for(i = 0; i < 3; i++) {
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+ BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+ eth16i_set_port(ioaddr, i);
+
+ if(eth16i_debug > 1)
+ printk("Set port number %d\n", i);
+
+ retcode = eth16i_send_probe_packet(ioaddr, dummy_packet, 64);
+ if(retcode == 0) {
+ retcode = eth16i_receive_probe_packet(ioaddr);
+ if(retcode != -1) {
+ if(eth16i_debug > 1)
+ printk("Eth16i interface port found at %d\n", i);
+ return i;
+ }
+ }
+ else {
+ if(eth16i_debug > 1)
+ printk("TRANSMIT_DONE timeout\n");
+ }
+ }
+
+ if( eth16i_debug > 1)
+ printk("Using default port\n");
+
+ return E_PORT_BNC;
+}
+
+static void eth16i_set_port(short ioaddr, int porttype)
+{
+ unsigned short temp = 0;
+
+ eth16i_select_regbank(TRANSCEIVER_MODE_RB, ioaddr);
+ outb(LOOPBACK_CONTROL, ioaddr + TRANSMIT_MODE_REG);
+
+ temp |= DIS_AUTO_PORT_SEL;
+
+ switch(porttype) {
+
+ case E_PORT_BNC :
+ temp |= AUI_SELECT;
+ break;
+
+ case E_PORT_TP :
+ break;
+
+ case E_PORT_DIX :
+ temp |= AUI_SELECT;
+ BITSET(ioaddr + TRANSMIT_MODE_REG, CONTROL_OUTPUT);
+ break;
+ }
+ outb(temp, ioaddr + TRANSCEIVER_MODE_REG);
+
+ if(eth16i_debug > 1) {
+ printk("TRANSMIT_MODE_REG = %x\n", inb(ioaddr + TRANSMIT_MODE_REG));
+ printk("TRANSCEIVER_MODE_REG = %x\n", inb(ioaddr+TRANSCEIVER_MODE_REG));
+ }
+}
+
+static int eth16i_send_probe_packet(short ioaddr, unsigned char *b, int l)
+{
+ int starttime;
+
+ outb(0xff, ioaddr + TX_STATUS_REG);
+
+ outw(l, ioaddr + DATAPORT);
+ outsw(ioaddr + DATAPORT, (unsigned short *)b, (l + 1) >> 1);
+
+ starttime = jiffies;
+ outb(TX_START | 1, ioaddr + TRANSMIT_START_REG);
+
+ while( (inb(ioaddr + TX_STATUS_REG) & 0x80) == 0) {
+ if( (jiffies - starttime) > TIMEOUT_TICKS) {
+ break;
+ }
+ }
+
+ return(0);
+}
+
+static int eth16i_receive_probe_packet(short ioaddr)
+{
+ int starttime;
+
+ starttime = jiffies;
+
+ while((inb(ioaddr + TX_STATUS_REG) & 0x20) == 0) {
+ if( (jiffies - starttime) > TIMEOUT_TICKS) {
+
+ if(eth16i_debug > 1)
+ printk("Timeout occured waiting transmit packet received\n");
+ starttime = jiffies;
+ while((inb(ioaddr + RX_STATUS_REG) & 0x80) == 0) {
+ if( (jiffies - starttime) > TIMEOUT_TICKS) {
+ if(eth16i_debug > 1)
+ printk("Timeout occured waiting receive packet\n");
+ return -1;
+ }
+ }
+
+ if(eth16i_debug > 1)
+ printk("RECEIVE_PACKET\n");
+ return(0); /* Found receive packet */
+ }
+ }
+
+ if(eth16i_debug > 1) {
+ printk("TRANSMIT_PACKET_RECEIVED %x\n", inb(ioaddr + TX_STATUS_REG));
+ printk("RX_STATUS_REG = %x\n", inb(ioaddr + RX_STATUS_REG));
+ }
+
+ return(0); /* Return success */
+}
+
+static int eth16i_get_irq(short ioaddr)
+{
+ unsigned char cbyte;
+
+ if( ioaddr < 0x1000) {
+ cbyte = inb(ioaddr + JUMPERLESS_CONFIG);
+ return( eth16i_irqmap[ ((cbyte & 0xC0) >> 6) ] );
+ } else { /* Oh..the card is EISA so method getting IRQ different */
+ unsigned short index = 0;
+ cbyte = inb(ioaddr + EISA_IRQ_REG);
+ while( (cbyte & 0x01) == 0) {
+ cbyte = cbyte >> 1;
+ index++;
+ }
+ return( eth32i_irqmap[ index ] );
+ }
+}
+
+static int eth16i_check_signature(short ioaddr)
+{
+ int i;
+ unsigned char creg[4] = { 0 };
+
+ for(i = 0; i < 4 ; i++) {
+
+ creg[i] = inb(ioaddr + TRANSMIT_MODE_REG + i);
+
+ if(eth16i_debug > 1)
+ printk("eth16i: read signature byte %x at %x\n", creg[i],
+ ioaddr + TRANSMIT_MODE_REG + i);
+ }
+
+ creg[0] &= 0x0F; /* Mask collision cnr */
+ creg[2] &= 0x7F; /* Mask DCLEN bit */
+
+#ifdef 0
+/*
+ This was removed because the card was sometimes left to state
+ from which it couldn't be find anymore. If there is need
+ to more strict chech still this have to be fixed.
+*/
+ if( !( (creg[0] == 0x06) && (creg[1] == 0x41)) ) {
+ if(creg[1] != 0x42)
+ return -1;
+ }
+#endif
+
+ if( !( (creg[2] == 0x36) && (creg[3] == 0xE0)) ) {
+ creg[2] &= 0x42;
+ creg[3] &= 0x03;
+
+ if( !( (creg[2] == 0x42) && (creg[3] == 0x00)) )
+ return -1;
+ }
+
+ if(eth16i_read_eeprom(ioaddr, E_NODEID_0) != 0)
+ return -1;
+ if((eth16i_read_eeprom(ioaddr, E_NODEID_1) & 0xFF00) != 0x4B00)
+ return -1;
+
+ return 0;
+}
+
+static int eth16i_read_eeprom(int ioaddr, int offset)
+{
+ int data = 0;
+
+ eth16i_eeprom_cmd(ioaddr, EEPROM_READ | offset);
+ outb(CS_1, ioaddr + EEPROM_CTRL_REG);
+ data = eth16i_read_eeprom_word(ioaddr);
+ outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
+
+ return(data);
+}
+
+static int eth16i_read_eeprom_word(int ioaddr)
+{
+ int i;
+ int data = 0;
+
+ for(i = 16; i > 0; i--) {
+ outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ data = (data << 1) | ((inb(ioaddr + EEPROM_DATA_REG) & DI_1) ? 1 : 0);
+ eeprom_slow_io();
+ }
+
+ return(data);
+}
+
+static void eth16i_eeprom_cmd(int ioaddr, unsigned char command)
+{
+ int i;
+
+ outb(CS_0 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ outb(DI_0, ioaddr + EEPROM_DATA_REG);
+ outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ outb(DI_1, ioaddr + EEPROM_DATA_REG);
+ outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+
+ for(i = 7; i >= 0; i--) {
+ short cmd = ( (command & (1 << i)) ? DI_1 : DI_0 );
+ outb(cmd, ioaddr + EEPROM_DATA_REG);
+ outb(CS_1 | SK_0, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ outb(CS_1 | SK_1, ioaddr + EEPROM_CTRL_REG);
+ eeprom_slow_io();
+ }
+}
+
+static int eth16i_open(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Powerup the chip */
+ outb(0xc0 | POWERUP, ioaddr + CONFIG_REG_1);
+
+ /* Initialize the chip */
+ eth16i_initialize(dev);
+
+ /* Set the transmit buffer size */
+ lp->tx_buf_size = eth16i_tx_buf_map[ETH16I_TX_BUF_SIZE & 0x03];
+
+ if(eth16i_debug > 3)
+ printk("%s: transmit buffer size %d\n", dev->name, lp->tx_buf_size);
+
+ /* Now enable Transmitter and Receiver sections */
+ BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ /* Now switch to register bank 2, for run time operation */
+ eth16i_select_regbank(2, ioaddr);
+
+ lp->open_time = jiffies;
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
+ /* Turn on interrupts*/
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef MODULE
+ MOD_INC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static int eth16i_close(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ lp->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Disable transmit and receive */
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ /* Reset the chip */
+ outb(0xff, ioaddr + RESET);
+
+ /* Save some energy by switching off power */
+ BITCLR(ioaddr + CONFIG_REG_1, POWERUP);
+
+#ifdef MODULE
+ MOD_DEC_USE_COUNT;
+#endif
+
+ return 0;
+}
+
+static int eth16i_tx(struct sk_buff *skb, struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ if(dev->tbusy) {
+ /*
+ If we get here, some higher level has decided that we are broken.
+ There should really be a "kick me" function call instead.
+ */
+
+ int tickssofar = jiffies - dev->trans_start;
+ if(tickssofar < TIMEOUT_TICKS) /* Let's not rush with our timeout, */
+ return 1; /* wait a couple of ticks first */
+
+ printk("%s: transmit timed out with status %04x, %s ?\n", dev->name,
+ inw(ioaddr + TX_STATUS_REG),
+ (inb(ioaddr + TX_STATUS_REG) & TX_DONE) ?
+ "IRQ conflict" : "network cable problem");
+
+ /* Let's dump all registers */
+ if(eth16i_debug > 0) {
+ printk("%s: timeout regs: %02x %02x %02x %02x %02x %02x %02x %02x.\n",
+ dev->name, inb(ioaddr + 0), inb(ioaddr + 1), inb(ioaddr + 2),
+ inb(ioaddr + 3), inb(ioaddr + 4), inb(ioaddr + 5),
+ inb(ioaddr + 6), inb(ioaddr + 7));
+
+
+ printk("lp->tx_queue = %d\n", lp->tx_queue);
+ printk("lp->tx_queue_len = %d\n", lp->tx_queue_len);
+ printk("lp->tx_started = %d\n", lp->tx_started);
+
+ }
+
+ lp->stats.tx_errors++;
+
+ /* Now let's try to restart the adaptor */
+
+ BITSET(ioaddr + CONFIG_REG_0, DLC_EN);
+ outw(0xffff, ioaddr + RESET);
+ eth16i_initialize(dev);
+ outw(0xffff, ioaddr + TX_STATUS_REG);
+ BITCLR(ioaddr + CONFIG_REG_0, DLC_EN);
+
+ lp->tx_started = 0;
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ dev->tbusy = 0;
+ dev->trans_start = jiffies;
+ }
+
+ /*
+ If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself
+ */
+ if(skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer based transmitter from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+
+ /* Turn off TX interrupts */
+ outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
+
+ if(set_bit(0, (void *)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ outw(length, ioaddr + DATAPORT);
+
+ if( ioaddr < 0x1000 )
+ outsw(ioaddr + DATAPORT, buf, (length + 1) >> 1);
+ else {
+ unsigned char frag = length % 4;
+
+ outsl(ioaddr + DATAPORT, buf, length >> 2);
+
+ if( frag != 0 ) {
+ outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC)), 1);
+ if( frag == 3 )
+ outsw(ioaddr + DATAPORT, (buf + (length & 0xFFFC) + 2), 1);
+ }
+ }
+
+ lp->tx_queue++;
+ lp->tx_queue_len += length + 2;
+
+ if(lp->tx_started == 0) {
+ /* If the transmitter is idle..always trigger a transmit */
+ outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ lp->tx_started = 1;
+ dev->tbusy = 0;
+ }
+ else if(lp->tx_queue_len < lp->tx_buf_size - (ETH_FRAME_LEN + 2)) {
+ /* There is still more room for one more packet in tx buffer */
+ dev->tbusy = 0;
+ }
+
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ /* Turn TX interrupts back on */
+ /* outb(TX_INTR_DONE | TX_INTR_16_COL, ioaddr + TX_INTR_REG); */
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+static void eth16i_rx(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = MAX_RX_LOOP;
+
+ /* Loop until all packets have been read */
+ while( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) {
+
+ /* Read status byte from receive buffer */
+ ushort status = inw(ioaddr + DATAPORT);
+
+ if(eth16i_debug > 4)
+ printk("%s: Receiving packet mode %02x status %04x.\n",
+ dev->name, inb(ioaddr + RECEIVE_MODE_REG), status);
+
+ if( !(status & PKT_GOOD) ) {
+ /* Hmm..something went wrong. Let's check what error occured */
+ lp->stats.rx_errors++;
+ if( status & PKT_SHORT ) lp->stats.rx_length_errors++;
+ if( status & PKT_ALIGN_ERR ) lp->stats.rx_frame_errors++;
+ if( status & PKT_CRC_ERR ) lp->stats.rx_crc_errors++;
+ if( status & PKT_RX_BUF_OVERFLOW) lp->stats.rx_over_errors++;
+ }
+ else { /* Ok so now we should have a good packet */
+ struct sk_buff *skb;
+
+ /* Get the size of the packet from receive buffer */
+ ushort pkt_len = inw(ioaddr + DATAPORT);
+
+ if(pkt_len > ETH_FRAME_LEN) {
+ printk("%s: %s claimed a very large packet, size of %d bytes.\n",
+ dev->name, cardname, pkt_len);
+ outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb = dev_alloc_skb(pkt_len + 3);
+ if( skb == NULL ) {
+ printk("%s: Could'n allocate memory for packet (len %d)\n",
+ dev->name, pkt_len);
+ outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
+ lp->stats.rx_dropped++;
+ break;
+ }
+
+ skb->dev = dev;
+ skb_reserve(skb,2);
+ /*
+ Now let's get the packet out of buffer.
+ size is (pkt_len + 1) >> 1, cause we are now reading words
+ and it have to be even aligned.
+ */
+
+ if( ioaddr < 0x1000)
+ insw(ioaddr + DATAPORT, skb_put(skb, pkt_len), (pkt_len + 1) >> 1);
+ else {
+ unsigned char *buf = skb_put(skb, pkt_len);
+ unsigned char frag = pkt_len % 4;
+
+ insl(ioaddr + DATAPORT, buf, pkt_len >> 2);
+
+ if(frag != 0) {
+ unsigned short rest[2];
+ rest[0] = inw( ioaddr + DATAPORT );
+ if(frag == 3)
+ rest[1] = inw( ioaddr + DATAPORT );
+
+ memcpy(buf + (pkt_len & 0xfffc), (char *)rest, frag);
+ }
+ }
+
+ skb->protocol=eth_type_trans(skb, dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+
+ if( eth16i_debug > 5 ) {
+ int i;
+ printk("%s: Received packet of length %d.\n", dev->name, pkt_len);
+ for(i = 0; i < 14; i++)
+ printk(" %02x", skb->data[i]);
+ printk(".\n");
+ }
+
+ } /* else */
+
+ if(--boguscount <= 0)
+ break;
+
+ } /* while */
+
+#if 0
+ {
+ int i;
+
+ for(i = 0; i < 20; i++) {
+ if( (inb(ioaddr+RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == RX_BUFFER_EMPTY)
+ break;
+ inw(ioaddr + DATAPORT);
+ outb(RX_BUF_SKIP_PACKET, ioaddr + FILTER_SELF_RX_REG);
+ }
+
+ if(eth16i_debug > 1)
+ printk("%s: Flushed receive buffer.\n", dev->name);
+ }
+#endif
+
+ return;
+}
+
+static void eth16i_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct eth16i_local *lp;
+ int ioaddr = 0,
+ status;
+
+ if(dev == NULL) {
+ printk("eth16i_interrupt(): irq %d for unknown device. \n", irq);
+ return;
+ }
+
+ /* Turn off all interrupts from adapter */
+ outw(ETH16I_INTR_OFF, ioaddr + TX_INTR_REG);
+
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct eth16i_local *)dev->priv;
+ status = inw(ioaddr + TX_STATUS_REG); /* Get the status */
+ outw(status, ioaddr + TX_STATUS_REG); /* Clear status bits */
+
+ if(eth16i_debug > 3)
+ printk("%s: Interrupt with status %04x.\n", dev->name, status);
+
+ if( status & 0x00ff ) { /* Let's check the transmit status reg */
+
+ if(status & TX_DONE) { /* The transmit has been done */
+ lp->stats.tx_packets++;
+
+ if(lp->tx_queue) { /* Is there still packets ? */
+ /* There was packet(s) so start transmitting and write also
+ how many packets there is to be sended */
+ outb(TX_START | lp->tx_queue, ioaddr + TRANSMIT_START_REG);
+ lp->tx_queue = 0;
+ lp->tx_queue_len = 0;
+ dev->trans_start = jiffies;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ else {
+ lp->tx_started = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+ }
+ }
+
+ if( ( status & 0xff00 ) ||
+ ( (inb(ioaddr + RECEIVE_MODE_REG) & RX_BUFFER_EMPTY) == 0) ) {
+ eth16i_rx(dev); /* We have packet in receive buffer */
+ }
+
+ dev->interrupt = 0;
+
+ /* Turn interrupts back on */
+ outw(ETH16I_INTR_ON, ioaddr + TX_INTR_REG);
+
+ return;
+}
+
+static void eth16i_multicast(struct device *dev, int num_addrs, void *addrs)
+{
+ short ioaddr = dev->base_addr;
+
+ if(dev->mc_count || dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ {
+ dev->flags|=IFF_PROMISC; /* Must do this */
+ outb(3, ioaddr + RECEIVE_MODE_REG);
+ } else {
+ outb(2, ioaddr + RECEIVE_MODE_REG);
+ }
+}
+
+static struct enet_statistics *eth16i_get_stats(struct device *dev)
+{
+ struct eth16i_local *lp = (struct eth16i_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+static void eth16i_select_regbank(unsigned char banknbr, short ioaddr)
+{
+ unsigned char data;
+
+ data = inb(ioaddr + CONFIG_REG_1);
+ outb( ((data & 0xF3) | ( (banknbr & 0x03) << 2)), ioaddr + CONFIG_REG_1);
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_eth16i = {
+ devicename,
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, eth16i_probe };
+
+int io = 0x2a0;
+int irq = 0;
+
+int init_module(void)
+{
+ if(io == 0)
+ printk("eth16i: You should not use auto-probing with insmod!\n");
+
+ dev_eth16i.base_addr = io;
+ dev_eth16i.irq = irq;
+ if( register_netdev( &dev_eth16i ) != 0 ) {
+ printk("eth16i: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+
+ return 0;
+}
+
+void cleanup_module(void)
+{
+ unregister_netdev( &dev_eth16i );
+ free_irq( dev_eth16i.irq );
+ irq2dev_map[ dev_eth16i.irq ] = NULL;
+ release_region( dev_eth16i.base_addr, ETH16I_IO_EXTENT );
+}
+
+#endif /* MODULE */
+
+
diff --git a/i386/i386at/gpl/linux/net/ewrk3.c b/i386/i386at/gpl/linux/net/ewrk3.c
new file mode 100644
index 00000000..90e3b932
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ewrk3.c
@@ -0,0 +1,1933 @@
+/* ewrk3.c: A DIGITAL EtherWORKS 3 ethernet driver for Linux.
+
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of
+ the GNU Public License, incorporated herein by reference.
+
+ This driver is written for the Digital Equipment Corporation series
+ of EtherWORKS ethernet cards:
+
+ DE203 Turbo (BNC)
+ DE204 Turbo (TP)
+ DE205 Turbo (TP BNC)
+
+ The driver has been tested on a relatively busy network using the DE205
+ card and benchmarked with 'ttcp': it transferred 16M of data at 975kB/s
+ (7.8Mb/s) to a DECstation 5000/200.
+
+ The author may be reached at davies@wanton.lkg.dec.com or
+ davies@maniac.ultranet.com or Digital Equipment Corporation, 550 King
+ Street, Littleton MA 01460.
+
+ =========================================================================
+ This driver has been written substantially from scratch, although its
+ inheritance of style and stack interface from 'depca.c' and in turn from
+ Donald Becker's 'lance.c' should be obvious.
+
+ The DE203/4/5 boards all use a new proprietary chip in place of the
+ LANCE chip used in prior cards (DEPCA, DE100, DE200/1/2, DE210, DE422).
+ Use the depca.c driver in the standard distribution for the LANCE based
+ cards from DIGITAL; this driver will not work with them.
+
+ The DE203/4/5 cards have 2 main modes: shared memory and I/O only. I/O
+ only makes all the card accesses through I/O transactions and no high
+ (shared) memory is used. This mode provides a >48% performance penalty
+ and is deprecated in this driver, although allowed to provide initial
+ setup when hardstrapped.
+
+ The shared memory mode comes in 3 flavours: 2kB, 32kB and 64kB. There is
+ no point in using any mode other than the 2kB mode - their performances
+ are virtually identical, although the driver has been tested in the 2kB
+ and 32kB modes. I would suggest you uncomment the line:
+
+ FORCE_2K_MODE;
+
+ to allow the driver to configure the card as a 2kB card at your current
+ base address, thus leaving more room to clutter your system box with
+ other memory hungry boards.
+
+ As many ISA and EISA cards can be supported under this driver as you
+ wish, limited primarily by the available IRQ lines, rather than by the
+ available I/O addresses (24 ISA, 16 EISA). I have checked different
+ configurations of multiple depca cards and ewrk3 cards and have not
+ found a problem yet (provided you have at least depca.c v0.38) ...
+
+ The board IRQ setting must be at an unused IRQ which is auto-probed
+ using Donald Becker's autoprobe routines. All these cards are at
+ {5,10,11,15}.
+
+ No 16MB memory limitation should exist with this driver as DMA is not
+ used and the common memory area is in low memory on the network card (my
+ current system has 20MB and I've not had problems yet).
+
+ The ability to load this driver as a loadable module has been included
+ and used extensively during the driver development (to save those long
+ reboot sequences). To utilise this ability, you have to do 8 things:
+
+ 0) have a copy of the loadable modules code installed on your system.
+ 1) copy ewrk3.c from the /linux/drivers/net directory to your favourite
+ temporary directory.
+ 2) edit the source code near line 1880 to reflect the I/O address and
+ IRQ you're using.
+ 3) compile ewrk3.c, but include -DMODULE in the command line to ensure
+ that the correct bits are compiled (see end of source code).
+ 4) if you are wanting to add a new card, goto 5. Otherwise, recompile a
+ kernel with the ewrk3 configuration turned off and reboot.
+ 5) insmod ewrk3.o
+ [Alan Cox: Changed this so you can insmod ewrk3.o irq=x io=y]
+ 6) run the net startup bits for your new eth?? interface manually
+ (usually /etc/rc.inet[12] at boot time).
+ 7) enjoy!
+
+ Note that autoprobing is not allowed in loadable modules - the system is
+ already up and running and you're messing with interrupts.
+
+ To unload a module, turn off the associated interface
+ 'ifconfig eth?? down' then 'rmmod ewrk3'.
+
+ Promiscuous mode has been turned off in this driver, but all the
+ multicast address bits have been turned on. This improved the send
+ performance on a busy network by about 13%.
+
+ Ioctl's have now been provided (primarily because I wanted to grab some
+ packet size statistics). They are patterned after 'plipconfig.c' from a
+ suggestion by Alan Cox. Using these ioctls, you can enable promiscuous
+ mode, add/delete multicast addresses, change the hardware address, get
+ packet size distribution statistics and muck around with the control and
+ status register. I'll add others if and when the need arises.
+
+ TO DO:
+ ------
+
+
+ Revision History
+ ----------------
+
+ Version Date Description
+
+ 0.1 26-aug-94 Initial writing. ALPHA code release.
+ 0.11 31-aug-94 Fixed: 2k mode memory base calc.,
+ LeMAC version calc.,
+ IRQ vector assignments during autoprobe.
+ 0.12 31-aug-94 Tested working on LeMAC2 (DE20[345]-AC) card.
+ Fixed up MCA hash table algorithm.
+ 0.20 4-sep-94 Added IOCTL functionality.
+ 0.21 14-sep-94 Added I/O mode.
+ 0.21axp 15-sep-94 Special version for ALPHA AXP Linux V1.0.
+ 0.22 16-sep-94 Added more IOCTLs & tidied up.
+ 0.23 21-sep-94 Added transmit cut through.
+ 0.24 31-oct-94 Added uid checks in some ioctls.
+ 0.30 1-nov-94 BETA code release.
+ 0.31 5-dec-94 Added check/allocate region code.
+ 0.32 16-jan-95 Broadcast packet fix.
+ 0.33 10-Feb-95 Fix recognition bug reported by <bkm@star.rl.ac.uk>.
+ 0.40 27-Dec-95 Rationalise MODULE and autoprobe code.
+ Rewrite for portability & updated.
+ ALPHA support from <jestabro@amt.tay1.dec.com>
+ Added verify_area() calls in depca_ioctl() from
+ suggestion by <heiko@colossus.escape.de>.
+ Add new multicasting code.
+
+ =========================================================================
+*/
+
+static const char *version = "ewrk3.c:v0.40 95/12/27 davies@wanton.lkg.dec.com\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/segment.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/time.h>
+#include <linux/types.h>
+#include <linux/unistd.h>
+
+#include "ewrk3.h"
+
+#ifdef EWRK3_DEBUG
+static int ewrk3_debug = EWRK3_DEBUG;
+#else
+static int ewrk3_debug = 1;
+#endif
+
+#define EWRK3_NDA 0xffe0 /* No Device Address */
+
+#define PROBE_LENGTH 32
+#define ETH_PROM_SIG 0xAA5500FFUL
+
+#ifndef EWRK3_SIGNATURE
+#define EWRK3_SIGNATURE {"DE203","DE204","DE205",""}
+#define EWRK3_STRLEN 8
+#endif
+
+#ifndef EWRK3_RAM_BASE_ADDRESSES
+#define EWRK3_RAM_BASE_ADDRESSES {0xc0000,0xd0000,0x00000}
+#endif
+
+/*
+** Sets up the I/O area for the autoprobe.
+*/
+#define EWRK3_IO_BASE 0x100 /* Start address for probe search */
+#define EWRK3_IOP_INC 0x20 /* I/O address increment */
+#define EWRK3_TOTAL_SIZE 0x20 /* required I/O address length */
+
+#ifndef MAX_NUM_EWRK3S
+#define MAX_NUM_EWRK3S 21
+#endif
+
+#ifndef EWRK3_EISA_IO_PORTS
+#define EWRK3_EISA_IO_PORTS 0x0c00 /* I/O port base address, slot 0 */
+#endif
+
+#ifndef MAX_EISA_SLOTS
+#define MAX_EISA_SLOTS 16
+#define EISA_SLOT_INC 0x1000
+#endif
+
+#define CRC_POLYNOMIAL_BE 0x04c11db7UL /* Ethernet CRC, big endian */
+#define CRC_POLYNOMIAL_LE 0xedb88320UL /* Ethernet CRC, little endian */
+
+#define QUEUE_PKT_TIMEOUT (100) /* Jiffies */
+
+/*
+** EtherWORKS 3 shared memory window sizes
+*/
+#define IO_ONLY 0x00
+#define SHMEM_2K 0x800
+#define SHMEM_32K 0x8000
+#define SHMEM_64K 0x10000
+
+/*
+** EtherWORKS 3 IRQ ENABLE/DISABLE
+*/
+#define ENABLE_IRQs { \
+ icr |= lp->irq_mask;\
+ outb(icr, EWRK3_ICR); /* Enable the IRQs */\
+}
+
+#define DISABLE_IRQs { \
+ icr = inb(EWRK3_ICR);\
+ icr &= ~lp->irq_mask;\
+ outb(icr, EWRK3_ICR); /* Disable the IRQs */\
+}
+
+/*
+** EtherWORKS 3 START/STOP
+*/
+#define START_EWRK3 { \
+ csr = inb(EWRK3_CSR);\
+ csr &= ~(CSR_TXD|CSR_RXD);\
+ outb(csr, EWRK3_CSR); /* Enable the TX and/or RX */\
+}
+
+#define STOP_EWRK3 { \
+ csr = (CSR_TXD|CSR_RXD);\
+ outb(csr, EWRK3_CSR); /* Disable the TX and/or RX */\
+}
+
+/*
+** The EtherWORKS 3 private structure
+*/
+#define EWRK3_PKT_STAT_SZ 16
+#define EWRK3_PKT_BIN_SZ 128 /* Should be >=100 unless you
+ increase EWRK3_PKT_STAT_SZ */
+
+struct ewrk3_private {
+ char adapter_name[80]; /* Name exported to /proc/ioports */
+ u_long shmem_base; /* Shared memory start address */
+ u_long shmem_length; /* Shared memory window length */
+ struct enet_statistics stats; /* Public stats */
+ struct {
+ u32 bins[EWRK3_PKT_STAT_SZ]; /* Private stats counters */
+ u32 unicast;
+ u32 multicast;
+ u32 broadcast;
+ u32 excessive_collisions;
+ u32 tx_underruns;
+ u32 excessive_underruns;
+ } pktStats;
+ u_char irq_mask; /* Adapter IRQ mask bits */
+ u_char mPage; /* Maximum 2kB Page number */
+ u_char lemac; /* Chip rev. level */
+ u_char hard_strapped; /* Don't allow a full open */
+ u_char lock; /* Lock the page register */
+ u_char txc; /* Transmit cut through */
+ u_char *mctbl; /* Pointer to the multicast table */
+};
+
+/*
+** Force the EtherWORKS 3 card to be in 2kB MODE
+*/
+#define FORCE_2K_MODE { \
+ shmem_length = SHMEM_2K;\
+ outb(((mem_start - 0x80000) >> 11), EWRK3_MBR);\
+}
+
+/*
+** Public Functions
+*/
+static int ewrk3_open(struct device *dev);
+static int ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev);
+static void ewrk3_interrupt(int irq, struct pt_regs *regs);
+static int ewrk3_close(struct device *dev);
+static struct enet_statistics *ewrk3_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd);
+
+/*
+** Private functions
+*/
+static int ewrk3_hw_init(struct device *dev, u_long iobase);
+static void ewrk3_init(struct device *dev);
+static int ewrk3_rx(struct device *dev);
+static int ewrk3_tx(struct device *dev);
+
+static void EthwrkSignature(char * name, char *eeprom_image);
+static int DevicePresent(u_long iobase);
+static void SetMulticastFilter(struct device *dev);
+static int EISA_signature(char *name, s32 eisa_id);
+
+static int Read_EEPROM(u_long iobase, u_char eaddr);
+static int Write_EEPROM(short data, u_long iobase, u_char eaddr);
+static u_char get_hw_addr (struct device *dev, u_char *eeprom_image, char chipType);
+
+static void isa_probe(struct device *dev, u_long iobase);
+static void eisa_probe(struct device *dev, u_long iobase);
+static struct device *alloc_device(struct device *dev, u_long iobase);
+
+
+#ifdef MODULE
+int init_module(void);
+void cleanup_module(void);
+static int autoprobed = 1, loading_module = 1;
+
+# else
+static u_char irq[] = {5,0,10,3,11,9,15,12};
+static int autoprobed = 0, loading_module = 0;
+
+#endif /* MODULE */
+
+static char name[EWRK3_STRLEN + 1];
+static int num_ewrk3s = 0, num_eth = 0;
+
+/*
+** Miscellaneous defines...
+*/
+#define INIT_EWRK3 {\
+ outb(EEPROM_INIT, EWRK3_IOPR);\
+ udelay(1000);\
+}
+
+
+
+
+int ewrk3_probe(struct device *dev)
+{
+ int tmp = num_ewrk3s, status = -ENODEV;
+ u_long iobase = dev->base_addr;
+
+ if ((iobase == 0) && loading_module){
+ printk("Autoprobing is not supported when loading a module based driver.\n");
+ status = -EIO;
+ } else { /* First probe for the Ethernet */
+ /* Address PROM pattern */
+ isa_probe(dev, iobase);
+ eisa_probe(dev, iobase);
+
+ if ((tmp == num_ewrk3s) && (iobase != 0) && loading_module) {
+ printk("%s: ewrk3_probe() cannot find device at 0x%04lx.\n", dev->name,
+ iobase);
+ }
+
+ /*
+ ** Walk the device list to check that at least one device
+ ** initialised OK
+ */
+ for (; (dev->priv == NULL) && (dev->next != NULL); dev = dev->next);
+
+ if (dev->priv) status = 0;
+ if (iobase == 0) autoprobed = 1;
+ }
+
+ return status;
+}
+
+static int
+ewrk3_hw_init(struct device *dev, u_long iobase)
+{
+ struct ewrk3_private *lp;
+ int i, status=0;
+ u_long mem_start, shmem_length;
+ u_char cr, cmr, icr, nicsr, lemac, hard_strapped = 0;
+ u_char eeprom_image[EEPROM_MAX], chksum, eisa_cr = 0;
+
+ /*
+ ** Stop the EWRK3. Enable the DBR ROM. Disable interrupts and remote boot.
+ ** This also disables the EISA_ENABLE bit in the EISA Control Register.
+ */
+ if (iobase > 0x400) eisa_cr = inb(EISA_CR);
+ INIT_EWRK3;
+
+ nicsr = inb(EWRK3_CSR);
+
+ icr = inb(EWRK3_ICR);
+ icr |= 0xf0;
+ outb(icr, EWRK3_ICR); /* Disable all the IRQs */
+
+ if (nicsr == CSR_TXD|CSR_RXD) {
+
+ /* Check that the EEPROM is alive and well and not living on Pluto... */
+ for (chksum=0, i=0; i<EEPROM_MAX; i+=2) {
+ union {
+ short val;
+ char c[2];
+ } tmp;
+
+ tmp.val = (short)Read_EEPROM(iobase, (i>>1));
+ eeprom_image[i] = tmp.c[0];
+ eeprom_image[i+1] = tmp.c[1];
+ chksum += eeprom_image[i] + eeprom_image[i+1];
+ }
+
+ if (chksum != 0) { /* Bad EEPROM Data! */
+ printk("%s: Device has a bad on-board EEPROM.\n", dev->name);
+ status = -ENXIO;
+ } else {
+ EthwrkSignature(name, eeprom_image);
+ if (*name != '\0') { /* found a EWRK3 device */
+ dev->base_addr = iobase;
+
+ if (iobase > 0x400) {
+ outb(eisa_cr, EISA_CR); /* Rewrite the EISA CR */
+ }
+
+ lemac = eeprom_image[EEPROM_CHIPVER];
+ cmr = inb(EWRK3_CMR);
+
+ if (((lemac == LeMAC) && ((cmr & CMR_NO_EEPROM) != CMR_NO_EEPROM)) ||
+ ((lemac == LeMAC2) && !(cmr & CMR_HS))) {
+ printk("%s: %s at %#4lx", dev->name, name, iobase);
+ hard_strapped = 1;
+ } else if ((iobase&0x0fff)==EWRK3_EISA_IO_PORTS) {
+ /* EISA slot address */
+ printk("%s: %s at %#4lx (EISA slot %ld)",
+ dev->name, name, iobase, ((iobase>>12)&0x0f));
+ } else { /* ISA port address */
+ printk("%s: %s at %#4lx", dev->name, name, iobase);
+ }
+
+ if (!status) {
+ printk(", h/w address ");
+ if (lemac!=LeMAC2) DevicePresent(iobase);/* need after EWRK3_INIT */
+ status = get_hw_addr(dev, eeprom_image, lemac);
+ for (i = 0; i < ETH_ALEN - 1; i++) { /* get the ethernet addr. */
+ printk("%2.2x:", dev->dev_addr[i]);
+ }
+ printk("%2.2x,\n", dev->dev_addr[i]);
+
+ if (status) {
+ printk(" which has an EEPROM CRC error.\n");
+ status = -ENXIO;
+ } else {
+ if (lemac == LeMAC2) { /* Special LeMAC2 CMR things */
+ cmr &= ~(CMR_RA | CMR_WB | CMR_LINK | CMR_POLARITY | CMR_0WS);
+ if (eeprom_image[EEPROM_MISC0] & READ_AHEAD) cmr |= CMR_RA;
+ if (eeprom_image[EEPROM_MISC0] & WRITE_BEHIND) cmr |= CMR_WB;
+ if (eeprom_image[EEPROM_NETMAN0] & NETMAN_POL) cmr |= CMR_POLARITY;
+ if (eeprom_image[EEPROM_NETMAN0] & NETMAN_LINK) cmr |= CMR_LINK;
+ if (eeprom_image[EEPROM_MISC0] & _0WS_ENA) cmr |= CMR_0WS;
+ }
+ if (eeprom_image[EEPROM_SETUP] & SETUP_DRAM) cmr |= CMR_DRAM;
+ outb(cmr, EWRK3_CMR);
+
+ cr = inb(EWRK3_CR); /* Set up the Control Register */
+ cr |= eeprom_image[EEPROM_SETUP] & SETUP_APD;
+ if (cr & SETUP_APD) cr |= eeprom_image[EEPROM_SETUP] & SETUP_PS;
+ cr |= eeprom_image[EEPROM_MISC0] & FAST_BUS;
+ cr |= eeprom_image[EEPROM_MISC0] & ENA_16;
+ outb(cr, EWRK3_CR);
+
+ /*
+ ** Determine the base address and window length for the EWRK3
+ ** RAM from the memory base register.
+ */
+ mem_start = inb(EWRK3_MBR);
+ shmem_length = 0;
+ if (mem_start != 0) {
+ if ((mem_start >= 0x0a) && (mem_start <= 0x0f)) {
+ mem_start *= SHMEM_64K;
+ shmem_length = SHMEM_64K;
+ } else if ((mem_start >= 0x14) && (mem_start <= 0x1f)) {
+ mem_start *= SHMEM_32K;
+ shmem_length = SHMEM_32K;
+ } else if ((mem_start >= 0x40) && (mem_start <= 0xff)) {
+ mem_start = mem_start * SHMEM_2K + 0x80000;
+ shmem_length = SHMEM_2K;
+ } else {
+ status = -ENXIO;
+ }
+ }
+
+ /*
+ ** See the top of this source code for comments about
+ ** uncommenting this line.
+ */
+/* FORCE_2K_MODE;*/
+
+ if (!status) {
+ if (hard_strapped) {
+ printk(" is hard strapped.\n");
+ } else if (mem_start) {
+ printk(" has a %dk RAM window", (int)(shmem_length >> 10));
+ printk(" at 0x%.5lx", mem_start);
+ } else {
+ printk(" is in I/O only mode");
+ }
+
+ /* private area & initialise */
+ dev->priv = (void *) kmalloc(sizeof(struct ewrk3_private),
+ GFP_KERNEL);
+ if (dev->priv == NULL) {
+ return -ENOMEM;
+ }
+ lp = (struct ewrk3_private *)dev->priv;
+ memset(dev->priv, 0, sizeof(struct ewrk3_private));
+ lp->shmem_base = mem_start;
+ lp->shmem_length = shmem_length;
+ lp->lemac = lemac;
+ lp->hard_strapped = hard_strapped;
+
+ lp->mPage = 64;
+ if (cmr & CMR_DRAM) lp->mPage <<= 1 ;/* 2 DRAMS on module */
+
+ sprintf(lp->adapter_name,"%s (%s)", name, dev->name);
+ request_region(iobase, EWRK3_TOTAL_SIZE, lp->adapter_name);
+
+ lp->irq_mask = ICR_TNEM|ICR_TXDM|ICR_RNEM|ICR_RXDM;
+
+ if (!hard_strapped) {
+ /*
+ ** Enable EWRK3 board interrupts for autoprobing
+ */
+ icr |= ICR_IE; /* Enable interrupts */
+ outb(icr, EWRK3_ICR);
+
+ /* The DMA channel may be passed in on this parameter. */
+ dev->dma = 0;
+
+ /* To auto-IRQ we enable the initialization-done and DMA err,
+ interrupts. For now we will always get a DMA error. */
+ if (dev->irq < 2) {
+#ifndef MODULE
+ u_char irqnum;
+
+ autoirq_setup(0);
+
+ /*
+ ** Trigger a TNE interrupt.
+ */
+ icr |=ICR_TNEM;
+ outb(1,EWRK3_TDQ); /* Write to the TX done queue */
+ outb(icr, EWRK3_ICR); /* Unmask the TXD interrupt */
+
+ irqnum = irq[((icr & IRQ_SEL) >> 4)];
+
+ dev->irq = autoirq_report(1);
+ if ((dev->irq) && (irqnum == dev->irq)) {
+ printk(" and uses IRQ%d.\n", dev->irq);
+ } else {
+ if (!dev->irq) {
+ printk(" and failed to detect IRQ line.\n");
+ } else if ((irqnum == 1) && (lemac == LeMAC2)) {
+ printk(" and an illegal IRQ line detected.\n");
+ } else {
+ printk(", but incorrect IRQ line detected.\n");
+ }
+ status = -ENXIO;
+ }
+
+ DISABLE_IRQs; /* Mask all interrupts */
+
+#endif /* MODULE */
+ } else {
+ printk(" and requires IRQ%d.\n", dev->irq);
+ }
+ }
+ if (status) release_region(iobase, EWRK3_TOTAL_SIZE);
+ } else {
+ status = -ENXIO;
+ }
+ }
+ }
+ } else {
+ status = -ENXIO;
+ }
+ }
+
+ if (!status) {
+ if (ewrk3_debug > 0) {
+ printk(version);
+ }
+
+ /* The EWRK3-specific entries in the device structure. */
+ dev->open = &ewrk3_open;
+ dev->hard_start_xmit = &ewrk3_queue_pkt;
+ dev->stop = &ewrk3_close;
+ dev->get_stats = &ewrk3_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->do_ioctl = &ewrk3_ioctl;
+
+ dev->mem_start = 0;
+
+ /* Fill in the generic field of the device structure. */
+ ether_setup(dev);
+ }
+ } else {
+ status = -ENXIO;
+ }
+
+ return status;
+}
+
+
+static int
+ewrk3_open(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ u_char icr, csr;
+
+ /*
+ ** Stop the TX and RX...
+ */
+ STOP_EWRK3;
+
+ if (!lp->hard_strapped) {
+ irq2dev_map[dev->irq] = dev; /* For latched interrupts */
+
+ if (request_irq(dev->irq, (void *)ewrk3_interrupt, 0, "ewrk3")) {
+ printk("ewrk3_open(): Requested IRQ%d is busy\n",dev->irq);
+ status = -EAGAIN;
+ } else {
+
+ /*
+ ** Re-initialize the EWRK3...
+ */
+ ewrk3_init(dev);
+
+ if (ewrk3_debug > 1){
+ printk("%s: ewrk3 open with irq %d\n",dev->name,dev->irq);
+ printk(" physical address: ");
+ for (i=0;i<5;i++){
+ printk("%2.2x:",(u_char)dev->dev_addr[i]);
+ }
+ printk("%2.2x\n",(u_char)dev->dev_addr[i]);
+ if (lp->shmem_length == 0) {
+ printk(" no shared memory, I/O only mode\n");
+ } else {
+ printk(" start of shared memory: 0x%08lx\n",lp->shmem_base);
+ printk(" window length: 0x%04lx\n",lp->shmem_length);
+ }
+ printk(" # of DRAMS: %d\n",((inb(EWRK3_CMR) & 0x02) ? 2 : 1));
+ printk(" csr: 0x%02x\n", inb(EWRK3_CSR));
+ printk(" cr: 0x%02x\n", inb(EWRK3_CR));
+ printk(" icr: 0x%02x\n", inb(EWRK3_ICR));
+ printk(" cmr: 0x%02x\n", inb(EWRK3_CMR));
+ printk(" fmqc: 0x%02x\n", inb(EWRK3_FMQC));
+ }
+
+ dev->tbusy = 0;
+ dev->start = 1;
+ dev->interrupt = UNMASK_INTERRUPTS;
+
+ /*
+ ** Unmask EWRK3 board interrupts
+ */
+ icr = inb(EWRK3_ICR);
+ ENABLE_IRQs;
+
+ }
+ } else {
+ dev->start = 0;
+ dev->tbusy = 1;
+ printk("%s: ewrk3 available for hard strapped set up only.\n", dev->name);
+ printk(" Run the 'ewrk3setup' utility or remove the hard straps.\n");
+ }
+
+ MOD_INC_USE_COUNT;
+
+ return status;
+}
+
+/*
+** Initialize the EtherWORKS 3 operating conditions
+*/
+static void
+ewrk3_init(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_char csr, page;
+ u_long iobase = dev->base_addr;
+
+ /*
+ ** Enable any multicasts
+ */
+ set_multicast_list(dev);
+
+ /*
+ ** Clean out any remaining entries in all the queues here
+ */
+ while (inb(EWRK3_TQ));
+ while (inb(EWRK3_TDQ));
+ while (inb(EWRK3_RQ));
+ while (inb(EWRK3_FMQ));
+
+ /*
+ ** Write a clean free memory queue
+ */
+ for (page=1;page<lp->mPage;page++) { /* Write the free page numbers */
+ outb(page, EWRK3_FMQ); /* to the Free Memory Queue */
+ }
+
+ lp->lock = 0; /* Ensure there are no locks */
+
+ START_EWRK3; /* Enable the TX and/or RX */
+}
+
+/*
+** Writes a socket buffer to the free page queue
+*/
+static int
+ewrk3_queue_pkt(struct sk_buff *skb, struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int status = 0;
+ u_char icr, csr;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy || lp->lock) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < QUEUE_PKT_TIMEOUT) {
+ status = -1;
+ } else if (!lp->hard_strapped) {
+ printk("%s: transmit timed/locked out, status %04x, resetting.\n",
+ dev->name, inb(EWRK3_CSR));
+
+ /*
+ ** Mask all board interrupts
+ */
+ DISABLE_IRQs;
+
+ /*
+ ** Stop the TX and RX...
+ */
+ STOP_EWRK3;
+
+ ewrk3_init(dev);
+
+ /*
+ ** Unmask EWRK3 board interrupts
+ */
+ ENABLE_IRQs;
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+ } else if (skb == NULL) {
+ dev_tint(dev);
+ } else if (skb->len > 0) {
+
+ /*
+ ** Block a timer-based transmit from overlapping. This could better be
+ ** done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ DISABLE_IRQs; /* So that the page # remains correct */
+
+ /*
+ ** Get a free page from the FMQ when resources are available
+ */
+ if (inb(EWRK3_FMQC) > 0) {
+ u_long buf = 0;
+ u_char page;
+
+ if ((page = inb(EWRK3_FMQ)) < lp->mPage) {
+ /*
+ ** Set up shared memory window and pointer into the window
+ */
+ while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(page, EWRK3_IOPR);
+ } else if (lp->shmem_length == SHMEM_2K) {
+ buf = lp->shmem_base;
+ outb(page, EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_32K) {
+ buf = ((((short)page << 11) & 0x7800) + lp->shmem_base);
+ outb((page >> 4), EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_64K) {
+ buf = ((((short)page << 11) & 0xf800) + lp->shmem_base);
+ outb((page >> 5), EWRK3_MPR);
+ } else {
+ status = -1;
+ printk("%s: Oops - your private data area is hosed!\n",dev->name);
+ }
+
+ if (!status) {
+
+ /*
+ ** Set up the buffer control structures and copy the data from
+ ** the socket buffer to the shared memory .
+ */
+
+ if (lp->shmem_length == IO_ONLY) {
+ int i;
+ u_char *p = skb->data;
+
+ outb((char)(TCR_QMODE | TCR_PAD | TCR_IFC), EWRK3_DATA);
+ outb((char)(skb->len & 0xff), EWRK3_DATA);
+ outb((char)((skb->len >> 8) & 0xff), EWRK3_DATA);
+ outb((char)0x04, EWRK3_DATA);
+ for (i=0; i<skb->len; i++) {
+ outb(*p++, EWRK3_DATA);
+ }
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ } else {
+ writeb((char)(TCR_QMODE|TCR_PAD|TCR_IFC), (char *)buf);/* ctrl byte*/
+ buf+=1;
+ writeb((char)(skb->len & 0xff), (char *)buf);/* length (16 bit xfer)*/
+ buf+=1;
+ if (lp->txc) {
+ writeb((char)(((skb->len >> 8) & 0xff) | XCT), (char *)buf);
+ buf+=1;
+ writeb(0x04, (char *)buf); /* index byte */
+ buf+=1;
+ writeb(0x00, (char *)(buf + skb->len)); /* Write the XCT flag */
+ memcpy_toio(buf, skb->data, PRELOAD);/* Write PRELOAD bytes*/
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ memcpy_toio(buf+PRELOAD, skb->data+PRELOAD, skb->len-PRELOAD);
+ writeb(0xff, (char *)(buf + skb->len)); /* Write the XCT flag */
+ } else {
+ writeb((char)((skb->len >> 8) & 0xff), (char *)buf);
+ buf+=1;
+ writeb(0x04, (char *)buf); /* index byte */
+ buf+=1;
+ memcpy_toio((char *)buf, skb->data, skb->len);/* Write data bytes */
+ outb(page, EWRK3_TQ); /* Start sending pkt */
+ }
+ }
+
+ dev->trans_start = jiffies;
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ } else { /* return unused page to the free memory queue */
+ outb(page, EWRK3_FMQ);
+ }
+ lp->lock = 0; /* unlock the page register */
+ } else {
+ printk("ewrk3_queue_pkt(): Invalid free memory page (%d).\n",
+ (u_char) page);
+ }
+ } else {
+ printk("ewrk3_queue_pkt(): No free resources...\n");
+ printk("ewrk3_queue_pkt(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC));
+ }
+
+ /* Check for free resources: clear 'tbusy' if there are some */
+ if (inb(EWRK3_FMQC) > 0) {
+ dev->tbusy = 0;
+ }
+
+ ENABLE_IRQs;
+ }
+
+ return status;
+}
+
+/*
+** The EWRK3 interrupt handler.
+*/
+static void
+ewrk3_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct ewrk3_private *lp;
+ u_long iobase;
+ u_char icr, cr, csr;
+
+ if (dev == NULL) {
+ printk ("ewrk3_interrupt(): irq %d for unknown device.\n", irq);
+ } else {
+ lp = (struct ewrk3_private *)dev->priv;
+ iobase = dev->base_addr;
+
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = MASK_INTERRUPTS;
+
+ /* get the interrupt information */
+ csr = inb(EWRK3_CSR);
+
+ /*
+ ** Mask the EWRK3 board interrupts and turn on the LED
+ */
+ DISABLE_IRQs;
+
+ cr = inb(EWRK3_CR);
+ cr |= CR_LED;
+ outb(cr, EWRK3_CR);
+
+ if (csr & CSR_RNE) /* Rx interrupt (packet[s] arrived) */
+ ewrk3_rx(dev);
+
+ if (csr & CSR_TNE) /* Tx interrupt (packet sent) */
+ ewrk3_tx(dev);
+
+ /*
+ ** Now deal with the TX/RX disable flags. These are set when there
+ ** are no more resources. If resources free up then enable these
+ ** interrupts, otherwise mask them - failure to do this will result
+ ** in the system hanging in an interrupt loop.
+ */
+ if (inb(EWRK3_FMQC)) { /* any resources available? */
+ lp->irq_mask |= ICR_TXDM|ICR_RXDM;/* enable the interrupt source */
+ csr &= ~(CSR_TXD|CSR_RXD);/* ensure restart of a stalled TX or RX */
+ outb(csr, EWRK3_CSR);
+ dev->tbusy = 0; /* clear TX busy flag */
+ mark_bh(NET_BH);
+ } else {
+ lp->irq_mask &= ~(ICR_TXDM|ICR_RXDM);/* disable the interrupt source */
+ }
+
+ /* Unmask the EWRK3 board interrupts and turn off the LED */
+ cr &= ~CR_LED;
+ outb(cr, EWRK3_CR);
+
+ dev->interrupt = UNMASK_INTERRUPTS;
+ ENABLE_IRQs;
+ }
+
+ return;
+}
+
+static int
+ewrk3_rx(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ int i, status = 0;
+ u_char page, tmpPage = 0, tmpLock = 0;
+ u_long buf = 0;
+
+ while (inb(EWRK3_RQC) && !status) { /* Whilst there's incoming data */
+ if ((page = inb(EWRK3_RQ)) < lp->mPage) {/* Get next entry's buffer page */
+ /*
+ ** Preempt any process using the current page register. Check for
+ ** an existing lock to reduce time taken in I/O transactions.
+ */
+ if ((tmpLock = set_bit(0, (void *)&lp->lock)) == 1) { /* Assert lock */
+ if (lp->shmem_length == IO_ONLY) { /* Get existing page */
+ tmpPage = inb(EWRK3_IOPR);
+ } else {
+ tmpPage = inb(EWRK3_MPR);
+ }
+ }
+
+ /*
+ ** Set up shared memory window and pointer into the window
+ */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(page, EWRK3_IOPR);
+ } else if (lp->shmem_length == SHMEM_2K) {
+ buf = lp->shmem_base;
+ outb(page, EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_32K) {
+ buf = ((((short)page << 11) & 0x7800) + lp->shmem_base);
+ outb((page >> 4), EWRK3_MPR);
+ } else if (lp->shmem_length == SHMEM_64K) {
+ buf = ((((short)page << 11) & 0xf800) + lp->shmem_base);
+ outb((page >> 5), EWRK3_MPR);
+ } else {
+ status = -1;
+ printk("%s: Oops - your private data area is hosed!\n",dev->name);
+ }
+
+ if (!status) {
+ char rx_status;
+ int pkt_len;
+
+ if (lp->shmem_length == IO_ONLY) {
+ rx_status = inb(EWRK3_DATA);
+ pkt_len = inb(EWRK3_DATA);
+ pkt_len |= ((u_short)inb(EWRK3_DATA) << 8);
+ } else {
+ rx_status = readb(buf);
+ buf+=1;
+ pkt_len = readw(buf);
+ buf+=3;
+ }
+
+ if (!(rx_status & R_ROK)) { /* There was an error. */
+ lp->stats.rx_errors++; /* Update the error stats. */
+ if (rx_status & R_DBE) lp->stats.rx_frame_errors++;
+ if (rx_status & R_CRC) lp->stats.rx_crc_errors++;
+ if (rx_status & R_PLL) lp->stats.rx_fifo_errors++;
+ } else {
+ struct sk_buff *skb;
+
+ if ((skb = dev_alloc_skb(pkt_len+2)) != NULL) {
+ unsigned char *p;
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align to 16 bytes */
+ p = skb_put(skb,pkt_len);
+
+ if (lp->shmem_length == IO_ONLY) {
+ *p = inb(EWRK3_DATA); /* dummy read */
+ for (i=0; i<pkt_len; i++) {
+ *p++ = inb(EWRK3_DATA);
+ }
+ } else {
+ memcpy_fromio(p, buf, pkt_len);
+ }
+
+ /*
+ ** Notify the upper protocol layers that there is another
+ ** packet to handle
+ */
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ /*
+ ** Update stats
+ */
+ lp->stats.rx_packets++;
+ for (i=1; i<EWRK3_PKT_STAT_SZ-1; i++) {
+ if (pkt_len < i*EWRK3_PKT_BIN_SZ) {
+ lp->pktStats.bins[i]++;
+ i = EWRK3_PKT_STAT_SZ;
+ }
+ }
+ p = skb->data; /* Look at the dest addr */
+ if (p[0] & 0x01) { /* Multicast/Broadcast */
+ if ((*(s32 *)&p[0] == -1) && (*(s16 *)&p[4] == -1)) {
+ lp->pktStats.broadcast++;
+ } else {
+ lp->pktStats.multicast++;
+ }
+ } else if ((*(s32 *)&p[0] == *(s32 *)&dev->dev_addr[0]) &&
+ (*(s16 *)&p[4] == *(s16 *)&dev->dev_addr[4])) {
+ lp->pktStats.unicast++;
+ }
+
+ lp->pktStats.bins[0]++; /* Duplicates stats.rx_packets */
+ if (lp->pktStats.bins[0] == 0) { /* Reset counters */
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ }
+ } else {
+ printk("%s: Insufficient memory; nuking packet.\n", dev->name);
+ lp->stats.rx_dropped++; /* Really, deferred. */
+ break;
+ }
+ }
+ }
+ /*
+ ** Return the received buffer to the free memory queue
+ */
+ outb(page, EWRK3_FMQ);
+
+ if (tmpLock) { /* If a lock was preempted */
+ if (lp->shmem_length == IO_ONLY) { /* Replace old page */
+ outb(tmpPage, EWRK3_IOPR);
+ } else {
+ outb(tmpPage, EWRK3_MPR);
+ }
+ }
+ lp->lock = 0; /* Unlock the page register */
+ } else {
+ printk("ewrk3_rx(): Illegal page number, page %d\n",page);
+ printk("ewrk3_rx(): CSR: %02x ICR: %02x FMQC: %02x\n",inb(EWRK3_CSR),inb(EWRK3_ICR),inb(EWRK3_FMQC));
+ }
+ }
+ return status;
+}
+
+/*
+** Buffer sent - check for TX buffer errors.
+*/
+static int
+ewrk3_tx(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char tx_status;
+
+ while ((tx_status = inb(EWRK3_TDQ)) > 0) { /* Whilst there's old buffers */
+ if (tx_status & T_VSTS) { /* The status is valid */
+ if (tx_status & T_TXE) {
+ lp->stats.tx_errors++;
+ if (tx_status & T_NCL) lp->stats.tx_carrier_errors++;
+ if (tx_status & T_LCL) lp->stats.tx_window_errors++;
+ if (tx_status & T_CTU) {
+ if ((tx_status & T_COLL) ^ T_XUR) {
+ lp->pktStats.tx_underruns++;
+ } else {
+ lp->pktStats.excessive_underruns++;
+ }
+ } else if (tx_status & T_COLL) {
+ if ((tx_status & T_COLL) ^ T_XCOLL) {
+ lp->stats.collisions++;
+ } else {
+ lp->pktStats.excessive_collisions++;
+ }
+ }
+ } else {
+ lp->stats.tx_packets++;
+ }
+ }
+ }
+
+ return 0;
+}
+
+static int
+ewrk3_close(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char icr, csr;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ewrk3_debug > 1) {
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inb(EWRK3_CSR));
+ }
+
+ /*
+ ** We stop the EWRK3 here... mask interrupts and stop TX & RX
+ */
+ DISABLE_IRQs;
+
+ STOP_EWRK3;
+
+ /*
+ ** Clean out the TX and RX queues here (note that one entry
+ ** may get added to either the TXD or RX queues if the the TX or RX
+ ** just starts processing a packet before the STOP_EWRK3 command
+ ** is received. This will be flushed in the ewrk3_open() call).
+ */
+ while (inb(EWRK3_TQ));
+ while (inb(EWRK3_TDQ));
+ while (inb(EWRK3_RQ));
+
+ if (!lp->hard_strapped) {
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+ }
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+static struct enet_statistics *
+ewrk3_get_stats(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+
+ /* Null body since there is no framing error counter */
+
+ return &lp->stats;
+}
+
+/*
+** Set or clear the multicast filter for this adaptor.
+*/
+static void
+set_multicast_list(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ u_long iobase = dev->base_addr;
+ u_char csr;
+
+ if (irq2dev_map[dev->irq] != NULL) {
+ csr = inb(EWRK3_CSR);
+
+ if (lp->shmem_length == IO_ONLY) {
+ lp->mctbl = (char *) PAGE0_HTE;
+ } else {
+ lp->mctbl = (char *)(lp->shmem_base + PAGE0_HTE);
+ }
+
+ csr &= ~(CSR_PME | CSR_MCE);
+ if (dev->flags & IFF_PROMISC) { /* set promiscuous mode */
+ csr |= CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ SetMulticastFilter(dev);
+ csr |= CSR_MCE;
+ outb(csr, EWRK3_CSR);
+ }
+ }
+}
+
+/*
+** Calculate the hash code and update the logical address filter
+** from a list of ethernet multicast addresses.
+** Little endian crc one liner from Matt Thomas, DEC.
+**
+** Note that when clearing the table, the broadcast bit must remain asserted
+** to receive broadcast messages.
+*/
+static void SetMulticastFilter(struct device *dev)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ u_long iobase = dev->base_addr;
+ int i;
+ char *addrs, j, bit, byte;
+ short *p = (short *) lp->mctbl;
+ u16 hashcode;
+ s32 crc, poly = CRC_POLYNOMIAL_LE;
+
+ while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
+
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0, EWRK3_IOPR);
+ outw(EEPROM_OFFSET(lp->mctbl), EWRK3_PIR1);
+ } else {
+ outb(0, EWRK3_MPR);
+ }
+
+ if (dev->flags & IFF_ALLMULTI) {
+ for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0xff, EWRK3_DATA);
+ } else { /* memset didn't work here */
+ writew(0xffff, p);
+ p++; i++;
+ }
+ }
+ } else {
+ /* Clear table except for broadcast bit */
+ if (lp->shmem_length == IO_ONLY) {
+ for (i=0; i<(HASH_TABLE_LEN >> 4) - 1; i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ outb(0x80, EWRK3_DATA); i++; /* insert the broadcast bit */
+ for (; i<(HASH_TABLE_LEN >> 3); i++) {
+ outb(0x00, EWRK3_DATA);
+ }
+ } else {
+ memset_io(lp->mctbl, 0, (HASH_TABLE_LEN >> 3));
+ writeb(0x80, (char *)(lp->mctbl + (HASH_TABLE_LEN >> 4) - 1));
+ }
+
+ /* Update table */
+ for (i=0;i<dev->mc_count;i++) { /* for each address in the list */
+ addrs=dmi->dmi_addr;
+ dmi=dmi->next;
+ if ((*addrs & 0x01) == 1) { /* multicast address? */
+ crc = 0xffffffff; /* init CRC for each address */
+ for (byte=0;byte<ETH_ALEN;byte++) { /* for each address byte */
+ /* process each address bit */
+ for (bit = *addrs++,j=0;j<8;j++, bit>>=1) {
+ crc = (crc >> 1) ^ (((crc ^ bit) & 0x01) ? poly : 0);
+ }
+ }
+ hashcode = crc & ((1 << 9) - 1); /* hashcode is 9 LSb of CRC */
+
+ byte = hashcode >> 3; /* bit[3-8] -> byte in filter */
+ bit = 1 << (hashcode & 0x07); /* bit[0-2] -> bit in byte */
+
+ if (lp->shmem_length == IO_ONLY) {
+ u_char tmp;
+
+ outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1);
+ tmp = inb(EWRK3_DATA);
+ tmp |= bit;
+ outw((short)((long)lp->mctbl) + byte, EWRK3_PIR1);
+ outb(tmp, EWRK3_DATA);
+ } else {
+ writeb(readb(lp->mctbl + byte) | bit, lp->mctbl + byte);
+ }
+ }
+ }
+ }
+
+ lp->lock = 0; /* Unlock the page register */
+
+ return;
+}
+
+/*
+** ISA bus I/O device probe
+*/
+static void isa_probe(struct device *dev, u_long ioaddr)
+{
+ int i = num_ewrk3s, maxSlots;
+ u_long iobase;
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if (ioaddr >= 0x400) return; /* Not ISA */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EWRK3_IO_BASE; /* Get the first slot address */
+ maxSlots = 24;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ maxSlots = i + 1;
+ }
+
+ for (; (i<maxSlots) && (dev!=NULL);iobase+=EWRK3_IOP_INC, i++) {
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (ewrk3_hw_init(dev, iobase) == 0) {
+ num_ewrk3s++;
+ }
+ num_eth++;
+ }
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+
+ return;
+}
+
+/*
+** EISA bus I/O device probe. Probe from slot 1 since slot 0 is usually
+** the motherboard.
+*/
+static void eisa_probe(struct device *dev, u_long ioaddr)
+{
+ int i, maxSlots;
+ u_long iobase;
+ char name[EWRK3_STRLEN];
+
+ if (!ioaddr && autoprobed) return ; /* Been here before ! */
+ if (ioaddr < 0x1000) return; /* Not EISA */
+
+ if (ioaddr == 0) { /* Autoprobing */
+ iobase = EISA_SLOT_INC; /* Get the first slot address */
+ i = 1;
+ maxSlots = MAX_EISA_SLOTS;
+ } else { /* Probe a specific location */
+ iobase = ioaddr;
+ i = (ioaddr >> 12);
+ maxSlots = i + 1;
+ }
+
+ for (i=1; (i<maxSlots) && (dev!=NULL); i++, iobase+=EISA_SLOT_INC) {
+ if (EISA_signature(name, EISA_ID) == 0) {
+ if (!check_region(iobase, EWRK3_TOTAL_SIZE)) {
+ if (DevicePresent(iobase) == 0) {
+ if ((dev = alloc_device(dev, iobase)) != NULL) {
+ if (ewrk3_hw_init(dev, iobase) == 0) {
+ num_ewrk3s++;
+ }
+ num_eth++;
+ }
+ }
+ } else if (autoprobed) {
+ printk("%s: region already allocated at 0x%04lx.\n", dev->name, iobase);
+ }
+ }
+ }
+
+ return;
+}
+
+/*
+** Allocate the device by pointing to the next available space in the
+** device structure. Should one not be available, it is created.
+*/
+static struct device *alloc_device(struct device *dev, u_long iobase)
+{
+ int addAutoProbe = 0;
+ struct device *tmp = NULL, *ret;
+ int (*init)(struct device *) = NULL;
+
+ /*
+ ** Check the device structures for an end of list or unused device
+ */
+ if (!loading_module) {
+ while (dev->next != NULL) {
+ if ((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0)) break;
+ dev = dev->next; /* walk through eth device list */
+ num_eth++; /* increment eth device number */
+ }
+
+ /*
+ ** If an autoprobe is requested for another device, we must re-insert
+ ** the request later in the list. Remember the current position first.
+ */
+ if ((dev->base_addr == 0) && (num_ewrk3s > 0)) {
+ addAutoProbe++;
+ tmp = dev->next; /* point to the next device */
+ init = dev->init; /* remember the probe function */
+ }
+
+ /*
+ ** If at end of list and can't use current entry, malloc one up.
+ ** If memory could not be allocated, print an error message.
+ */
+ if ((dev->next == NULL) &&
+ !((dev->base_addr == EWRK3_NDA) || (dev->base_addr == 0))){
+ dev->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+
+ dev = dev->next; /* point to the new device */
+ if (dev == NULL) {
+ printk("eth%d: Device not initialised, insufficient memory\n",
+ num_eth);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ dev->name = (char *)(dev + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(dev->name,"eth????"); /* New device name */
+ } else {
+ sprintf(dev->name,"eth%d", num_eth);/* New device name */
+ }
+ dev->base_addr = iobase; /* assign the io address */
+ dev->next = NULL; /* mark the end of list */
+ dev->init = &ewrk3_probe; /* initialisation routine */
+ num_ewrk3s++;
+ }
+ }
+ ret = dev; /* return current struct, or NULL */
+
+ /*
+ ** Now figure out what to do with the autoprobe that has to be inserted.
+ ** Firstly, search the (possibly altered) list for an empty space.
+ */
+ if (ret != NULL) {
+ if (addAutoProbe) {
+ for (;(tmp->next!=NULL) && (tmp->base_addr!=EWRK3_NDA); tmp=tmp->next);
+
+ /*
+ ** If no more device structures and can't use the current one, malloc
+ ** one up. If memory could not be allocated, print an error message.
+ */
+ if ((tmp->next == NULL) && !(tmp->base_addr == EWRK3_NDA)) {
+ tmp->next = (struct device *)kmalloc(sizeof(struct device) + 8,
+ GFP_KERNEL);
+ tmp = tmp->next; /* point to the new device */
+ if (tmp == NULL) {
+ printk("%s: Insufficient memory to extend the device list.\n",
+ dev->name);
+ } else {
+ /*
+ ** If the memory was allocated, point to the new memory area
+ ** and initialize it (name, I/O address, next device (NULL) and
+ ** initialisation probe routine).
+ */
+ tmp->name = (char *)(tmp + sizeof(struct device));
+ if (num_eth > 9999) {
+ sprintf(tmp->name,"eth????"); /* New device name */
+ } else {
+ sprintf(tmp->name,"eth%d", num_eth);/* New device name */
+ }
+ tmp->base_addr = 0; /* re-insert the io address */
+ tmp->next = NULL; /* mark the end of list */
+ tmp->init = init; /* initialisation routine */
+ }
+ } else { /* structure already exists */
+ tmp->base_addr = 0; /* re-insert the io address */
+ }
+ }
+ }
+ } else {
+ ret = dev;
+ }
+
+ return ret;
+}
+
+/*
+** Read the EWRK3 EEPROM using this routine
+*/
+static int Read_EEPROM(u_long iobase, u_char eaddr)
+{
+ int i;
+
+ outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
+ outb(EEPROM_RD, EWRK3_IOPR); /* issue read command */
+ for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+
+ return inw(EWRK3_EPROM1); /* 16 bits data return */
+}
+
+/*
+** Write the EWRK3 EEPROM using this routine
+*/
+static int Write_EEPROM(short data, u_long iobase, u_char eaddr)
+{
+ int i;
+
+ outb(EEPROM_WR_EN, EWRK3_IOPR); /* issue write enable command */
+ for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+ outw(data, EWRK3_EPROM1); /* write data to register */
+ outb((eaddr & 0x3f), EWRK3_PIR1); /* set up 6 bits of address info */
+ outb(EEPROM_WR, EWRK3_IOPR); /* issue write command */
+ for (i=0;i<75000;i++) inb(EWRK3_CSR); /* wait 15msec */
+ outb(EEPROM_WR_DIS, EWRK3_IOPR); /* issue write disable command */
+ for (i=0;i<5000;i++) inb(EWRK3_CSR); /* wait 1msec */
+
+ return 0;
+}
+
+/*
+** Look for a particular board name in the on-board EEPROM.
+*/
+static void EthwrkSignature(char *name, char *eeprom_image)
+{
+ u_long i,j,k;
+ char *signatures[] = EWRK3_SIGNATURE;
+
+ strcpy(name, "");
+ for (i=0;*signatures[i] != '\0' && *name == '\0';i++) {
+ for (j=EEPROM_PNAME7,k=0;j<=EEPROM_PNAME0 && k<strlen(signatures[i]);j++) {
+ if (signatures[i][k] == eeprom_image[j]) { /* track signature */
+ k++;
+ } else { /* lost signature; begin search again */
+ k=0;
+ }
+ }
+ if (k == strlen(signatures[i])) {
+ for (k=0; k<EWRK3_STRLEN; k++) {
+ name[k] = eeprom_image[EEPROM_PNAME7 + k];
+ name[EWRK3_STRLEN] = '\0';
+ }
+ }
+ }
+
+ return; /* return the device name string */
+}
+
+/*
+** Look for a special sequence in the Ethernet station address PROM that
+** is common across all EWRK3 products.
+**
+** Search the Ethernet address ROM for the signature. Since the ROM address
+** counter can start at an arbitrary point, the search must include the entire
+** probe sequence length plus the (length_of_the_signature - 1).
+** Stop the search IMMEDIATELY after the signature is found so that the
+** PROM address counter is correctly positioned at the start of the
+** ethernet address for later read out.
+*/
+
+static int DevicePresent(u_long iobase)
+{
+ union {
+ struct {
+ u32 a;
+ u32 b;
+ } llsig;
+ char Sig[sizeof(u32) << 1];
+ } dev;
+ short sigLength;
+ char data;
+ int i, j, status = 0;
+
+ dev.llsig.a = ETH_PROM_SIG;
+ dev.llsig.b = ETH_PROM_SIG;
+ sigLength = sizeof(u32) << 1;
+
+ for (i=0,j=0;j<sigLength && i<PROBE_LENGTH+sigLength-1;i++) {
+ data = inb(EWRK3_APROM);
+ if (dev.Sig[j] == data) { /* track signature */
+ j++;
+ } else { /* lost signature; begin search again */
+ if (data == dev.Sig[0]) {
+ j=1;
+ } else {
+ j=0;
+ }
+ }
+ }
+
+ if (j!=sigLength) {
+ status = -ENODEV; /* search failed */
+ }
+
+ return status;
+}
+
+static u_char get_hw_addr(struct device *dev, u_char *eeprom_image, char chipType)
+{
+ int i, j, k;
+ u_short chksum;
+ u_char crc, lfsr, sd, status = 0;
+ u_long iobase = dev->base_addr;
+ u16 tmp;
+
+ if (chipType == LeMAC2) {
+ for (crc=0x6a, j=0; j<ETH_ALEN; j++) {
+ sd = dev->dev_addr[j] = eeprom_image[EEPROM_PADDR0 + j];
+ outb(dev->dev_addr[j], EWRK3_PAR0 + j);
+ for (k=0; k<8; k++, sd >>= 1) {
+ lfsr = ((((crc & 0x02) >> 1) ^ (crc & 0x01)) ^ (sd & 0x01)) << 7;
+ crc = (crc >> 1) + lfsr;
+ }
+ }
+ if (crc != eeprom_image[EEPROM_PA_CRC]) status = -1;
+ } else {
+ for (i=0,k=0;i<ETH_ALEN;) {
+ k <<= 1 ;
+ if (k > 0xffff) k-=0xffff;
+
+ k += (u_char) (tmp = inb(EWRK3_APROM));
+ dev->dev_addr[i] = (u_char) tmp;
+ outb(dev->dev_addr[i], EWRK3_PAR0 + i);
+ i++;
+ k += (u_short) ((tmp = inb(EWRK3_APROM)) << 8);
+ dev->dev_addr[i] = (u_char) tmp;
+ outb(dev->dev_addr[i], EWRK3_PAR0 + i);
+ i++;
+
+ if (k > 0xffff) k-=0xffff;
+ }
+ if (k == 0xffff) k=0;
+ chksum = inb(EWRK3_APROM);
+ chksum |= (inb(EWRK3_APROM)<<8);
+ if (k != chksum) status = -1;
+ }
+
+ return status;
+}
+
+/*
+** Look for a particular board name in the EISA configuration space
+*/
+static int EISA_signature(char *name, s32 eisa_id)
+{
+ u_long i;
+ char *signatures[] = EWRK3_SIGNATURE;
+ char ManCode[EWRK3_STRLEN];
+ union {
+ s32 ID;
+ char Id[4];
+ } Eisa;
+ int status = 0;
+
+ *name = '\0';
+ for (i=0; i<4; i++) {
+ Eisa.Id[i] = inb(eisa_id + i);
+ }
+
+ ManCode[0]=(((Eisa.Id[0]>>2)&0x1f)+0x40);
+ ManCode[1]=(((Eisa.Id[1]&0xe0)>>5)+((Eisa.Id[0]&0x03)<<3)+0x40);
+ ManCode[2]=(((Eisa.Id[2]>>4)&0x0f)+0x30);
+ ManCode[3]=((Eisa.Id[2]&0x0f)+0x30);
+ ManCode[4]=(((Eisa.Id[3]>>4)&0x0f)+0x30);
+ ManCode[5]='\0';
+
+ for (i=0;(*signatures[i] != '\0') && (*name == '\0');i++) {
+ if (strstr(ManCode, signatures[i]) != NULL) {
+ strcpy(name,ManCode);
+ status = 1;
+ }
+ }
+
+ return status; /* return the device name string */
+}
+
+/*
+** Perform IOCTL call functions here. Some are privileged operations and the
+** effective uid is checked in those cases.
+*/
+static int ewrk3_ioctl(struct device *dev, struct ifreq *rq, int cmd)
+{
+ struct ewrk3_private *lp = (struct ewrk3_private *)dev->priv;
+ struct ewrk3_ioctl *ioc = (struct ewrk3_ioctl *) &rq->ifr_data;
+ u_long iobase = dev->base_addr;
+ int i, j, status = 0;
+ u_char csr;
+ union {
+ u_char addr[HASH_TABLE_LEN * ETH_ALEN];
+ u_short val[(HASH_TABLE_LEN * ETH_ALEN) >> 1];
+ } tmp;
+
+ switch(ioc->cmd) {
+ case EWRK3_GET_HWADDR: /* Get the hardware address */
+ for (i=0; i<ETH_ALEN; i++) {
+ tmp.addr[i] = dev->dev_addr[i];
+ }
+ ioc->len = ETH_ALEN;
+ if (!(status = verify_area(VERIFY_WRITE, (void *)ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case EWRK3_SET_HWADDR: /* Set the hardware address */
+ if (suser()) {
+ if (!(status = verify_area(VERIFY_READ, (void *)ioc->data, ETH_ALEN))) {
+ csr = inb(EWRK3_CSR);
+ csr |= (CSR_TXD|CSR_RXD);
+ outb(csr, EWRK3_CSR); /* Disable the TX and RX */
+
+ memcpy_fromfs(tmp.addr,ioc->data,ETH_ALEN);
+ for (i=0; i<ETH_ALEN; i++) {
+ dev->dev_addr[i] = tmp.addr[i];
+ outb(tmp.addr[i], EWRK3_PAR0 + i);
+ }
+
+ csr &= ~(CSR_TXD|CSR_RXD); /* Enable the TX and RX */
+ outb(csr, EWRK3_CSR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SET_PROM: /* Set Promiscuous Mode */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr |= CSR_PME;
+ csr &= ~CSR_MCE;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_PROM: /* Clear Promiscuous Mode */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr &= ~CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SAY_BOO: /* Say "Boo!" to the kernel log file */
+ printk("%s: Boo!\n", dev->name);
+
+ break;
+ case EWRK3_GET_MCA: /* Get the multicast address table */
+ if (!(status = verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ while (set_bit(0, (void *)&lp->lock) != 0); /* Wait for lock to free */
+ if (lp->shmem_length == IO_ONLY) {
+ outb(0, EWRK3_IOPR);
+ outw(PAGE0_HTE, EWRK3_PIR1);
+ for (i=0; i<(HASH_TABLE_LEN >> 3); i++) {
+ tmp.addr[i] = inb(EWRK3_DATA);
+ }
+ } else {
+ outb(0, EWRK3_MPR);
+ memcpy_fromio(tmp.addr, (char *)(lp->shmem_base + PAGE0_HTE), (HASH_TABLE_LEN >> 3));
+ }
+ ioc->len = (HASH_TABLE_LEN >> 3);
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+ lp->lock = 0; /* Unlock the page register */
+
+ break;
+ case EWRK3_SET_MCA: /* Set a multicast address */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, ETH_ALEN*ioc->len))) {
+ memcpy_fromfs(tmp.addr, ioc->data, ETH_ALEN * ioc->len);
+ set_multicast_list(dev);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_MCA: /* Clear all multicast addresses */
+ if (suser()) {
+ set_multicast_list(dev);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_MCA_EN: /* Enable multicast addressing */
+ if (suser()) {
+ csr = inb(EWRK3_CSR);
+ csr |= CSR_MCE;
+ csr &= ~CSR_PME;
+ outb(csr, EWRK3_CSR);
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_STATS: /* Get the driver statistics */
+ cli();
+ ioc->len = sizeof(lp->pktStats);
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, &lp->pktStats, ioc->len);
+ }
+ sti();
+
+ break;
+ case EWRK3_CLR_STATS: /* Zero out the driver statistics */
+ if (suser()) {
+ cli();
+ memset(&lp->pktStats, 0, sizeof(lp->pktStats));
+ sti();
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_CSR: /* Get the CSR Register contents */
+ tmp.addr[0] = inb(EWRK3_CSR);
+ ioc->len = 1;
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case EWRK3_SET_CSR: /* Set the CSR Register contents */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, 1))) {
+ memcpy_fromfs(tmp.addr, ioc->data, 1);
+ outb(tmp.addr[0], EWRK3_CSR);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_EEPROM: /* Get the EEPROM contents */
+ if (suser()) {
+ for (i=0; i<(EEPROM_MAX>>1); i++) {
+ tmp.val[i] = (short)Read_EEPROM(iobase, i);
+ }
+ i = EEPROM_MAX;
+ tmp.addr[i++] = inb(EWRK3_CMR); /* Config/Management Reg. */
+ for (j=0;j<ETH_ALEN;j++) {
+ tmp.addr[i++] = inb(EWRK3_PAR0 + j);
+ }
+ ioc->len = EEPROM_MAX + 1 + ETH_ALEN;
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_SET_EEPROM: /* Set the EEPROM contents */
+ if (suser()) {
+ if (!(status=verify_area(VERIFY_READ, ioc->data, EEPROM_MAX))) {
+ memcpy_fromfs(tmp.addr, ioc->data, EEPROM_MAX);
+ for (i=0; i<(EEPROM_MAX>>1); i++) {
+ Write_EEPROM(tmp.val[i], iobase, i);
+ }
+ }
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_GET_CMR: /* Get the CMR Register contents */
+ tmp.addr[0] = inb(EWRK3_CMR);
+ ioc->len = 1;
+ if (!(status=verify_area(VERIFY_WRITE, ioc->data, ioc->len))) {
+ memcpy_tofs(ioc->data, tmp.addr, ioc->len);
+ }
+
+ break;
+ case EWRK3_SET_TX_CUT_THRU: /* Set TX cut through mode */
+ if (suser()) {
+ lp->txc = 1;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ case EWRK3_CLR_TX_CUT_THRU: /* Clear TX cut through mode */
+ if (suser()) {
+ lp->txc = 0;
+ } else {
+ status = -EPERM;
+ }
+
+ break;
+ default:
+ status = -EOPNOTSUPP;
+ }
+
+ return status;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device thisEthwrk = {
+ devicename, /* device name is inserted by /linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0x300, 5, /* I/O address, IRQ */
+ 0, 0, 0, NULL, ewrk3_probe };
+
+static int io=0x300; /* <--- EDIT THESE LINES FOR YOUR CONFIGURATION */
+static int irq=5; /* or use the insmod io= irq= options */
+
+int
+init_module(void)
+{
+ thisEthwrk.base_addr=io;
+ thisEthwrk.irq=irq;
+ if (register_netdev(&thisEthwrk) != 0)
+ return -EIO;
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ release_region(thisEthwrk.base_addr, EWRK3_TOTAL_SIZE);
+
+ if (thisEthwrk.priv) {
+ kfree(thisEthwrk.priv);
+ thisEthwrk.priv = NULL;
+ }
+ thisEthwrk.irq = 0;
+
+ unregister_netdev(&thisEthwrk);
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * kernel-compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c ewrk3.c"
+ *
+ * module-compile-command: "gcc -D__KERNEL__ -DMODULE -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O2 -m486 -c ewrk3.c"
+ * End:
+ */
+
diff --git a/i386/i386at/gpl/linux/net/ewrk3.h b/i386/i386at/gpl/linux/net/ewrk3.h
new file mode 100644
index 00000000..b37abf46
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ewrk3.h
@@ -0,0 +1,322 @@
+/*
+ Written 1994 by David C. Davies.
+
+ Copyright 1994 Digital Equipment Corporation.
+
+ This software may be used and distributed according to the terms of the
+ GNU Public License, incorporated herein by reference.
+
+ The author may be reached as davies@wanton.lkg.dec.com or Digital
+ Equipment Corporation, 550 King Street, Littleton MA 01460.
+
+ =========================================================================
+*/
+
+/*
+** I/O Address Register Map
+*/
+#define EWRK3_CSR iobase+0x00 /* Control and Status Register */
+#define EWRK3_CR iobase+0x01 /* Control Register */
+#define EWRK3_ICR iobase+0x02 /* Interrupt Control Register */
+#define EWRK3_TSR iobase+0x03 /* Transmit Status Register */
+#define EWRK3_RSVD1 iobase+0x04 /* RESERVED */
+#define EWRK3_RSVD2 iobase+0x05 /* RESERVED */
+#define EWRK3_FMQ iobase+0x06 /* Free Memory Queue */
+#define EWRK3_FMQC iobase+0x07 /* Free Memory Queue Counter */
+#define EWRK3_RQ iobase+0x08 /* Receive Queue */
+#define EWRK3_RQC iobase+0x09 /* Receive Queue Counter */
+#define EWRK3_TQ iobase+0x0a /* Transmit Queue */
+#define EWRK3_TQC iobase+0x0b /* Transmit Queue Counter */
+#define EWRK3_TDQ iobase+0x0c /* Transmit Done Queue */
+#define EWRK3_TDQC iobase+0x0d /* Transmit Done Queue Counter */
+#define EWRK3_PIR1 iobase+0x0e /* Page Index Register 1 */
+#define EWRK3_PIR2 iobase+0x0f /* Page Index Register 2 */
+#define EWRK3_DATA iobase+0x10 /* Data Register */
+#define EWRK3_IOPR iobase+0x11 /* I/O Page Register */
+#define EWRK3_IOBR iobase+0x12 /* I/O Base Register */
+#define EWRK3_MPR iobase+0x13 /* Memory Page Register */
+#define EWRK3_MBR iobase+0x14 /* Memory Base Register */
+#define EWRK3_APROM iobase+0x15 /* Address PROM */
+#define EWRK3_EPROM1 iobase+0x16 /* EEPROM Data Register 1 */
+#define EWRK3_EPROM2 iobase+0x17 /* EEPROM Data Register 2 */
+#define EWRK3_PAR0 iobase+0x18 /* Physical Address Register 0 */
+#define EWRK3_PAR1 iobase+0x19 /* Physical Address Register 1 */
+#define EWRK3_PAR2 iobase+0x1a /* Physical Address Register 2 */
+#define EWRK3_PAR3 iobase+0x1b /* Physical Address Register 3 */
+#define EWRK3_PAR4 iobase+0x1c /* Physical Address Register 4 */
+#define EWRK3_PAR5 iobase+0x1d /* Physical Address Register 5 */
+#define EWRK3_CMR iobase+0x1e /* Configuration/Management Register */
+
+/*
+** Control Page Map
+*/
+#define PAGE0_FMQ 0x000 /* Free Memory Queue */
+#define PAGE0_RQ 0x080 /* Receive Queue */
+#define PAGE0_TQ 0x100 /* Transmit Queue */
+#define PAGE0_TDQ 0x180 /* Transmit Done Queue */
+#define PAGE0_HTE 0x200 /* Hash Table Entries */
+#define PAGE0_RSVD 0x240 /* RESERVED */
+#define PAGE0_USRD 0x600 /* User Data */
+
+/*
+** Control and Status Register bit definitions (EWRK3_CSR)
+*/
+#define CSR_RA 0x80 /* Runt Accept */
+#define CSR_PME 0x40 /* Promiscuous Mode Enable */
+#define CSR_MCE 0x20 /* Multicast Enable */
+#define CSR_TNE 0x08 /* TX Done Queue Not Empty */
+#define CSR_RNE 0x04 /* RX Queue Not Empty */
+#define CSR_TXD 0x02 /* TX Disable */
+#define CSR_RXD 0x01 /* RX Disable */
+
+/*
+** Control Register bit definitions (EWRK3_CR)
+*/
+#define CR_APD 0x80 /* Auto Port Disable */
+#define CR_PSEL 0x40 /* Port Select (0->TP port) */
+#define CR_LBCK 0x20 /* LoopBaCK enable */
+#define CR_FDUP 0x10 /* Full DUPlex enable */
+#define CR_FBUS 0x08 /* Fast BUS enable (ISA clk > 8.33MHz) */
+#define CR_EN_16 0x04 /* ENable 16 bit memory accesses */
+#define CR_LED 0x02 /* LED (1-> turn on) */
+
+/*
+** Interrupt Control Register bit definitions (EWRK3_ICR)
+*/
+#define ICR_IE 0x80 /* Interrupt Enable */
+#define ICR_IS 0x60 /* Interrupt Selected */
+#define ICR_TNEM 0x08 /* TNE Mask (0->mask) */
+#define ICR_RNEM 0x04 /* RNE Mask (0->mask) */
+#define ICR_TXDM 0x02 /* TXD Mask (0->mask) */
+#define ICR_RXDM 0x01 /* RXD Mask (0->mask) */
+
+/*
+** Transmit Status Register bit definitions (EWRK3_TSR)
+*/
+#define TSR_NCL 0x80 /* No Carrier Loopback */
+#define TSR_ID 0x40 /* Initially Deferred */
+#define TSR_LCL 0x20 /* Late CoLlision */
+#define TSR_ECL 0x10 /* Excessive CoLlisions */
+#define TSR_RCNTR 0x0f /* Retries CouNTeR */
+
+/*
+** I/O Page Register bit definitions (EWRK3_IOPR)
+*/
+#define EEPROM_INIT 0xc0 /* EEPROM INIT command */
+#define EEPROM_WR_EN 0xc8 /* EEPROM WRITE ENABLE command */
+#define EEPROM_WR 0xd0 /* EEPROM WRITE command */
+#define EEPROM_WR_DIS 0xd8 /* EEPROM WRITE DISABLE command */
+#define EEPROM_RD 0xe0 /* EEPROM READ command */
+
+/*
+** I/O Base Register bit definitions (EWRK3_IOBR)
+*/
+#define EISA_REGS_EN 0x20 /* Enable EISA ID and Control Registers */
+#define EISA_IOB 0x1f /* Compare bits for I/O Base Address */
+
+/*
+** I/O Congiguration/Management Register bit definitions (EWRK3_CMR)
+*/
+#define CMR_RA 0x80 /* Read Ahead */
+#define CMR_WB 0x40 /* Write Behind */
+#define CMR_LINK 0x20 /* 0->TP */
+#define CMR_POLARITY 0x10 /* Informational */
+#define CMR_NO_EEPROM 0x0c /* NO_EEPROM<1:0> pin status */
+#define CMR_HS 0x08 /* Hard Strapped pin status (LeMAC2) */
+#define CMR_PNP 0x04 /* Plug 'n Play */
+#define CMR_DRAM 0x02 /* 0-> 1DRAM, 1-> 2 DRAM on board */
+#define CMR_0WS 0x01 /* Zero Wait State */
+
+/*
+** MAC Receive Status Register bit definitions
+*/
+
+#define R_ROK 0x80 /* Receive OK summary */
+#define R_IAM 0x10 /* Individual Address Match */
+#define R_MCM 0x08 /* MultiCast Match */
+#define R_DBE 0x04 /* Dribble Bit Error */
+#define R_CRC 0x02 /* CRC error */
+#define R_PLL 0x01 /* Phase Lock Lost */
+
+/*
+** MAC Transmit Control Register bit definitions
+*/
+
+#define TCR_SQEE 0x40 /* SQE Enable - look for heartbeat */
+#define TCR_SED 0x20 /* Stop when Error Detected */
+#define TCR_QMODE 0x10 /* Q_MODE */
+#define TCR_LAB 0x08 /* Less Aggressive Backoff */
+#define TCR_PAD 0x04 /* PAD Runt Packets */
+#define TCR_IFC 0x02 /* Insert Frame Check */
+#define TCR_ISA 0x01 /* Insert Source Address */
+
+/*
+** MAC Transmit Status Register bit definitions
+*/
+
+#define T_VSTS 0x80 /* Valid STatuS */
+#define T_CTU 0x40 /* Cut Through Used */
+#define T_SQE 0x20 /* Signal Quality Error */
+#define T_NCL 0x10 /* No Carrier Loopback */
+#define T_LCL 0x08 /* Late Collision */
+#define T_ID 0x04 /* Initially Deferred */
+#define T_COLL 0x03 /* COLLision status */
+#define T_XCOLL 0x03 /* Excessive Collisions */
+#define T_MCOLL 0x02 /* Multiple Collisions */
+#define T_OCOLL 0x01 /* One Collision */
+#define T_NOCOLL 0x00 /* No Collisions */
+#define T_XUR 0x03 /* Excessive Underruns */
+#define T_TXE 0x7f /* TX Errors */
+
+/*
+** EISA Configuration Register bit definitions
+*/
+
+#define EISA_ID iobase + 0x0c80 /* EISA ID Registers */
+#define EISA_ID0 iobase + 0x0c80 /* EISA ID Register 0 */
+#define EISA_ID1 iobase + 0x0c81 /* EISA ID Register 1 */
+#define EISA_ID2 iobase + 0x0c82 /* EISA ID Register 2 */
+#define EISA_ID3 iobase + 0x0c83 /* EISA ID Register 3 */
+#define EISA_CR iobase + 0x0c84 /* EISA Control Register */
+
+/*
+** EEPROM BYTES
+*/
+#define EEPROM_MEMB 0x00
+#define EEPROM_IOB 0x01
+#define EEPROM_EISA_ID0 0x02
+#define EEPROM_EISA_ID1 0x03
+#define EEPROM_EISA_ID2 0x04
+#define EEPROM_EISA_ID3 0x05
+#define EEPROM_MISC0 0x06
+#define EEPROM_MISC1 0x07
+#define EEPROM_PNAME7 0x08
+#define EEPROM_PNAME6 0x09
+#define EEPROM_PNAME5 0x0a
+#define EEPROM_PNAME4 0x0b
+#define EEPROM_PNAME3 0x0c
+#define EEPROM_PNAME2 0x0d
+#define EEPROM_PNAME1 0x0e
+#define EEPROM_PNAME0 0x0f
+#define EEPROM_SWFLAGS 0x10
+#define EEPROM_HWCAT 0x11
+#define EEPROM_NETMAN2 0x12
+#define EEPROM_REVLVL 0x13
+#define EEPROM_NETMAN0 0x14
+#define EEPROM_NETMAN1 0x15
+#define EEPROM_CHIPVER 0x16
+#define EEPROM_SETUP 0x17
+#define EEPROM_PADDR0 0x18
+#define EEPROM_PADDR1 0x19
+#define EEPROM_PADDR2 0x1a
+#define EEPROM_PADDR3 0x1b
+#define EEPROM_PADDR4 0x1c
+#define EEPROM_PADDR5 0x1d
+#define EEPROM_PA_CRC 0x1e
+#define EEPROM_CHKSUM 0x1f
+
+/*
+** EEPROM bytes for checksumming
+*/
+#define EEPROM_MAX 32 /* bytes */
+
+/*
+** EEPROM MISCELLANEOUS FLAGS
+*/
+#define RBE_SHADOW 0x0100 /* Remote Boot Enable Shadow */
+#define READ_AHEAD 0x0080 /* Read Ahead feature */
+#define IRQ_SEL2 0x0070 /* IRQ line selection (LeMAC2) */
+#define IRQ_SEL 0x0060 /* IRQ line selection */
+#define FAST_BUS 0x0008 /* ISA Bus speeds > 8.33MHz */
+#define ENA_16 0x0004 /* Enables 16 bit memory transfers */
+#define WRITE_BEHIND 0x0002 /* Write Behind feature */
+#define _0WS_ENA 0x0001 /* Zero Wait State Enable */
+
+/*
+** EEPROM NETWORK MANAGEMENT FLAGS
+*/
+#define NETMAN_POL 0x04 /* Polarity defeat */
+#define NETMAN_LINK 0x02 /* Link defeat */
+#define NETMAN_CCE 0x01 /* Custom Counters Enable */
+
+/*
+** EEPROM SW FLAGS
+*/
+#define SW_SQE 0x10 /* Signal Quality Error */
+#define SW_LAB 0x08 /* Less Aggressive Backoff */
+#define SW_INIT 0x04 /* Initialized */
+#define SW_TIMEOUT 0x02 /* 0:2.5 mins, 1: 30 secs */
+#define SW_REMOTE 0x01 /* Remote Boot Enable -> 1 */
+
+/*
+** EEPROM SETUP FLAGS
+*/
+#define SETUP_APD 0x80 /* AutoPort Disable */
+#define SETUP_PS 0x40 /* Port Select */
+#define SETUP_MP 0x20 /* MultiPort */
+#define SETUP_1TP 0x10 /* 1 port, TP */
+#define SETUP_1COAX 0x00 /* 1 port, Coax */
+#define SETUP_DRAM 0x02 /* Number of DRAMS on board */
+
+/*
+** EEPROM MANAGEMENT FLAGS
+*/
+#define MGMT_CCE 0x01 /* Custom Counters Enable */
+
+/*
+** EEPROM VERSIONS
+*/
+#define LeMAC 0x11
+#define LeMAC2 0x12
+
+/*
+** Miscellaneous
+*/
+
+#define EEPROM_WAIT_TIME 1000 /* Number of microseconds */
+#define EISA_EN 0x0001 /* Enable EISA bus buffers */
+
+#define HASH_TABLE_LEN 512 /* Bits */
+
+#define XCT 0x80 /* Transmit Cut Through */
+#define PRELOAD 16 /* 4 long words */
+
+#define MASK_INTERRUPTS 1
+#define UNMASK_INTERRUPTS 0
+
+#define EEPROM_OFFSET(a) ((u_short)((u_long)(a)))
+
+/*
+** Include the IOCTL stuff
+*/
+#include <linux/sockios.h>
+
+#define EWRK3IOCTL SIOCDEVPRIVATE
+
+struct ewrk3_ioctl {
+ unsigned short cmd; /* Command to run */
+ unsigned short len; /* Length of the data buffer */
+ unsigned char *data; /* Pointer to the data buffer */
+};
+
+/*
+** Recognised commands for the driver
+*/
+#define EWRK3_GET_HWADDR 0x01 /* Get the hardware address */
+#define EWRK3_SET_HWADDR 0x02 /* Get the hardware address */
+#define EWRK3_SET_PROM 0x03 /* Set Promiscuous Mode */
+#define EWRK3_CLR_PROM 0x04 /* Clear Promiscuous Mode */
+#define EWRK3_SAY_BOO 0x05 /* Say "Boo!" to the kernel log file */
+#define EWRK3_GET_MCA 0x06 /* Get a multicast address */
+#define EWRK3_SET_MCA 0x07 /* Set a multicast address */
+#define EWRK3_CLR_MCA 0x08 /* Clear a multicast address */
+#define EWRK3_MCA_EN 0x09 /* Enable a multicast address group */
+#define EWRK3_GET_STATS 0x0a /* Get the driver statistics */
+#define EWRK3_CLR_STATS 0x0b /* Zero out the driver statistics */
+#define EWRK3_GET_CSR 0x0c /* Get the CSR Register contents */
+#define EWRK3_SET_CSR 0x0d /* Set the CSR Register contents */
+#define EWRK3_GET_EEPROM 0x0e /* Get the EEPROM contents */
+#define EWRK3_SET_EEPROM 0x0f /* Set the EEPROM contents */
+#define EWRK3_GET_CMR 0x10 /* Get the CMR Register contents */
+#define EWRK3_CLR_TX_CUT_THRU 0x11 /* Clear the TX cut through mode */
+#define EWRK3_SET_TX_CUT_THRU 0x12 /* Set the TX cut through mode */
diff --git a/i386/i386at/gpl/linux/net/hp-plus.c b/i386/i386at/gpl/linux/net/hp-plus.c
new file mode 100644
index 00000000..aed7ee01
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp-plus.c
@@ -0,0 +1,483 @@
+/* hp-plus.c: A HP PCLAN/plus ethernet driver for linux. */
+/*
+ Written 1994 by Donald Becker.
+
+ This driver is for the Hewlett Packard PC LAN (27***) plus ethercards.
+ These cards are sold under several model numbers, usually 2724*.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ As is often the case, a great deal of credit is owed to Russ Nelson.
+ The Crynwr packet driver was my primary source of HP-specific
+ programming information.
+*/
+
+static const char *version =
+"hp-plus.c:v1.10 9/24/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/string.h> /* Important -- this inlines word moves. */
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hpplus_portlist[] =
+{0x200, 0x240, 0x280, 0x2C0, 0x300, 0x320, 0x340, 0};
+
+/*
+ The HP EtherTwist chip implementation is a fairly routine DP8390
+ implementation. It allows both shared memory and programmed-I/O buffer
+ access, using a custom interface for both. The programmed-I/O mode is
+ entirely implemented in the HP EtherTwist chip, bypassing the problem
+ ridden built-in 8390 facilities used on NE2000 designs. The shared
+ memory mode is likewise special, with an offset register used to make
+ packets appear at the shared memory base. Both modes use a base and bounds
+ page register to hide the Rx ring buffer wrap -- a packet that spans the
+ end of physical buffer memory appears continuous to the driver. (c.f. the
+ 3c503 and Cabletron E2100)
+
+ A special note: the internal buffer of the board is only 8 bits wide.
+ This lays several nasty traps for the unaware:
+ - the 8390 must be programmed for byte-wide operations
+ - all I/O and memory operations must work on whole words (the access
+ latches are serially preloaded and have no byte-swapping ability).
+
+ This board is laid out in I/O space much like the earlier HP boards:
+ the first 16 locations are for the board registers, and the second 16 are
+ for the 8390. The board is easy to identify, with both a dedicated 16 bit
+ ID register and a constant 0x530* value in the upper bits of the paging
+ register.
+*/
+
+#define HP_ID 0x00 /* ID register, always 0x4850. */
+#define HP_PAGING 0x02 /* Registers visible @ 8-f, see PageName. */
+#define HPP_OPTION 0x04 /* Bitmapped options, see HP_Option. */
+#define HPP_OUT_ADDR 0x08 /* I/O output location in Perf_Page. */
+#define HPP_IN_ADDR 0x0A /* I/O input location in Perf_Page. */
+#define HP_DATAPORT 0x0c /* I/O data transfer in Perf_Page. */
+#define NIC_OFFSET 0x10 /* Offset to the 8390 registers. */
+#define HP_IO_EXTENT 32
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* The register set selected in HP_PAGING. */
+enum PageName {
+ Perf_Page = 0, /* Normal operation. */
+ MAC_Page = 1, /* The ethernet address (+checksum). */
+ HW_Page = 2, /* EEPROM-loaded hardware parameters. */
+ LAN_Page = 4, /* Transceiver selection, testing, etc. */
+ ID_Page = 6 };
+
+/* The bit definitions for the HPP_OPTION register. */
+enum HP_Option {
+ NICReset = 1, ChipReset = 2, /* Active low, really UNreset. */
+ EnableIRQ = 4, FakeIntr = 8, BootROMEnb = 0x10, IOEnb = 0x20,
+ MemEnable = 0x40, ZeroWait = 0x80, MemDisable = 0x1000, };
+
+int hp_plus_probe(struct device *dev);
+int hpp_probe1(struct device *dev, int ioaddr);
+
+static void hpp_reset_8390(struct device *dev);
+static int hpp_open(struct device *dev);
+static int hpp_close(struct device *dev);
+static void hpp_mem_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hpp_mem_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void hpp_io_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void hpp_io_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static void hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+
+
+/* Probe a list of addresses for an HP LAN+ adaptor.
+ This routine is almost boilerplate. */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry hpplus_drv =
+{"hpplus", hpp_probe1, HP_IO_EXTENT, hpplus_portlist};
+#else
+
+int hp_plus_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return hpp_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; hpplus_portlist[i]; i++) {
+ int ioaddr = hpplus_portlist[i];
+ if (check_region(ioaddr, HP_IO_EXTENT))
+ continue;
+ if (hpp_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* Do the interesting part of the probe at a single address. */
+int hpp_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ unsigned char checksum = 0;
+ const char *name = "HP-PC-LAN+";
+ int mem_start;
+ static unsigned version_printed = 0;
+
+ /* Check for the HP+ signature, 50 48 0x 53. */
+ if (inw(ioaddr + HP_ID) != 0x4850
+ || (inw(ioaddr + HP_PAGING) & 0xfff0) != 0x5300)
+ return ENODEV;
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("hp-plus.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s at %#3x,", dev->name, name, ioaddr);
+
+ /* Retrieve and checksum the station address. */
+ outw(MAC_Page, ioaddr + HP_PAGING);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ unsigned char inval = inb(ioaddr + 8 + i);
+ dev->dev_addr[i] = inval;
+ checksum += inval;
+ printk(" %2.2x", inval);
+ }
+ checksum += inb(ioaddr + 14);
+
+ if (checksum != 0xff) {
+ printk(" bad checksum %2.2x.\n", checksum);
+ return ENODEV;
+ } else {
+ /* Point at the Software Configuration Flags. */
+ outw(ID_Page, ioaddr + HP_PAGING);
+ printk(" ID %4.4x", inw(ioaddr + 12));
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk ("hp-plus.c: unable to allocate memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ /* Grab the region so we can find another board if something fails. */
+ request_region(ioaddr, HP_IO_EXTENT,"hp-plus");
+
+ /* Read the IRQ line. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ {
+ int irq = inb(ioaddr + 13) & 0x0f;
+ int option = inw(ioaddr + HPP_OPTION);
+
+ dev->irq = irq;
+ if (option & MemEnable) {
+ mem_start = inw(ioaddr + 9) << 8;
+ printk(", IRQ %d, memory address %#x.\n", irq, mem_start);
+ } else {
+ mem_start = 0;
+ printk(", IRQ %d, programmed-I/O mode.\n", irq);
+ }
+ }
+
+ /* Set the wrap registers for string I/O reads. */
+ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+
+ dev->open = &hpp_open;
+ dev->stop = &hpp_close;
+
+ ei_status.name = name;
+ ei_status.word16 = 0; /* Agggghhhhh! Debug time: 2 days! */
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_2X_PAGES;
+ ei_status.stop_page = HP_STOP_PG;
+
+ ei_status.reset_8390 = &hpp_reset_8390;
+ ei_status.block_input = &hpp_io_block_input;
+ ei_status.block_output = &hpp_io_block_output;
+ ei_status.get_8390_hdr = &hpp_io_get_8390_hdr;
+
+ /* Check if the memory_enable flag is set in the option register. */
+ if (mem_start) {
+ ei_status.block_input = &hpp_mem_block_input;
+ ei_status.block_output = &hpp_mem_block_output;
+ ei_status.get_8390_hdr = &hpp_mem_get_8390_hdr;
+ dev->mem_start = mem_start;
+ dev->rmem_start = dev->mem_start + TX_2X_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (HP_STOP_PG - HP_START_PG)*256;
+ }
+
+ outw(Perf_Page, ioaddr + HP_PAGING);
+ NS8390_init(dev, 0);
+ /* Leave the 8390 and HP chip reset. */
+ outw(inw(ioaddr + HPP_OPTION) & ~EnableIRQ, ioaddr + HPP_OPTION);
+
+ return 0;
+}
+
+static int
+hpp_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg;
+
+ if (request_irq(dev->irq, &ei_interrupt, 0, "hp-plus")) {
+ return -EAGAIN;
+ }
+
+ /* Reset the 8390 and HP chip. */
+ option_reg = inw(ioaddr + HPP_OPTION);
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+ /* Unreset the board and enable interrupts. */
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ /* Set the wrap registers for programmed-I/O operation. */
+ outw(HW_Page, ioaddr + HP_PAGING);
+ outw((HP_START_PG + TX_2X_PAGES) | ((HP_STOP_PG - 1) << 8), ioaddr + 14);
+
+ /* Select the operational page. */
+ outw(Perf_Page, ioaddr + HP_PAGING);
+
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+hpp_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ ei_close(dev);
+ outw((option_reg & ~EnableIRQ) | MemDisable | NICReset | ChipReset,
+ ioaddr + HPP_OPTION);
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void
+hpp_reset_8390(struct device *dev)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+
+ outw(option_reg & ~(NICReset + ChipReset), ioaddr + HPP_OPTION);
+ /* Pause a few cycles for the hardware reset to take place. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ ei_status.txing = 0;
+ outw(option_reg | (EnableIRQ + NICReset + ChipReset), ioaddr + HPP_OPTION);
+
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+
+
+ if ((inb_p(ioaddr+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+ return;
+}
+
+/* The programmed-I/O version of reading the 4 byte 8390 specific header.
+ Note that transfer with the EtherTwist+ must be on word boundaries. */
+
+static void
+hpp_io_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+
+ outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. */
+
+static void
+hpp_io_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ char *buf = skb->data;
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+ insw(ioaddr + HP_DATAPORT, buf, count>>1);
+ if (count & 0x01)
+ buf[count-1] = inw(ioaddr + HP_DATAPORT);
+}
+
+/* The corresponding shared memory versions of the above 2 functions. */
+
+static void
+hpp_mem_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw((ring_page<<8), ioaddr + HPP_IN_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy_fromio(hdr, dev->mem_start, sizeof(struct e8390_pkt_hdr));
+ outw(option_reg, ioaddr + HPP_OPTION);
+ hdr->count = (hdr->count + 3) & ~3; /* Round up allocation. */
+}
+
+static void
+hpp_mem_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(ring_offset, ioaddr + HPP_IN_ADDR);
+
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+
+ /* Caution: this relies on get_8390_hdr() rounding up count!
+ Also note that we *can't* use eth_io_copy_and_sum() because
+ it will not always copy "count" bytes (e.g. padded IP). */
+
+ memcpy_fromio(skb->data, dev->mem_start, count);
+ outw(option_reg, ioaddr + HPP_OPTION);
+}
+
+/* A special note: we *must* always transfer >=16 bit words.
+ It's always safe to round up, so we do. */
+static void
+hpp_io_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outsl(ioaddr + HP_DATAPORT, buf, (count+3)>>2);
+ return;
+}
+
+static void
+hpp_mem_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ int option_reg = inw(ioaddr + HPP_OPTION);
+
+ outw(start_page << 8, ioaddr + HPP_OUT_ADDR);
+ outw(option_reg & ~(MemDisable + BootROMEnb), ioaddr + HPP_OPTION);
+ memcpy_toio(dev->mem_start, buf, (count + 3) & ~3);
+ outw(option_reg, ioaddr + HPP_OPTION);
+
+ return;
+}
+
+
+#ifdef MODULE
+#define MAX_HPP_CARDS 4 /* Max number of HPP cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HPP_CARDS] = { 0, };
+static struct device dev_hpp[MAX_HPP_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_HPP_CARDS] = { 0, };
+static int irq[MAX_HPP_CARDS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+ struct device *dev = &dev_hpp[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = hp_plus_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "hp-plus.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "hp-plus.c: No HP-Plus card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_HPP_CARDS; this_dev++) {
+ struct device *dev = &dev_hpp[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: hpp_close() handles free_irq + irq2dev map */
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(ioaddr, HP_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp-plus.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/hp.c b/i386/i386at/gpl/linux/net/hp.c
new file mode 100644
index 00000000..d0443a7d
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp.c
@@ -0,0 +1,451 @@
+/* hp.c: A HP LAN ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a driver for the HP PC-LAN adaptors.
+
+ Sources:
+ The Crynwr packet driver.
+*/
+
+static const char *version =
+ "hp.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int hppclan_portlist[] =
+{ 0x300, 0x320, 0x340, 0x280, 0x2C0, 0x200, 0x240, 0};
+
+#define HP_IO_EXTENT 32
+
+#define HP_DATAPORT 0x0c /* "Remote DMA" data port. */
+#define HP_ID 0x07
+#define HP_CONFIGURE 0x08 /* Configuration register. */
+#define HP_RUN 0x01 /* 1 == Run, 0 == reset. */
+#define HP_IRQ 0x0E /* Mask for software-configured IRQ line. */
+#define HP_DATAON 0x10 /* Turn on dataport */
+#define NIC_OFFSET 0x10 /* Offset the 8390 registers. */
+
+#define HP_START_PG 0x00 /* First page of TX buffer */
+#define HP_8BSTOP_PG 0x80 /* Last page +1 of RX ring */
+#define HP_16BSTOP_PG 0xFF /* Same, for 16 bit cards. */
+
+int hp_probe(struct device *dev);
+int hp_probe1(struct device *dev, int ioaddr);
+
+static int hp_open(struct device *dev);
+static int hp_close(struct device *dev);
+static void hp_reset_8390(struct device *dev);
+static void hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void hp_block_input(struct device *dev, int count,
+ struct sk_buff *skb , int ring_offset);
+static void hp_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+
+static void hp_init_card(struct device *dev);
+
+/* The map from IRQ number to HP_CONFIGURE register setting. */
+/* My default is IRQ5 0 1 2 3 4 5 6 7 8 9 10 11 */
+static char irqmap[16] = { 0, 0, 4, 6, 8,10, 0,14, 0, 4, 2,12,0,0,0,0};
+
+
+/* Probe for an HP LAN adaptor.
+ Also initialize the card and fill in STATION_ADDR with the station
+ address. */
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"hp", hp_probe1, HP_IO_EXTENT, hppclan_portlist};
+#else
+
+int hp_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return hp_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; hppclan_portlist[i]; i++) {
+ int ioaddr = hppclan_portlist[i];
+ if (check_region(ioaddr, HP_IO_EXTENT))
+ continue;
+ if (hp_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+int hp_probe1(struct device *dev, int ioaddr)
+{
+ int i, board_id, wordmode;
+ const char *name;
+ static unsigned version_printed = 0;
+
+ /* Check for the HP physical address, 08 00 09 xx xx xx. */
+ /* This really isn't good enough: we may pick up HP LANCE boards
+ also! Avoid the lance 0x5757 signature. */
+ if (inb(ioaddr) != 0x08
+ || inb(ioaddr+1) != 0x00
+ || inb(ioaddr+2) != 0x09
+ || inb(ioaddr+14) == 0x57)
+ return ENODEV;
+
+ /* Set up the parameters based on the board ID.
+ If you have additional mappings, please mail them to me -djb. */
+ if ((board_id = inb(ioaddr + HP_ID)) & 0x80) {
+ name = "HP27247";
+ wordmode = 1;
+ } else {
+ name = "HP27250";
+ wordmode = 0;
+ }
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("hp.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s (ID %02x) at %#3x,", dev->name, name, board_id, ioaddr);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ /* Snarf the interrupt now. Someday this could be moved to open(). */
+ if (dev->irq < 2) {
+ int irq_16list[] = { 11, 10, 5, 3, 4, 7, 9, 0};
+ int irq_8list[] = { 7, 5, 3, 4, 9, 0};
+ int *irqp = wordmode ? irq_16list : irq_8list;
+ do {
+ int irq = *irqp;
+ if (request_irq (irq, NULL, 0, "bogus") != -EBUSY) {
+ autoirq_setup(0);
+ /* Twinkle the interrupt, and check if it's seen. */
+ outb_p(irqmap[irq] | HP_RUN, ioaddr + HP_CONFIGURE);
+ outb_p( 0x00 | HP_RUN, ioaddr + HP_CONFIGURE);
+ if (irq == autoirq_report(0) /* It's a good IRQ line! */
+ && request_irq (irq, &ei_interrupt, 0, "hp") == 0) {
+ printk(" selecting IRQ %d.\n", irq);
+ dev->irq = *irqp;
+ break;
+ }
+ }
+ } while (*++irqp);
+ if (*irqp == 0) {
+ printk(" no free IRQ lines.\n");
+ return EBUSY;
+ }
+ } else {
+ if (dev->irq == 2)
+ dev->irq = 9;
+ if (request_irq(dev->irq, ei_interrupt, 0, "hp")) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return EBUSY;
+ }
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ /* Grab the region so we can find another board if something fails. */
+ request_region(ioaddr, HP_IO_EXTENT,"hp");
+
+ /* Set the base address to point to the NIC, not the "real" base! */
+ dev->base_addr = ioaddr + NIC_OFFSET;
+ dev->open = &hp_open;
+ dev->stop = &hp_close;
+
+ ei_status.name = name;
+ ei_status.word16 = wordmode;
+ ei_status.tx_start_page = HP_START_PG;
+ ei_status.rx_start_page = HP_START_PG + TX_PAGES;
+ ei_status.stop_page = wordmode ? HP_16BSTOP_PG : HP_8BSTOP_PG;
+
+ ei_status.reset_8390 = &hp_reset_8390;
+ ei_status.get_8390_hdr = &hp_get_8390_hdr;
+ ei_status.block_input = &hp_block_input;
+ ei_status.block_output = &hp_block_output;
+ hp_init_card(dev);
+
+ return 0;
+}
+
+static int
+hp_open(struct device *dev)
+{
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+hp_close(struct device *dev)
+{
+ ei_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static void
+hp_reset_8390(struct device *dev)
+{
+ int hp_base = dev->base_addr - NIC_OFFSET;
+ int saved_config = inb_p(hp_base + HP_CONFIGURE);
+
+ if (ei_debug > 1) printk("resetting the 8390 time=%ld...", jiffies);
+ outb_p(0x00, hp_base + HP_CONFIGURE);
+ ei_status.txing = 0;
+ /* Pause just a few cycles for the hardware reset to take place. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ outb_p(saved_config, hp_base + HP_CONFIGURE);
+ SLOW_DOWN_IO; SLOW_DOWN_IO;
+
+ if ((inb_p(hp_base+NIC_OFFSET+EN0_ISR) & ENISR_RESET) == 0)
+ printk("%s: hp_reset_8390() did not complete.\n", dev->name);
+
+ if (ei_debug > 1) printk("8390 reset done (%ld).", jiffies);
+ return;
+}
+
+static void
+hp_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base);
+
+ if (ei_status.word16)
+ insw(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(nic_base - NIC_OFFSET + HP_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you are
+ porting to a new ethercard look at the packet driver source for hints.
+ The HP LAN doesn't use shared memory -- we put the packet
+ out through the "remote DMA" dataport. */
+
+static void
+hp_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+ int xfer_count = count;
+ char *buf = skb->data;
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base);
+ if (ei_status.word16) {
+ insw(nic_base - NIC_OFFSET + HP_DATAPORT,buf,count>>1);
+ if (count & 0x01)
+ buf[count-1] = inb(nic_base - NIC_OFFSET + HP_DATAPORT), xfer_count++;
+ } else {
+ insb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ /* Check only the lower 8 bits so we can ignore ring wrap. */
+ if (((ring_offset + xfer_count) & 0xff) != (addr & 0xff))
+ printk("%s: RX transfer address mismatch, %#4.4x vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+}
+
+static void
+hp_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page)
+{
+ int nic_base = dev->base_addr;
+ int saved_config = inb_p(nic_base - NIC_OFFSET + HP_CONFIGURE);
+
+ outb_p(saved_config | HP_DATAON, nic_base - NIC_OFFSET + HP_CONFIGURE);
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base);
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work. */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0xff, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+#define NE_CMD 0x00
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ inb_p(0x61);
+ inb_p(0x61);
+#endif
+
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base);
+ if (ei_status.word16) {
+ /* Use the 'rep' sequence for 16 bit boards. */
+ outsw(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count>>1);
+ } else {
+ outsb(nic_base - NIC_OFFSET + HP_DATAPORT, buf, count);
+ }
+
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here -- it's broken! */
+
+ /* This is for the ALPHA version only, remove for later releases. */
+ if (ei_debug > 0) { /* DMA termination address check... */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ int addr = (high << 8) + low;
+ if ((start_page << 8) + count != addr)
+ printk("%s: TX Transfer address mismatch, %#4.4x vs. %#4.4x.\n",
+ dev->name, (start_page << 8) + count, addr);
+ }
+ outb_p(saved_config & (~HP_DATAON), nic_base - NIC_OFFSET + HP_CONFIGURE);
+ return;
+}
+
+/* This function resets the ethercard if something screws up. */
+static void
+hp_init_card(struct device *dev)
+{
+ int irq = dev->irq;
+ NS8390_init(dev, 0);
+ outb_p(irqmap[irq&0x0f] | HP_RUN,
+ dev->base_addr - NIC_OFFSET + HP_CONFIGURE);
+ return;
+}
+
+#ifdef MODULE
+#define MAX_HP_CARDS 4 /* Max number of HP cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_HP_CARDS] = { 0, };
+static struct device dev_hp[MAX_HP_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_HP_CARDS] = { 0, };
+static int irq[MAX_HP_CARDS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+ struct device *dev = &dev_hp[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = hp_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "hp.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "hp.c: No HP card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_HP_CARDS; this_dev++) {
+ struct device *dev = &dev_hp[this_dev];
+ if (dev->priv != NULL) {
+ int ioaddr = dev->base_addr - NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(ioaddr, HP_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c hp.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * c-indent-level: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/hp100.c b/i386/i386at/gpl/linux/net/hp100.c
new file mode 100644
index 00000000..d8186bf1
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp100.c
@@ -0,0 +1,1144 @@
+/*
+ * hp100.c: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
+ *
+ * Author: Jaroslav Kysela, <perex@pf.jcu.cz>
+ *
+ * Supports only the following Hewlett Packard cards:
+ *
+ * HP J2577 10/100 EISA card with REVA Cascade chip
+ * HP J2573 10/100 ISA card with REVA Cascade chip
+ * HP 27248B 10 only EISA card with Cascade chip
+ * HP J2577 10/100 EISA card with Cascade chip
+ * HP J2573 10/100 ISA card with Cascade chip
+ * HP J2585 10/100 PCI card
+ *
+ * Other ATT2MD01 Chip based boards might be supported in the future
+ * (there are some minor changes needed).
+ *
+ * This driver is based on the 'hpfepkt' crynwr packet driver.
+ *
+ * This source/code is public free; you can distribute it and/or modify
+ * it under terms of the GNU General Public License (published by the
+ * Free Software Foundation) either version two of this License, or any
+ * later version.
+ * ----------------------------------------------------------------------------
+ *
+ * Note: Some routines (interrupt handling, transmit) assumes that
+ * there is the PERFORMANCE page selected...
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * If you are going to use the module version of this driver, you may
+ * change this values at the "insert time" :
+ *
+ * Variable Description
+ *
+ * hp100_rx_ratio Range 1-99 - onboard memory used for RX
+ * packets in %.
+ * hp100_priority_tx If this variable is nonzero - all outgoing
+ * packets will be transmitted as priority.
+ * hp100_port Adapter port (for example 0x380).
+ *
+ * ----------------------------------------------------------------------------
+ * MY BEST REGARDS GOING TO:
+ *
+ * IPEX s.r.o which lend me two HP J2573 cards and
+ * the HP AdvanceStack 100VG Hub-15 for debugging.
+ *
+ * Russel Nellson <nelson@crynwr.com> for help with obtaining sources
+ * of the 'hpfepkt' packet driver.
+ *
+ * Also thanks to Abacus Electric s.r.o which let me to use their
+ * motherboard for my second computer.
+ *
+ * ----------------------------------------------------------------------------
+ *
+ * TO DO:
+ * ======
+ * - ioctl handling - some runtime setup things
+ * - 100Mb/s Voice Grade AnyLAN network adapter/hub services support
+ * - 802.5 frames
+ * - promiscuous mode
+ * - bridge mode
+ * - cascaded repeater mode
+ * - 100Mbit MAC
+ *
+ * Revision history:
+ * =================
+ *
+ * Version Date Description
+ *
+ * 0.1 14-May-95 Initial writing. ALPHA code was released.
+ * Only HP J2573 on 10Mb/s (two machines) tested.
+ * 0.11 14-Jun-95 Reset interface bug fixed?
+ * Little bug in hp100_close function fixed.
+ * 100Mb/s connection debugged.
+ * 0.12 14-Jul-95 Link down is now handled better.
+ * 0.20 01-Aug-95 Added PCI support for HP J2585A card.
+ * Statistics bug fixed.
+ * 0.21 04-Aug-95 Memory mapped access support for PCI card.
+ * Added priority transmit support for 100Mb/s
+ * Voice Grade AnyLAN network.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include <linux/types.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+#include "hp100.h"
+
+/*
+ * defines
+ */
+
+#define HP100_BUS_ISA 0
+#define HP100_BUS_EISA 1
+#define HP100_BUS_PCI 2
+
+#define HP100_REGION_SIZE 0x20
+
+#define HP100_MAX_PACKET_SIZE (1536+4)
+#define HP100_MIN_PACKET_SIZE 60
+
+#ifndef HP100_DEFAULT_RX_RATIO
+/* default - 65% onboard memory on the card are used for RX packets */
+#define HP100_DEFAULT_RX_RATIO 65
+#endif
+
+#ifndef HP100_DEFAULT_PRIORITY_TX
+/* default - don't enable transmit outgoing packets as priority */
+#define HP100_DEFAULT_PRIORITY_TX 0
+#endif
+
+#ifdef MACH
+#define HP100_IO_MAPPED
+#endif
+
+/*
+ * structures
+ */
+
+struct hp100_eisa_id {
+ u_int id;
+ const char *name;
+ u_char bus;
+};
+
+struct hp100_private {
+ struct hp100_eisa_id *id;
+ u_short soft_model;
+ u_int memory_size;
+ u_short rx_ratio; /* 1 - 99 */
+ u_short priority_tx; /* != 0 - priority tx */
+ short mem_mapped; /* memory mapped access */
+ u_char *mem_ptr_virt; /* virtual memory mapped area, maybe NULL */
+ u_char *mem_ptr_phys; /* physical memory mapped area */
+ short lan_type; /* 10Mb/s, 100Mb/s or -1 (error) */
+ int hub_status; /* login to hub was successfull? */
+ u_char mac1_mode;
+ u_char mac2_mode;
+ struct enet_statistics stats;
+};
+
+/*
+ * variables
+ */
+
+static struct hp100_eisa_id hp100_eisa_ids[] = {
+
+ /* 10/100 EISA card with REVA Cascade chip */
+ { 0x080F1F022, "HP J2577 rev A", HP100_BUS_EISA },
+
+ /* 10/100 ISA card with REVA Cascade chip */
+ { 0x050F1F022, "HP J2573 rev A", HP100_BUS_ISA },
+
+ /* 10 only EISA card with Cascade chip */
+ { 0x02019F022, "HP 27248B", HP100_BUS_EISA },
+
+ /* 10/100 EISA card with Cascade chip */
+ { 0x04019F022, "HP J2577", HP100_BUS_EISA },
+
+ /* 10/100 ISA card with Cascade chip */
+ { 0x05019F022, "HP J2573", HP100_BUS_ISA },
+
+ /* 10/100 PCI card */
+ /* Note: ID for this card is same as PCI vendor/device numbers. */
+ { 0x01030103c, "HP J2585", HP100_BUS_PCI },
+};
+
+int hp100_rx_ratio = HP100_DEFAULT_RX_RATIO;
+int hp100_priority_tx = HP100_DEFAULT_PRIORITY_TX;
+
+/*
+ * prototypes
+ */
+
+static int hp100_probe1( struct device *dev, int ioaddr, int bus );
+static int hp100_open( struct device *dev );
+static int hp100_close( struct device *dev );
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev );
+static void hp100_rx( struct device *dev );
+static struct enet_statistics *hp100_get_stats( struct device *dev );
+static void hp100_update_stats( struct device *dev );
+static void hp100_clear_stats( int ioaddr );
+static void hp100_set_multicast_list( struct device *dev);
+static void hp100_interrupt( int irq, struct pt_regs *regs );
+
+static void hp100_start_interface( struct device *dev );
+static void hp100_stop_interface( struct device *dev );
+static void hp100_load_eeprom( struct device *dev );
+static int hp100_sense_lan( struct device *dev );
+static int hp100_login_to_vg_hub( struct device *dev );
+static int hp100_down_vg_link( struct device *dev );
+
+/*
+ * probe functions
+ */
+
+int hp100_probe( struct device *dev )
+{
+ int base_addr = dev ? dev -> base_addr : 0;
+ int ioaddr;
+#ifdef CONFIG_PCI
+ int pci_start_index = 0;
+#endif
+
+ if ( base_addr > 0xff ) /* Check a single specified location. */
+ {
+ if ( check_region( base_addr, HP100_REGION_SIZE ) ) return -EINVAL;
+ if ( base_addr < 0x400 )
+ return hp100_probe1( dev, base_addr, HP100_BUS_ISA );
+ else
+ return hp100_probe1( dev, base_addr, HP100_BUS_EISA );
+ }
+ else
+#ifdef CONFIG_PCI
+ if ( base_addr > 0 && base_addr < 8 + 1 )
+ pci_start_index = 0x100 | ( base_addr - 1 );
+ else
+#endif
+ if ( base_addr != 0 ) return -ENXIO;
+
+ /* at first - scan PCI bus(es) */
+
+#ifdef CONFIG_PCI
+ if ( pcibios_present() )
+ {
+ int pci_index;
+
+#ifdef HP100_DEBUG_PCI
+ printk( "hp100: PCI BIOS is present, checking for devices..\n" );
+#endif
+ for ( pci_index = pci_start_index & 7; pci_index < 8; pci_index++ )
+ {
+ u_char pci_bus, pci_device_fn;
+ u_short pci_command;
+
+ if ( pcibios_find_device( PCI_VENDOR_ID_HP, PCI_DEVICE_ID_HP_J2585A,
+ pci_index, &pci_bus,
+ &pci_device_fn ) != 0 ) break;
+ pcibios_read_config_dword( pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &ioaddr );
+
+ ioaddr &= ~3; /* remove I/O space marker in bit 0. */
+
+ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+
+ pcibios_read_config_word( pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command );
+ if ( !( pci_command & PCI_COMMAND_MASTER ) )
+ {
+#ifdef HP100_DEBUG_PCI
+ printk( "hp100: PCI Master Bit has not been set. Setting...\n" );
+#endif
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word( pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command );
+ }
+#ifdef HP100_DEBUG_PCI
+ printk( "hp100: PCI adapter found at 0x%x\n", ioaddr );
+#endif
+ if ( hp100_probe1( dev, ioaddr, HP100_BUS_PCI ) == 0 ) return 0;
+ }
+ }
+ if ( pci_start_index > 0 ) return -ENODEV;
+#endif /* CONFIG_PCI */
+
+ /* at second - probe all EISA possible port regions (if EISA bus present) */
+
+ for ( ioaddr = 0x1c38; EISA_bus && ioaddr < 0x10000; ioaddr += 0x400 )
+ {
+ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+ if ( hp100_probe1( dev, ioaddr, HP100_BUS_EISA ) == 0 ) return 0;
+ }
+
+ /* at third - probe all ISA possible port regions */
+
+ for ( ioaddr = 0x100; ioaddr < 0x400; ioaddr += 0x20 )
+ {
+ if ( check_region( ioaddr, HP100_REGION_SIZE ) ) continue;
+ if ( hp100_probe1( dev, ioaddr, HP100_BUS_ISA ) == 0 ) return 0;
+ }
+
+ return -ENODEV;
+}
+
+static int hp100_probe1( struct device *dev, int ioaddr, int bus )
+{
+ int i;
+ u_char uc, uc_1;
+ u_int eisa_id;
+ short mem_mapped;
+ u_char *mem_ptr_phys, *mem_ptr_virt;
+ struct hp100_private *lp;
+ struct hp100_eisa_id *eid;
+
+ if ( dev == NULL )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_probe1: dev == NULL ?\n" );
+#endif
+ return EIO;
+ }
+
+ if ( bus != HP100_BUS_PCI ) /* don't check PCI cards again */
+ if ( inb( ioaddr + 0 ) != HP100_HW_ID_0 ||
+ inb( ioaddr + 1 ) != HP100_HW_ID_1 ||
+ ( inb( ioaddr + 2 ) & 0xf0 ) != HP100_HW_ID_2_REVA ||
+ inb( ioaddr + 3 ) != HP100_HW_ID_3 )
+ return -ENODEV;
+
+ dev -> base_addr = ioaddr;
+
+#ifdef HP100_DEBUG_PROBE1
+ printk( "hp100_probe1: card found at port 0x%x\n", ioaddr );
+#endif
+
+ hp100_page( ID_MAC_ADDR );
+ for ( i = uc = eisa_id = 0; i < 4; i++ )
+ {
+ eisa_id >>= 8;
+ uc_1 = hp100_inb( BOARD_ID + i );
+ eisa_id |= uc_1 << 24;
+ uc += uc_1;
+ }
+ uc += hp100_inb( BOARD_ID + 4 );
+
+#ifdef HP100_DEBUG_PROBE1
+ printk( "hp100_probe1: EISA ID = 0x%08x checksum = 0x%02x\n", eisa_id, uc );
+#endif
+
+ if ( uc != 0xff ) /* bad checksum? */
+ {
+ printk( "hp100_probe: bad EISA ID checksum at base port 0x%x\n", ioaddr );
+ return -ENODEV;
+ }
+
+ for ( i = 0; i < sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ); i++ )
+ if ( ( hp100_eisa_ids[ i ].id & 0xf0ffffff ) == ( eisa_id & 0xf0ffffff ) )
+ break;
+ if ( i >= sizeof( hp100_eisa_ids ) / sizeof( struct hp100_eisa_id ) )
+ {
+ printk( "hp100_probe1: card at port 0x%x isn't known (id = 0x%x)\n", ioaddr, eisa_id );
+ return -ENODEV;
+ }
+ eid = &hp100_eisa_ids[ i ];
+ if ( ( eid -> id & 0x0f000000 ) < ( eisa_id & 0x0f000000 ) )
+ {
+ printk( "hp100_probe1: newer version of card %s at port 0x%x - unsupported\n",
+ eid -> name, ioaddr );
+ return -ENODEV;
+ }
+
+ for ( i = uc = 0; i < 7; i++ )
+ uc += hp100_inb( LAN_ADDR + i );
+ if ( uc != 0xff )
+ {
+ printk( "hp100_probe1: bad lan address checksum (card %s at port 0x%x)\n",
+ eid -> name, ioaddr );
+ return -EIO;
+ }
+
+#ifndef HP100_IO_MAPPED
+ hp100_page( HW_MAP );
+ mem_mapped = ( hp100_inw( OPTION_LSW ) &
+ ( HP100_MEM_EN | HP100_BM_WRITE | HP100_BM_READ ) ) != 0;
+ mem_ptr_phys = mem_ptr_virt = NULL;
+ if ( mem_mapped )
+ {
+ mem_ptr_phys = (u_char *)( hp100_inw( MEM_MAP_LSW ) |
+ ( hp100_inw( MEM_MAP_MSW ) << 16 ) );
+ (u_int)mem_ptr_phys &= ~0x1fff; /* 8k aligment */
+ if ( bus == HP100_BUS_ISA && ( (u_long)mem_ptr_phys & ~0xfffff ) != 0 )
+ {
+ mem_ptr_phys = NULL;
+ mem_mapped = 0;
+ }
+ if ( mem_mapped && bus == HP100_BUS_PCI )
+ {
+ if ( ( mem_ptr_virt = vremap( (u_long)mem_ptr_phys, 0x2000 ) ) == NULL )
+ {
+ printk( "hp100: vremap for high PCI memory at 0x%lx failed\n", (u_long)mem_ptr_phys );
+ mem_ptr_phys = NULL;
+ mem_mapped = 0;
+ }
+ }
+ }
+#else
+ mem_mapped = 0;
+ mem_ptr_phys = mem_ptr_virt = NULL;
+#endif
+
+ if ( ( dev -> priv = kmalloc( sizeof( struct hp100_private ), GFP_KERNEL ) ) == NULL )
+ return -ENOMEM;
+ memset( dev -> priv, 0, sizeof( struct hp100_private ) );
+
+ lp = (struct hp100_private *)dev -> priv;
+ lp -> id = eid;
+ lp -> mem_mapped = mem_mapped;
+ lp -> mem_ptr_phys = mem_ptr_phys;
+ lp -> mem_ptr_virt = mem_ptr_virt;
+ hp100_page( ID_MAC_ADDR );
+ lp -> soft_model = hp100_inb( SOFT_MODEL );
+ lp -> mac1_mode = HP100_MAC1MODE3;
+ lp -> mac2_mode = HP100_MAC2MODE3;
+
+ dev -> base_addr = ioaddr;
+ hp100_page( HW_MAP );
+ dev -> irq = hp100_inb( IRQ_CHANNEL ) & HP100_IRQ_MASK;
+ if ( dev -> irq == 2 ) dev -> irq = 9;
+ lp -> memory_size = 0x200 << ( ( hp100_inb( SRAM ) & 0xe0 ) >> 5 );
+ lp -> rx_ratio = hp100_rx_ratio;
+
+ dev -> open = hp100_open;
+ dev -> stop = hp100_close;
+ dev -> hard_start_xmit = hp100_start_xmit;
+ dev -> get_stats = hp100_get_stats;
+ dev -> set_multicast_list = &hp100_set_multicast_list;
+
+ request_region( dev -> base_addr, HP100_REGION_SIZE, eid -> name );
+
+ hp100_page( ID_MAC_ADDR );
+ for ( i = uc = 0; i < 6; i++ )
+ dev -> dev_addr[ i ] = hp100_inb( LAN_ADDR + i );
+
+ hp100_clear_stats( ioaddr );
+
+ ether_setup( dev );
+
+ lp -> lan_type = hp100_sense_lan( dev );
+
+ printk( "%s: %s at 0x%x, IRQ %d, ",
+ dev -> name, lp -> id -> name, ioaddr, dev -> irq );
+ switch ( bus ) {
+ case HP100_BUS_EISA: printk( "EISA" ); break;
+ case HP100_BUS_PCI: printk( "PCI" ); break;
+ default: printk( "ISA" ); break;
+ }
+ printk( " bus, %dk SRAM (rx/tx %d%%).\n",
+ lp -> memory_size >> ( 10 - 4 ), lp -> rx_ratio );
+ if ( mem_mapped )
+ {
+ printk( "%s: Memory area at 0x%lx-0x%lx",
+ dev -> name, (u_long)mem_ptr_phys, (u_long)mem_ptr_phys + 0x1fff );
+ if ( mem_ptr_virt )
+ printk( " (virtual base 0x%lx)", (u_long)mem_ptr_virt );
+ printk( ".\n" );
+ }
+ printk( "%s: ", dev -> name );
+ if ( lp -> lan_type != HP100_LAN_ERR )
+ printk( "Adapter is attached to " );
+ switch ( lp -> lan_type ) {
+ case HP100_LAN_100:
+ printk( "100Mb/s Voice Grade AnyLAN network.\n" );
+ break;
+ case HP100_LAN_10:
+ printk( "10Mb/s network.\n" );
+ break;
+ default:
+ printk( "Warning! Link down.\n" );
+ }
+
+ hp100_stop_interface( dev );
+
+ return 0;
+}
+
+/*
+ * open/close functions
+ */
+
+static int hp100_open( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ if ( request_irq( dev -> irq, hp100_interrupt, SA_INTERRUPT, lp -> id -> name ) )
+ {
+ printk( "%s: unable to get IRQ %d\n", dev -> name, dev -> irq );
+ return -EAGAIN;
+ }
+ irq2dev_map[ dev -> irq ] = dev;
+
+ MOD_INC_USE_COUNT;
+
+ dev -> tbusy = 0;
+ dev -> trans_start = jiffies;
+ dev -> interrupt = 0;
+ dev -> start = 1;
+
+ lp -> lan_type = hp100_sense_lan( dev );
+ lp -> mac1_mode = HP100_MAC1MODE3;
+ lp -> mac2_mode = HP100_MAC2MODE3;
+
+ hp100_page( MAC_CTRL );
+ hp100_orw( HP100_LINK_BEAT_DIS | HP100_RESET_LB, LAN_CFG_10 );
+
+ hp100_stop_interface( dev );
+ hp100_load_eeprom( dev );
+
+ hp100_outw( HP100_MMAP_DIS | HP100_SET_HB |
+ HP100_IO_EN | HP100_SET_LB, OPTION_LSW );
+ hp100_outw( HP100_DEBUG_EN | HP100_RX_HDR | HP100_EE_EN | HP100_RESET_HB |
+ HP100_FAKE_INT | HP100_RESET_LB, OPTION_LSW );
+ hp100_outw( HP100_ADV_NXT_PKT | HP100_TX_CMD | HP100_RESET_LB |
+ HP100_PRIORITY_TX | ( hp100_priority_tx ? HP100_SET_HB : HP100_RESET_HB ),
+ OPTION_MSW );
+
+ hp100_page( MAC_ADDRESS );
+ for ( i = 0; i < 6; i++ )
+ hp100_outb( dev -> dev_addr[ i ], MAC_ADDR + i );
+ for ( i = 0; i < 8; i++ ) /* setup multicast filter to receive all */
+ hp100_outb( 0xff, HASH_BYTE0 + i );
+ hp100_page( PERFORMANCE );
+ hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all ints */
+ hp100_outw( 0xffff, IRQ_STATUS ); /* ack IRQ */
+ hp100_outw( (HP100_RX_PACKET | HP100_RX_ERROR | HP100_SET_HB) |
+ (HP100_TX_ERROR | HP100_SET_LB ), IRQ_MASK );
+ /* and enable few */
+ hp100_reset_card();
+ hp100_page( MMU_CFG );
+ hp100_outw( ( lp -> memory_size * lp -> rx_ratio ) / 100, RX_MEM_STOP );
+ hp100_outw( lp -> memory_size - 1, TX_MEM_STOP );
+ hp100_unreset_card();
+
+ if ( lp -> lan_type == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+
+ hp100_start_interface( dev );
+
+ return 0;
+}
+
+static int hp100_close( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ hp100_page( PERFORMANCE );
+ hp100_outw( 0xfefe, IRQ_MASK ); /* mask off all IRQs */
+
+ hp100_stop_interface( dev );
+
+ if ( lp -> lan_type == HP100_LAN_100 ) /* relogin */
+ hp100_login_to_vg_hub( dev );
+
+ dev -> tbusy = 1;
+ dev -> start = 0;
+
+ free_irq( dev -> irq );
+ irq2dev_map[ dev -> irq ] = NULL;
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * transmit
+ */
+
+static int hp100_start_xmit( struct sk_buff *skb, struct device *dev )
+{
+ int i, ok_flag;
+ int ioaddr = dev -> base_addr;
+ u_short val;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ if ( lp -> lan_type < 0 )
+ {
+ hp100_stop_interface( dev );
+ if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) < 0 )
+ {
+ printk( "%s: no connection found - check wire\n", dev -> name );
+ hp100_start_interface( dev ); /* 10Mb/s RX packets maybe handled */
+ return -EIO;
+ }
+ if ( lp -> lan_type == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_start_interface( dev );
+ }
+
+ if ( ( i = ( hp100_inl( TX_MEM_FREE ) & ~0x7fffffff ) ) < skb -> len + 16 )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_start_xmit: rx free mem = 0x%x\n", i );
+#endif
+ if ( jiffies - dev -> trans_start < 2 * HZ ) return -EAGAIN;
+ if ( lp -> lan_type == HP100_LAN_100 && lp -> hub_status < 0 )
+ /* 100Mb/s adapter isn't connected to hub */
+ {
+ printk( "%s: login to 100Mb/s hub retry\n", dev -> name );
+ hp100_stop_interface( dev );
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_start_interface( dev );
+ }
+ else
+ {
+ hp100_ints_off();
+ i = hp100_sense_lan( dev );
+ hp100_page( PERFORMANCE );
+ hp100_ints_on();
+ if ( i == HP100_LAN_ERR )
+ printk( "%s: link down detected\n", dev -> name );
+ else
+ if ( lp -> lan_type != i )
+ {
+ /* it's very heavy - all network setting must be changed!!! */
+ printk( "%s: cable change 10Mb/s <-> 100Mb/s detected\n", dev -> name );
+ lp -> lan_type = i;
+ hp100_stop_interface( dev );
+ if ( lp -> lan_type == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_start_interface( dev );
+ }
+ else
+ {
+ printk( "%s: interface reset\n", dev -> name );
+ hp100_stop_interface( dev );
+ hp100_start_interface( dev );
+ }
+ }
+ dev -> trans_start = jiffies;
+ return -EAGAIN;
+ }
+
+ if ( skb == NULL )
+ {
+ dev_tint( dev );
+ return 0;
+ }
+
+ if ( skb -> len <= 0 ) return 0;
+
+ for ( i = 0; i < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_TX_CMD ); i++ )
+ {
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_start_xmit: busy\n" );
+#endif
+ }
+
+ hp100_ints_off();
+ val = hp100_inw( IRQ_STATUS );
+ hp100_outw( val & HP100_TX_COMPLETE, IRQ_STATUS );
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_start_xmit: irq_status = 0x%x, len = %d\n", val, (int)skb -> len );
+#endif
+ ok_flag = skb -> len >= HP100_MIN_PACKET_SIZE;
+ i = ok_flag ? skb -> len : HP100_MIN_PACKET_SIZE;
+ hp100_outw( i, DATA32 ); /* length to memory manager */
+ hp100_outw( i, FRAGMENT_LEN );
+ if ( lp -> mem_mapped )
+ {
+ if ( lp -> mem_ptr_virt )
+ {
+ memcpy( lp -> mem_ptr_virt, skb -> data, skb -> len );
+ if ( !ok_flag )
+ memset( lp -> mem_ptr_virt, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+ }
+ else
+ {
+ memcpy_toio( lp -> mem_ptr_phys, skb -> data, skb -> len );
+ if ( !ok_flag )
+ memset_io( lp -> mem_ptr_phys, 0, HP100_MIN_PACKET_SIZE - skb -> len );
+ }
+ }
+ else
+ {
+ outsl( ioaddr + HP100_REG_DATA32, skb -> data, ( skb -> len + 3 ) >> 2 );
+ if ( !ok_flag )
+ for ( i = ( skb -> len + 3 ) & ~3; i < HP100_MIN_PACKET_SIZE; i += 4 )
+ hp100_outl( 0, DATA32 );
+ }
+ hp100_outw( HP100_TX_CMD | HP100_SET_LB, OPTION_MSW ); /* send packet */
+ lp -> stats.tx_packets++;
+ dev -> trans_start = jiffies;
+ hp100_ints_on();
+
+ dev_kfree_skb( skb, FREE_WRITE );
+
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_start_xmit: end\n" );
+#endif
+
+ return 0;
+}
+
+/*
+ * receive - called from interrupt handler
+ */
+
+static void hp100_rx( struct device *dev )
+{
+ int packets, pkt_len;
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+ u_int header;
+ struct sk_buff *skb;
+
+#if 0
+ if ( lp -> lan_type < 0 )
+ {
+ if ( ( lp -> lan_type = hp100_sense_lan( dev ) ) == HP100_LAN_100 )
+ lp -> hub_status = hp100_login_to_vg_hub( dev );
+ hp100_page( PERFORMANCE );
+ }
+#endif
+
+ packets = hp100_inb( RX_PKT_CNT );
+#ifdef HP100_DEBUG
+ if ( packets > 1 )
+ printk( "hp100_rx: waiting packets = %d\n", packets );
+#endif
+ while ( packets-- > 0 )
+ {
+ for ( pkt_len = 0; pkt_len < 6000 && ( hp100_inw( OPTION_MSW ) & HP100_ADV_NXT_PKT ); pkt_len++ )
+ {
+#ifdef HP100_DEBUG_TX
+ printk( "hp100_rx: busy, remaining packets = %d\n", packets );
+#endif
+ }
+ if ( lp -> mem_mapped )
+ {
+ if ( lp -> mem_ptr_virt )
+ header = *(__u32 *)lp -> mem_ptr_virt;
+ else
+ header = readl( lp -> mem_ptr_phys );
+ }
+ else
+ header = hp100_inl( DATA32 );
+ pkt_len = header & HP100_PKT_LEN_MASK;
+#ifdef HP100_DEBUG_RX
+ printk( "hp100_rx: new packet - length = %d, errors = 0x%x, dest = 0x%x\n",
+ header & HP100_PKT_LEN_MASK, ( header >> 16 ) & 0xfff8, ( header >> 16 ) & 7 );
+#endif
+ /*
+ * NOTE! This (and the skb_put() below) depends on the skb-functions
+ * allocating more than asked (notably, aligning the request up to
+ * the next 16-byte length).
+ */
+ skb = dev_alloc_skb( pkt_len );
+ if ( skb == NULL )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_rx: couldn't allocate a sk_buff of size %d\n", pkt_len );
+#endif
+ lp -> stats.rx_dropped++;
+ }
+ else
+ {
+ u_char *ptr;
+
+ skb -> dev = dev;
+ ptr = (u_char *)skb_put( skb, pkt_len );
+ if ( lp -> mem_mapped )
+ {
+ if ( lp -> mem_ptr_virt )
+ memcpy( ptr, lp -> mem_ptr_virt, ( pkt_len + 3 ) & ~3 );
+ else
+ memcpy_fromio( ptr, lp -> mem_ptr_phys, ( pkt_len + 3 ) & ~3 );
+ }
+ else
+ insl( ioaddr + HP100_REG_DATA32, ptr, ( pkt_len + 3 ) >> 2 );
+ skb -> protocol = eth_type_trans( skb, dev );
+ netif_rx( skb );
+ lp -> stats.rx_packets++;
+#ifdef HP100_DEBUG_RX
+ printk( "rx: %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x\n",
+ ptr[ 0 ], ptr[ 1 ], ptr[ 2 ], ptr[ 3 ], ptr[ 4 ], ptr[ 5 ],
+ ptr[ 6 ], ptr[ 7 ], ptr[ 8 ], ptr[ 9 ], ptr[ 10 ], ptr[ 11 ] );
+#endif
+ }
+ hp100_outw( HP100_ADV_NXT_PKT | HP100_SET_LB, OPTION_MSW );
+ switch ( header & 0x00070000 ) {
+ case (HP100_MULTI_ADDR_HASH<<16):
+ case (HP100_MULTI_ADDR_NO_HASH<<16):
+ lp -> stats.multicast++; break;
+ }
+ }
+#ifdef HP100_DEBUG_RX
+ printk( "hp100_rx: end\n" );
+#endif
+}
+
+/*
+ * statistics
+ */
+
+static struct enet_statistics *hp100_get_stats( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+
+ hp100_ints_off();
+ hp100_update_stats( dev );
+ hp100_ints_on();
+ return &((struct hp100_private *)dev -> priv) -> stats;
+}
+
+static void hp100_update_stats( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ u_short val;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ hp100_page( MAC_CTRL ); /* get all statistics bytes */
+ val = hp100_inw( DROPPED ) & 0x0fff;
+ lp -> stats.rx_errors += val;
+ lp -> stats.rx_over_errors += val;
+ val = hp100_inb( CRC );
+ lp -> stats.rx_errors += val;
+ lp -> stats.rx_crc_errors += val;
+ val = hp100_inb( ABORT );
+ lp -> stats.tx_errors += val;
+ lp -> stats.tx_aborted_errors += val;
+ hp100_page( PERFORMANCE );
+}
+
+static void hp100_clear_stats( int ioaddr )
+{
+ cli();
+ hp100_page( MAC_CTRL ); /* get all statistics bytes */
+ hp100_inw( DROPPED );
+ hp100_inb( CRC );
+ hp100_inb( ABORT );
+ hp100_page( PERFORMANCE );
+ sti();
+}
+
+/*
+ * multicast setup
+ */
+
+/*
+ * Set or clear the multicast filter for this adapter.
+ */
+
+static void hp100_set_multicast_list( struct device *dev)
+{
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+#ifdef HP100_DEBUG_MULTI
+ printk( "hp100_set_multicast_list: num_addrs = %d\n", dev->mc_count);
+#endif
+ cli();
+ hp100_ints_off();
+ hp100_page( MAC_CTRL );
+ hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 ); /* stop rx/tx */
+
+ if ( dev->flags&IFF_PROMISC)
+ {
+ lp -> mac2_mode = HP100_MAC2MODE6; /* promiscuous mode, all good */
+ lp -> mac1_mode = HP100_MAC1MODE6; /* packets on the net */
+ }
+ else
+ if ( dev->mc_count || dev->flags&IFF_ALLMULTI )
+ {
+ lp -> mac2_mode = HP100_MAC2MODE5; /* multicast mode, packets for me */
+ lp -> mac1_mode = HP100_MAC1MODE5; /* broadcasts and all multicasts */
+ }
+ else
+ {
+ lp -> mac2_mode = HP100_MAC2MODE3; /* normal mode, packets for me */
+ lp -> mac1_mode = HP100_MAC1MODE3; /* and broadcasts */
+ }
+
+ hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
+ hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
+ hp100_orb( lp -> mac1_mode |
+ HP100_RX_EN | HP100_RX_IDLE | /* enable rx */
+ HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 ); /* enable tx */
+ hp100_page( PERFORMANCE );
+ hp100_ints_on();
+ sti();
+}
+
+/*
+ * hardware interrupt handling
+ */
+
+static void hp100_interrupt( int irq, struct pt_regs *regs )
+{
+ struct device *dev = (struct device *)irq2dev_map[ irq ];
+ struct hp100_private *lp;
+ int ioaddr;
+ u_short val;
+
+ if ( dev == NULL ) return;
+ ioaddr = dev -> base_addr;
+ if ( dev -> interrupt )
+ printk( "%s: re-entering the interrupt handler\n", dev -> name );
+ hp100_ints_off();
+ dev -> interrupt = 1;
+ hp100_page( PERFORMANCE );
+ val = hp100_inw( IRQ_STATUS );
+#ifdef HP100_DEBUG_IRQ
+ printk( "hp100_interrupt: irq_status = 0x%x\n", val );
+#endif
+ if ( val & HP100_RX_PACKET )
+ {
+ hp100_rx( dev );
+ hp100_outw( HP100_RX_PACKET, IRQ_STATUS );
+ }
+ if ( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE) )
+ {
+ hp100_outw( val & (HP100_TX_SPACE_AVAIL | HP100_TX_COMPLETE), IRQ_STATUS );
+ }
+ if ( val & ( HP100_TX_ERROR | HP100_RX_ERROR ) )
+ {
+ lp = (struct hp100_private *)dev -> priv;
+ hp100_update_stats( dev );
+ hp100_outw( val & (HP100_TX_ERROR | HP100_RX_ERROR), IRQ_STATUS );
+ }
+#ifdef HP100_DEBUG_IRQ
+ printk( "hp100_interrupt: end\n" );
+#endif
+ dev -> interrupt = 0;
+ hp100_ints_on();
+}
+
+/*
+ * some misc functions
+ */
+
+static void hp100_start_interface( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ cli();
+ hp100_unreset_card();
+ hp100_page( MAC_CTRL );
+ hp100_outb( lp -> mac2_mode, MAC_CFG_2 );
+ hp100_andb( HP100_MAC1MODEMASK, MAC_CFG_1 );
+ hp100_orb( lp -> mac1_mode |
+ HP100_RX_EN | HP100_RX_IDLE |
+ HP100_TX_EN | HP100_TX_IDLE, MAC_CFG_1 );
+ hp100_page( PERFORMANCE );
+ hp100_outw( HP100_INT_EN | HP100_SET_LB, OPTION_LSW );
+ hp100_outw( HP100_TRI_INT | HP100_RESET_HB, OPTION_LSW );
+ if ( lp -> mem_mapped )
+ {
+ /* enable memory mapping */
+ hp100_outw( HP100_MMAP_DIS | HP100_RESET_HB, OPTION_LSW );
+ }
+ sti();
+}
+
+static void hp100_stop_interface( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ u_short val;
+
+ hp100_outw( HP100_INT_EN | HP100_RESET_LB |
+ HP100_TRI_INT | HP100_MMAP_DIS | HP100_SET_HB, OPTION_LSW );
+ val = hp100_inw( OPTION_LSW );
+ hp100_page( HW_MAP );
+ hp100_andb( HP100_BM_SLAVE, BM );
+ hp100_page( MAC_CTRL );
+ hp100_andb( ~(HP100_RX_EN | HP100_TX_EN), MAC_CFG_1 );
+ if ( !(val & HP100_HW_RST) ) return;
+ for ( val = 0; val < 6000; val++ )
+ if ( ( hp100_inb( MAC_CFG_1 ) & (HP100_TX_IDLE | HP100_RX_IDLE) ) ==
+ (HP100_TX_IDLE | HP100_RX_IDLE) )
+ return;
+ printk( "%s: hp100_stop_interface - timeout\n", dev -> name );
+}
+
+static void hp100_load_eeprom( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+
+ hp100_page( EEPROM_CTRL );
+ hp100_andw( ~HP100_EEPROM_LOAD, EEPROM_CTRL );
+ hp100_orw( HP100_EEPROM_LOAD, EEPROM_CTRL );
+ for ( i = 0; i < 6000; i++ )
+ if ( !( hp100_inw( OPTION_MSW ) & HP100_EE_LOAD ) ) return;
+ printk( "%s: hp100_load_eeprom - timeout\n", dev -> name );
+}
+
+/* return values: LAN_10, LAN_100 or LAN_ERR (not connected or hub is down)... */
+
+static int hp100_sense_lan( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+ u_short val_VG, val_10;
+ struct hp100_private *lp = (struct hp100_private *)dev -> priv;
+
+ hp100_page( MAC_CTRL );
+ hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
+ val_10 = hp100_inw( LAN_CFG_10 );
+ val_VG = hp100_inw( LAN_CFG_VG );
+#ifdef HP100_DEBUG_SENSE
+ printk( "hp100_sense_lan: val_VG = 0x%04x, val_10 = 0x%04x\n", val_VG, val_10 );
+#endif
+ if ( val_10 & HP100_LINK_BEAT_ST ) return HP100_LAN_10;
+ if ( lp -> id -> id == 0x02019F022 ) /* HP J27248B doesn't have 100Mb/s interface */
+ return HP100_LAN_ERR;
+ for ( i = 0; i < 2500; i++ )
+ {
+ val_VG = hp100_inw( LAN_CFG_VG );
+ if ( val_VG & HP100_LINK_CABLE_ST ) return HP100_LAN_100;
+ }
+ return HP100_LAN_ERR;
+}
+
+static int hp100_down_vg_link( struct device *dev )
+{
+ int ioaddr = dev -> base_addr;
+ unsigned long time;
+ int i;
+
+ hp100_page( MAC_CTRL );
+ for ( i = 2500; i > 0; i-- )
+ if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
+ if ( i <= 0 ) /* not signal - not logout */
+ return 0;
+ hp100_andw( ~HP100_LINK_CMD, LAN_CFG_VG );
+ time = jiffies + 10;
+ while ( time > jiffies )
+ if ( !( hp100_inw( LAN_CFG_VG ) & ( HP100_LINK_UP_ST |
+ HP100_LINK_CABLE_ST |
+ HP100_LINK_GOOD_ST ) ) )
+ return 0;
+#ifdef HP100_DEBUG
+ printk( "hp100_down_vg_link: timeout\n" );
+#endif
+ return -EIO;
+}
+
+static int hp100_login_to_vg_hub( struct device *dev )
+{
+ int i;
+ int ioaddr = dev -> base_addr;
+ u_short val;
+ unsigned long time;
+
+ hp100_page( MAC_CTRL );
+ hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
+ time = jiffies + ( HZ / 2 );
+ do {
+ if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
+ } while ( time > jiffies );
+ if ( time <= jiffies )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_login_to_vg_hub: timeout for link\n" );
+#endif
+ return -EIO;
+ }
+
+ if ( hp100_down_vg_link( dev ) < 0 ) /* if fail, try reset VG link */
+ {
+ hp100_andw( ~HP100_VG_RESET, LAN_CFG_VG );
+ hp100_orw( HP100_VG_RESET, LAN_CFG_VG );
+ }
+ /* bring up link */
+ hp100_orw( HP100_LOAD_ADDR | HP100_LINK_CMD, LAN_CFG_VG );
+ for ( i = 2500; i > 0; i-- )
+ if ( hp100_inw( LAN_CFG_VG ) & HP100_LINK_CABLE_ST ) break;
+ if ( i <= 0 )
+ {
+#ifdef HP100_DEBUG
+ printk( "hp100_login_to_vg_hub: timeout for link (bring up)\n" );
+#endif
+ goto down_link;
+ }
+
+ time = jiffies + ( HZ / 2 );
+ do {
+ val = hp100_inw( LAN_CFG_VG );
+ if ( ( val & ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) ) ==
+ ( HP100_LINK_UP_ST | HP100_LINK_GOOD_ST ) )
+ return 0; /* success */
+ } while ( time > jiffies );
+ if ( val & HP100_LINK_GOOD_ST )
+ printk( "%s: 100Mb cable training failed, check cable.\n", dev -> name );
+ else
+ printk( "%s: 100Mb node not accepted by hub, check frame type or security.\n", dev -> name );
+
+down_link:
+ hp100_down_vg_link( dev );
+ hp100_page( MAC_CTRL );
+ hp100_andw( ~( HP100_LOAD_ADDR | HP100_PROM_MODE ), LAN_CFG_VG );
+ hp100_orw( HP100_LINK_CMD, LAN_CFG_VG );
+ return -EIO;
+}
+
+/*
+ * module section
+ */
+
+#ifdef MODULE
+
+static int hp100_port = -1;
+
+static char devicename[9] = { 0, };
+static struct device dev_hp100 = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, hp100_probe
+};
+
+int init_module( void )
+{
+ if (hp100_port == 0 && !EISA_bus)
+ printk("HP100: You should not use auto-probing with insmod!\n");
+ if ( hp100_port > 0 )
+ dev_hp100.base_addr = hp100_port;
+ if ( register_netdev( &dev_hp100 ) != 0 )
+ return -EIO;
+ return 0;
+}
+
+void cleanup_module( void )
+{
+ unregister_netdev( &dev_hp100 );
+ release_region( dev_hp100.base_addr, HP100_REGION_SIZE );
+ if ( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt )
+ vfree( ((struct hp100_private *)dev_hp100.priv) -> mem_ptr_virt );
+ kfree_s( dev_hp100.priv, sizeof( struct hp100_private ) );
+ dev_hp100.priv = NULL;
+}
+
+#endif
diff --git a/i386/i386at/gpl/linux/net/hp100.h b/i386/i386at/gpl/linux/net/hp100.h
new file mode 100644
index 00000000..9f14f95a
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/hp100.h
@@ -0,0 +1,374 @@
+/*
+ * hp100.h: Hewlett Packard HP10/100VG ANY LAN ethernet driver for Linux.
+ *
+ * Author: Jaroslav Kysela, <perex@pf.jcu.cz>
+ *
+ * Header file...
+ *
+ * This driver is based on the 'hpfepkt' crynwr packet driver.
+ *
+ * This source/code is public free; you can distribute it and/or modify
+ * it under terms of the GNU General Public License (published by the
+ * Free Software Foundation) either version two of this License, or any
+ * later version.
+ */
+
+/****************************************************************************
+ * Hardware Constants
+ ****************************************************************************/
+
+/*
+ * ATT2MD01 Register Page Constants
+ */
+
+#define HP100_PAGE_PERFORMANCE 0x0 /* Page 0 */
+#define HP100_PAGE_MAC_ADDRESS 0x1 /* Page 1 */
+#define HP100_PAGE_HW_MAP 0x2 /* Page 2 */
+#define HP100_PAGE_EEPROM_CTRL 0x3 /* Page 3 */
+#define HP100_PAGE_MAC_CTRL 0x4 /* Page 4 */
+#define HP100_PAGE_MMU_CFG 0x5 /* Page 5 */
+#define HP100_PAGE_ID_MAC_ADDR 0x6 /* Page 6 */
+#define HP100_PAGE_MMU_POINTER 0x7 /* Page 7 */
+
+/*
+ * ATT2MD01 Register Addresses
+ */
+
+/* Present on all pages */
+
+#define HP100_REG_HW_ID 0x00 /* R: (16) Unique card ID */
+#define HP100_REG_TRACE 0x00 /* W: (16) Used for debug output */
+#define HP100_REG_PAGING 0x02 /* R: (16),15:4 Card ID */
+ /* W: (16),3:0 Switch pages */
+#define HP100_REG_OPTION_LSW 0x04 /* RW: (16) Select card functions */
+#define HP100_REG_OPTION_MSW 0x06 /* RW: (16) Select card functions */
+
+/* Page 0 - Performance */
+
+#define HP100_REG_IRQ_STATUS 0x08 /* RW: (16) Which ints are pending */
+#define HP100_REG_IRQ_MASK 0x0a /* RW: (16) Select ints to allow */
+#define HP100_REG_FRAGMENT_LEN 0x0c /* RW: (16)12:0 Current fragment len */
+#define HP100_REG_OFFSET 0x0e /* RW: (16)12:0 Offset to start read */
+#define HP100_REG_DATA32 0x10 /* RW: (32) I/O mode data port */
+#define HP100_REG_DATA16 0x12 /* RW: WORDs must be read from here */
+#define HP100_REG_TX_MEM_FREE 0x14 /* RD: (32) Amount of free Tx mem */
+#define HP100_REG_RX_PKT_CNT 0x18 /* RD: (8) Rx count of pkts on card */
+#define HP100_REG_TX_PKT_CNT 0x19 /* RD: (8) Tx count of pkts on card */
+
+/* Page 1 - MAC Address/Hash Table */
+
+#define HP100_REG_MAC_ADDR 0x08 /* RW: (8) Cards MAC address */
+#define HP100_REG_HASH_BYTE0 0x10 /* RW: (8) Cards multicast filter */
+
+/* Page 2 - Hardware Mapping */
+
+#define HP100_REG_MEM_MAP_LSW 0x08 /* RW: (16) LSW of cards mem addr */
+#define HP100_REG_MEM_MAP_MSW 0x0a /* RW: (16) MSW of cards mem addr */
+#define HP100_REG_IO_MAP 0x0c /* RW: (8) Cards I/O address */
+#define HP100_REG_IRQ_CHANNEL 0x0d /* RW: (8) IRQ and edge/level int */
+#define HP100_REG_SRAM 0x0e /* RW: (8) How much RAM on card */
+#define HP100_REG_BM 0x0f /* RW: (8) Controls BM functions */
+
+/* Page 3 - EEPROM/Boot ROM */
+
+#define HP100_REG_EEPROM_CTRL 0x08 /* RW: (16) Used to load EEPROM */
+
+/* Page 4 - LAN Configuration */
+
+#define HP100_REG_LAN_CFG_10 0x08 /* RW: (16) Set 10M XCVR functions */
+#define HP100_REG_LAN_CFG_VG 0x0a /* RW: (16) Set 100M XCVR functions */
+#define HP100_REG_MAC_CFG_1 0x0c /* RW: (8) Types of pkts to accept */
+#define HP100_REG_MAC_CFG_2 0x0d /* RW: (8) Misc MAC functions */
+/* The follow clear when read: */
+#define HP100_REG_DROPPED 0x10 /* R: (16),11:0 Pkts cant fit in mem*/
+#define HP100_REG_CRC 0x12 /* R: (8) Pkts with CRC */
+#define HP100_REG_ABORT 0x13 /* R: (8) Aborted Tx pkts */
+
+/* Page 5 - MMU */
+
+#define HP100_REG_RX_MEM_STOP 0x0c /* RW: (16) End of Rx ring addr */
+#define HP100_REG_TX_MEM_STOP 0x0e /* RW: (16) End of Tx ring addr */
+
+/* Page 6 - Card ID/Physical LAN Address */
+
+#define HP100_REG_BOARD_ID 0x08 /* R: (8) EISA/ISA card ID */
+#define HP100_REG_BOARD_IO_CHCK 0x0c /* R: (8) Added to ID to get FFh */
+#define HP100_REG_SOFT_MODEL 0x0d /* R: (8) Config program defined */
+#define HP100_REG_LAN_ADDR 0x10 /* R: (8) MAC addr of card */
+#define HP100_REG_LAN_ADDR_CHCK 0x16 /* R: (8) Added to addr to get FFh */
+
+/* Page 7 - MMU Current Pointers */
+
+#define HP100_REG_RX_MEM_BR 0x08 /* R: (16) Current begin of Rx ring */
+#define HP100_REG_RX_MEM_ER 0x0a /* R: (16) Current end of Rx ring */
+#define HP100_REG_TX_MEM_BR 0x0c /* R: (16) Current begin of Tx ring */
+#define HP100_REG_TX_MEM_ER 0x0e /* R: (16) Current end of Rx ring */
+#define HP100_REG_MEM_DEBUG 0x1a /* RW: (16) Used for memory tests */
+
+/*
+ * HardwareIDReg bits/masks
+ */
+
+#define HP100_HW_ID_0 0x50 /* Hardware ID bytes. */
+#define HP100_HW_ID_1 0x48
+#define HP100_HW_ID_2_REVA 0x50 /* Rev. A ID. NOTE: lower nibble not used */
+#define HP100_HW_ID_3 0x53
+
+/*
+ * OptionLSWReg bits/masks
+ */
+
+#define HP100_DEBUG_EN 0x8000 /* 0:Disable, 1:Enable Debug Dump Pointer */
+#define HP100_RX_HDR 0x4000 /* 0:Disable, 1:Enable putting pkt into */
+ /* system memory before Rx interrupt */
+#define HP100_MMAP_DIS 0x2000 /* 0:Enable, 1:Disable memory mapping. */
+ /* MMAP_DIS must be 0 and MEM_EN must */
+ /* be 1 for memory-mapped mode to be */
+ /* enabled */
+#define HP100_EE_EN 0x1000 /* 0:Disable,1:Enable EEPROM writing */
+#define HP100_BM_WRITE 0x0800 /* 0:Slave, 1:Bus Master for Tx data */
+#define HP100_BM_READ 0x0400 /* 0:Slave, 1:Bus Master for Rx data */
+#define HP100_TRI_INT 0x0200 /* 0:Dont, 1:Do tri-state the int */
+#define HP100_MEM_EN 0x0040 /* Config program set this to */
+ /* 0:Disable, 1:Enable mem map. */
+ /* See MMAP_DIS. */
+#define HP100_IO_EN 0x0020 /* 0:Disable, 1:Enable I/O transfers */
+#define HP100_BOOT_EN 0x0010 /* 0:Disable, 1:Enable boot ROM access */
+#define HP100_FAKE_INT 0x0008 /* 0:No int, 1:int */
+#define HP100_INT_EN 0x0004 /* 0:Disable, 1:Enable ints from card */
+#define HP100_HW_RST 0x0002 /* 0:Reset, 1:Out of reset */
+
+/*
+ * OptionMSWReg bits/masks
+ */
+#define HP100_PRIORITY_TX 0x0080 /* 0:Don't, 1:Do all Tx pkts as priority */
+#define HP100_EE_LOAD 0x0040 /* 1:EEPROM loading, 0 when done */
+#define HP100_ADV_NXT_PKT 0x0004 /* 1:Advance to next pkt in Rx queue, */
+ /* h/w will set to 0 when done */
+#define HP100_TX_CMD 0x0002 /* 1:Tell h/w download done, h/w will set */
+ /* to 0 when done */
+
+/*
+ * InterruptStatusReg/InterruptMaskReg bits/masks. These bits will 0 when a 1
+ * is written to them.
+ */
+#define HP100_RX_PACKET 0x0400 /* 0:No, 1:Yes pkt has been Rx */
+#define HP100_RX_ERROR 0x0200 /* 0:No, 1:Yes Rx pkt had error */
+#define HP100_TX_SPACE_AVAIL 0x0010 /* 0:<8192, 1:>=8192 Tx free bytes */
+#define HP100_TX_COMPLETE 0x0008 /* 0:No, 1:Yes a Tx has completed */
+#define HP100_TX_ERROR 0x0002 /* 0:No, 1:Yes Tx pkt had error */
+
+/*
+ * TxMemoryFreeCountReg bits/masks.
+ */
+#define HP100_AUTO_COMPARE 0x8000 /* Says at least 8k is available for Tx. */
+ /* NOTE: This mask is for the upper */
+ /* word of the register. */
+
+/*
+ * IRQChannelReg bits/masks.
+ */
+#define HP100_ZERO_WAIT_EN 0x80 /* 0:No, 1:Yes assers NOWS signal */
+#define HP100_LEVEL_IRQ 0x10 /* 0:Edge, 1:Level type interrupts. */
+ /* Only valid on EISA cards. */
+#define HP100_IRQ_MASK 0x0F /* Isolate the IRQ bits */
+
+/*
+ * SRAMReg bits/masks.
+ */
+#define HP100_RAM_SIZE_MASK 0xe0 /* AND to get SRAM size index */
+#define HP100_RAM_SIZE_SHIFT 0x05 /* Shift count to put index in lower bits */
+
+/*
+ * BMReg bits/masks.
+ */
+#define HP100_BM_SLAVE 0x04 /* 0:Slave, 1:BM mode */
+
+/*
+ * EEPROMControlReg bits/masks.
+ */
+#define HP100_EEPROM_LOAD 0x0001 /* 0->1 loads the EEPROM into registers. */
+ /* When it goes back to 0, load is */
+ /* complete. This should take ~600us. */
+
+/*
+ * LANCntrCfg10Reg bits/masks.
+ */
+#define HP100_SQU_ST 0x0100 /* 0:No, 1:Yes collision signal sent */
+ /* after Tx. Only used for AUI. */
+#define HP100_MAC10_SEL 0x00c0 /* Get bits to indicate MAC */
+#define HP100_AUI_SEL 0x0020 /* Status of AUI selection */
+#define HP100_LOW_TH 0x0010 /* 0:No, 1:Yes allow better cabling */
+#define HP100_LINK_BEAT_DIS 0x0008 /* 0:Enable, 1:Disable link beat */
+#define HP100_LINK_BEAT_ST 0x0004 /* 0:No, 1:Yes link beat being Rx */
+#define HP100_R_ROL_ST 0x0002 /* 0:No, 1:Yes Rx twisted pair has been */
+ /* reversed */
+#define HP100_AUI_ST 0x0001 /* 0:No, 1:Yes use AUI on TP card */
+
+/* MAC Selection, use with MAC10_SEL bits */
+#define HP100_AUTO_SEL_10 0x0 /* Auto select */
+#define HP100_XCVR_LXT901_10 0x1 /* LXT901 10BaseT transceiver */
+#define HP100_XCVR_7213 0x2 /* 7213 transceiver */
+#define HP100_XCVR_82503 0x3 /* 82503 transceiver */
+
+
+/*
+ * LANCntrCfgVGReg bits/masks.
+ */
+#define HP100_FRAME_FORMAT 0x0800 /* 0:802.3, 1:802.5 frames */
+#define HP100_BRIDGE 0x0400 /* 0:No, 1:Yes tell hub it's a bridge */
+#define HP100_PROM_MODE 0x0200 /* 0:No, 1:Yes tell hub card is */
+ /* promiscuous */
+#define HP100_REPEATER 0x0100 /* 0:No, 1:Yes tell hub MAC wants to be */
+ /* a cascaded repeater */
+#define HP100_MAC100_SEL 0x0080 /* 0:No, 1:Yes use 100 Mbit MAC */
+#define HP100_LINK_UP_ST 0x0040 /* 0:No, 1:Yes endnode logged in */
+#define HP100_LINK_CABLE_ST 0x0020 /* 0:No, 1:Yes cable can hear tones from */
+ /* hub */
+#define HP100_LOAD_ADDR 0x0010 /* 0->1 card addr will be sent to hub. */
+ /* 100ms later the link status bits are */
+ /* valid */
+#define HP100_LINK_CMD 0x0008 /* 0->1 link will attempt to log in. */
+ /* 100ms later the link status bits are */
+ /* valid */
+#define HP100_LINK_GOOD_ST 0x0002 /* 0:No, 1:Yes cable passed training */
+#define HP100_VG_RESET 0x0001 /* 0:Yes, 1:No reset the 100VG MAC */
+
+
+/*
+ * MACConfiguration1Reg bits/masks.
+ */
+#define HP100_RX_IDLE 0x80 /* 0:Yes, 1:No currently receiving pkts */
+#define HP100_TX_IDLE 0x40 /* 0:Yes, 1:No currently Txing pkts */
+#define HP100_RX_EN 0x20 /* 0:No, 1:Yes allow receiving of pkts */
+#define HP100_TX_EN 0x10 /* 0:No, 1:Yes allow transmiting of pkts */
+#define HP100_ACC_ERRORED 0x08 /* 0:No, 1:Yes allow Rx of errored pkts */
+#define HP100_ACC_MC 0x04 /* 0:No, 1:Yes allow Rx of multicast pkts */
+#define HP100_ACC_BC 0x02 /* 0:No, 1:Yes allow Rx of broadcast pkts */
+#define HP100_ACC_PHY 0x01 /* 0:No, 1:Yes allow Rx of ALL physical pkts */
+
+#define HP100_MAC1MODEMASK 0xf0 /* Hide ACC bits */
+#define HP100_MAC1MODE1 0x00 /* Receive nothing, must also disable RX */
+#define HP100_MAC1MODE2 0x00
+#define HP100_MAC1MODE3 HP100_MAC1MODE2 | HP100_ACC_BC
+#define HP100_MAC1MODE4 HP100_MAC1MODE3 | HP100_ACC_MC
+#define HP100_MAC1MODE5 HP100_MAC1MODE4 /* set mc hash to all ones also */
+#define HP100_MAC1MODE6 HP100_MAC1MODE5 | HP100_ACC_PHY /* Promiscuous */
+
+/* Note MODE6 will receive all GOOD packets on the LAN. This really needs
+ a mode 7 defined to be LAN Analyzer mode, which will receive errored and
+ runt packets, and keep the CRC bytes. */
+
+#define HP100_MAC1MODE7 MAC1MODE6 OR ACC_ERRORED
+
+/*
+ * MACConfiguration2Reg bits/masks.
+ */
+#define HP100_TR_MODE 0x80 /* 0:No, 1:Yes support Token Ring formats */
+#define HP100_TX_SAME 0x40 /* 0:No, 1:Yes Tx same packet continuous */
+#define HP100_LBK_XCVR 0x20 /* 0:No, 1:Yes loopback through MAC & */
+ /* transceiver */
+#define HP100_LBK_MAC 0x10 /* 0:No, 1:Yes loopback through MAC */
+#define HP100_CRC_I 0x08 /* 0:No, 1:Yes inhibit CRC on Tx packets */
+#define HP100_KEEP_CRC 0x02 /* 0:No, 1:Yes keep CRC on Rx packets. */
+ /* The length will reflect this. */
+
+#define HP100_MAC2MODEMASK 0x02
+#define HP100_MAC2MODE1 0x00
+#define HP100_MAC2MODE2 0x00
+#define HP100_MAC2MODE3 0x00
+#define HP100_MAC2MODE4 0x00
+#define HP100_MAC2MODE5 0x00
+#define HP100_MAC2MODE6 0x00
+#define HP100_MAC2MODE7 KEEP_CRC
+
+/*
+ * Set/Reset bits
+ */
+#define HP100_SET_HB 0x0100 /* 0:Set fields to 0 whose mask is 1 */
+#define HP100_SET_LB 0x0001 /* HB sets upper byte, LB sets lower byte */
+#define HP100_RESET_HB 0x0000 /* For readability when resetting bits */
+#define HP100_RESET_LB 0x0000 /* For readability when resetting bits */
+
+/*
+ * Misc. Constants
+ */
+#define HP100_LAN_100 100 /* lan_type value for VG */
+#define HP100_LAN_10 10 /* lan_type value for 10BaseT */
+#define HP100_LAN_ERR (-1) /* lan_type value for link down */
+
+/*
+ * Receive Header Definition.
+ */
+
+struct hp100_rx_header {
+ u_short rx_length; /* Pkt length is bits 12:0 */
+ u_short rx_status; /* status of the packet */
+};
+
+#define HP100_PKT_LEN_MASK 0x1FFF /* AND with RxLength to get length bits */
+
+/* Receive Packet Status. Note, the error bits are only valid if ACC_ERRORED
+ bit in the MAC Configuration Register 1 is set. */
+
+#define HP100_RX_PRI 0x8000 /* 0:No, 1:Yes packet is priority */
+#define HP100_SDF_ERR 0x4000 /* 0:No, 1:Yes start of frame error */
+#define HP100_SKEW_ERR 0x2000 /* 0:No, 1:Yes skew out of range */
+#define HP100_BAD_SYMBOL_ERR 0x1000 /* 0:No, 1:Yes invalid symbol received */
+#define HP100_RCV_IPM_ERR 0x0800 /* 0:No, 1:Yes pkt had an invalid packet */
+ /* marker */
+#define HP100_SYMBOL_BAL_ERR 0x0400 /* 0:No, 1:Yes symbol balance error */
+#define HP100_VG_ALN_ERR 0x0200 /* 0:No, 1:Yes non-octet received */
+#define HP100_TRUNC_ERR 0x0100 /* 0:No, 1:Yes the packet was truncated */
+#define HP100_RUNT_ERR 0x0040 /* 0:No, 1:Yes pkt length < Min Pkt */
+ /* Length Reg. */
+#define HP100_ALN_ERR 0x0010 /* 0:No, 1:Yes align error. */
+#define HP100_CRC_ERR 0x0008 /* 0:No, 1:Yes CRC occurred. */
+
+/* The last three bits indicate the type of destination address */
+
+#define HP100_MULTI_ADDR_HASH 0x0006 /* 110: Addr multicast, matched hash */
+#define HP100_BROADCAST_ADDR 0x0003 /* x11: Addr broadcast */
+#define HP100_MULTI_ADDR_NO_HASH 0x0002 /* 010: Addr multicast, didn't match hash */
+#define HP100_PHYS_ADDR_MATCH 0x0001 /* x01: Addr was physical and mine */
+#define HP100_PHYS_ADDR_NO_MATCH 0x0000 /* x00: Addr was physical but not mine */
+
+/*
+ * macros
+ */
+
+#define hp100_inb( reg ) \
+ inb( ioaddr + HP100_REG_##reg )
+#define hp100_inw( reg ) \
+ inw( ioaddr + HP100_REG_##reg )
+#define hp100_inl( reg ) \
+ inl( ioaddr + HP100_REG_##reg )
+#define hp100_outb( data, reg ) \
+ outb( data, ioaddr + HP100_REG_##reg )
+#define hp100_outw( data, reg ) \
+ outw( data, ioaddr + HP100_REG_##reg )
+#define hp100_outl( data, reg ) \
+ outl( data, ioaddr + HP100_REG_##reg )
+#define hp100_orb( data, reg ) \
+ outb( inb( ioaddr + HP100_REG_##reg ) | (data), ioaddr + HP100_REG_##reg )
+#define hp100_orw( data, reg ) \
+ outw( inw( ioaddr + HP100_REG_##reg ) | (data), ioaddr + HP100_REG_##reg )
+#define hp100_andb( data, reg ) \
+ outb( inb( ioaddr + HP100_REG_##reg ) & (data), ioaddr + HP100_REG_##reg )
+#define hp100_andw( data, reg ) \
+ outw( inw( ioaddr + HP100_REG_##reg ) & (data), ioaddr + HP100_REG_##reg )
+
+#define hp100_page( page ) \
+ outw( HP100_PAGE_##page, ioaddr + HP100_REG_PAGING )
+#define hp100_ints_off() \
+ outw( HP100_INT_EN | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_ints_on() \
+ outw( HP100_INT_EN | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_mem_map_enable() \
+ outw( HP100_MMAP_DIS | HP100_RESET_HB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_mem_map_disable() \
+ outw( HP100_MMAP_DIS | HP100_SET_HB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_reset_card() \
+ outw( HP100_HW_RST | HP100_RESET_LB, ioaddr + HP100_REG_OPTION_LSW )
+#define hp100_unreset_card() \
+ outw( HP100_HW_RST | HP100_SET_LB, ioaddr + HP100_REG_OPTION_LSW )
diff --git a/i386/i386at/gpl/linux/net/i82586.h b/i386/i386at/gpl/linux/net/i82586.h
new file mode 100644
index 00000000..ff229e98
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/i82586.h
@@ -0,0 +1,408 @@
+/*
+ * Intel 82586 IEEE 802.3 Ethernet LAN Coprocessor.
+ *
+ * See:
+ * Intel Microcommunications 1991
+ * p1-1 to p1-37
+ * Intel order No. 231658
+ * ISBN 1-55512-119-5
+ *
+ * Unfortunately, the above chapter mentions neither
+ * the System Configuration Pointer (SCP) nor the
+ * Intermediate System Configuration Pointer (ISCP),
+ * so we probably need to look elsewhere for the
+ * whole story -- some recommend the "Intel LAN
+ * Components manual" but I have neither a copy
+ * nor a full reference. But "elsewhere" may be
+ * in the same publication...
+ * The description of a later device, the
+ * "82596CA High-Performance 32-Bit Local Area Network
+ * Coprocessor", (ibid. p1-38 to p1-109) does mention
+ * the SCP and ISCP and also has an i82586 compatibility
+ * mode. Even more useful is "AP-235 An 82586 Data Link
+ * Driver" (ibid. p1-337 to p1-417).
+ */
+
+#define I82586_MEMZ (64 * 1024)
+
+#define I82586_SCP_ADDR (I82586_MEMZ - sizeof(scp_t))
+
+#define ADDR_LEN 6
+#define I82586NULL 0xFFFF
+
+#define toff(t,p,f) (unsigned short)((void *)(&((t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * System Configuration Pointer (SCP).
+ */
+typedef struct scp_t scp_t;
+struct scp_t
+{
+ unsigned short scp_sysbus; /* 82586 bus width: */
+#define SCP_SY_16BBUS (0x0 << 0) /* 16 bits */
+#define SCP_SY_8BBUS (0x1 << 0) /* 8 bits. */
+ unsigned short scp_junk[2]; /* Unused */
+ unsigned short scp_iscpl; /* lower 16 bits of ISCP_ADDR */
+ unsigned short scp_iscph; /* upper 16 bits of ISCP_ADDR */
+};
+
+/*
+ * Intermediate System Configuration Pointer (ISCP).
+ */
+typedef struct iscp_t iscp_t;
+struct iscp_t
+{
+ unsigned short iscp_busy; /* set by CPU before first CA, */
+ /* cleared by 82586 after read. */
+ unsigned short iscp_offset; /* offset of SCB */
+ unsigned short iscp_basel; /* base of SCB */
+ unsigned short iscp_baseh; /* " */
+};
+
+/*
+ * System Control Block (SCB).
+ * The 82586 writes its status to scb_status and then
+ * raises an interrupt to alert the CPU.
+ * The CPU writes a command to scb_command and
+ * then issues a Channel Attention (CA) to alert the 82586.
+ */
+typedef struct scb_t scb_t;
+struct scb_t
+{
+ unsigned short scb_status; /* Status of 82586 */
+#define SCB_ST_INT (0xF << 12) /* Some of: */
+#define SCB_ST_CX (0x1 << 15) /* Cmd completed */
+#define SCB_ST_FR (0x1 << 14) /* Frame received */
+#define SCB_ST_CNA (0x1 << 13) /* Cmd unit not active */
+#define SCB_ST_RNR (0x1 << 12) /* Rcv unit not ready */
+#define SCB_ST_JUNK0 (0x1 << 11) /* 0 */
+#define SCB_ST_CUS (0x7 << 8) /* Cmd unit status */
+#define SCB_ST_CUS_IDLE (0 << 8) /* Idle */
+#define SCB_ST_CUS_SUSP (1 << 8) /* Suspended */
+#define SCB_ST_CUS_ACTV (2 << 8) /* Active */
+#define SCB_ST_JUNK1 (0x1 << 7) /* 0 */
+#define SCB_ST_RUS (0x7 << 4) /* Rcv unit status */
+#define SCB_ST_RUS_IDLE (0 << 4) /* Idle */
+#define SCB_ST_RUS_SUSP (1 << 4) /* Suspended */
+#define SCB_ST_RUS_NRES (2 << 4) /* No resources */
+#define SCB_ST_RUS_RDY (4 << 4) /* Ready */
+ unsigned short scb_command; /* Next command */
+#define SCB_CMD_ACK_CX (0x1 << 15) /* Ack cmd completion */
+#define SCB_CMD_ACK_FR (0x1 << 14) /* Ack frame received */
+#define SCB_CMD_ACK_CNA (0x1 << 13) /* Ack CU not active */
+#define SCB_CMD_ACK_RNR (0x1 << 12) /* Ack RU not ready */
+#define SCB_CMD_JUNKX (0x1 << 11) /* Unused */
+#define SCB_CMD_CUC (0x7 << 8) /* Command Unit command */
+#define SCB_CMD_CUC_NOP (0 << 8) /* Nop */
+#define SCB_CMD_CUC_GO (1 << 8) /* Start cbl_offset */
+#define SCB_CMD_CUC_RES (2 << 8) /* Resume execution */
+#define SCB_CMD_CUC_SUS (3 << 8) /* Suspend " */
+#define SCB_CMD_CUC_ABT (4 << 8) /* Abort " */
+#define SCB_CMD_RESET (0x1 << 7) /* Reset chip (hardware) */
+#define SCB_CMD_RUC (0x7 << 4) /* Receive Unit command */
+#define SCB_CMD_RUC_NOP (0 << 4) /* Nop */
+#define SCB_CMD_RUC_GO (1 << 4) /* Start rfa_offset */
+#define SCB_CMD_RUC_RES (2 << 4) /* Resume reception */
+#define SCB_CMD_RUC_SUS (3 << 4) /* Suspend " */
+#define SCB_CMD_RUC_ABT (4 << 4) /* Abort " */
+ unsigned short scb_cbl_offset; /* Offset of first command unit */
+ /* Action Command */
+ unsigned short scb_rfa_offset; /* Offset of first Receive */
+ /* Frame Descriptor in the */
+ /* Receive Frame Area */
+ unsigned short scb_crcerrs; /* Properly aligned frames */
+ /* received with a CRC error */
+ unsigned short scb_alnerrs; /* Misaligned frames received */
+ /* with a CRC error */
+ unsigned short scb_rscerrs; /* Frames lost due to no space */
+ unsigned short scb_ovrnerrs; /* Frames lost due to slow bus */
+};
+
+#define scboff(p,f) toff(scb_t, p, f)
+
+/*
+ * The eight Action Commands.
+ */
+typedef enum acmd_e acmd_e;
+enum acmd_e
+{
+ acmd_nop = 0, /* Do nothing */
+ acmd_ia_setup = 1, /* Load an (ethernet) address into the */
+ /* 82586 */
+ acmd_configure = 2, /* Update the 82586 operating parameters */
+ acmd_mc_setup = 3, /* Load a list of (ethernet) multicast */
+ /* addresses into the 82586 */
+ acmd_transmit = 4, /* Transmit a frame */
+ acmd_tdr = 5, /* Perform a Time Domain Reflectometer */
+ /* test on the serial link */
+ acmd_dump = 6, /* Copy 82586 registers to memory */
+ acmd_diagnose = 7, /* Run an internal self test */
+};
+
+/*
+ * Generic Action Command header.
+ */
+typedef struct ach_t ach_t;
+struct ach_t
+{
+ unsigned short ac_status; /* Command status: */
+#define AC_SFLD_C (0x1 << 15) /* Command completed */
+#define AC_SFLD_B (0x1 << 14) /* Busy executing */
+#define AC_SFLD_OK (0x1 << 13) /* Completed error free */
+#define AC_SFLD_A (0x1 << 12) /* Command aborted */
+#define AC_SFLD_FAIL (0x1 << 11) /* Selftest failed */
+#define AC_SFLD_S10 (0x1 << 10) /* No carrier sense */
+ /* during transmission */
+#define AC_SFLD_S9 (0x1 << 9) /* Tx unsuccessful: */
+ /* (stopped) lost CTS */
+#define AC_SFLD_S8 (0x1 << 8) /* Tx unsuccessful: */
+ /* (stopped) slow DMA */
+#define AC_SFLD_S7 (0x1 << 7) /* Tx deferred: */
+ /* other link traffic */
+#define AC_SFLD_S6 (0x1 << 6) /* Heart Beat: collision */
+ /* detect after last tx */
+#define AC_SFLD_S5 (0x1 << 5) /* Tx stopped: */
+ /* excessive collisions */
+#define AC_SFLD_MAXCOL (0xF << 0) /* Collision count */
+ unsigned short ac_command; /* Command specifier: */
+#define AC_CFLD_EL (0x1 << 15) /* End of command list */
+#define AC_CFLD_S (0x1 << 14) /* Suspend on completion */
+#define AC_CFLD_I (0x1 << 13) /* Interrupt on completion */
+#define AC_CFLD_CMD (0x7 << 0) /* acmd_e */
+ unsigned short ac_link; /* Next Action Command */
+};
+
+#define acoff(p,f) toff(ach_t, p, f)
+
+/*
+ * The Nop Action Command.
+ */
+typedef struct ac_nop_t ac_nop_t;
+struct ac_nop_t
+{
+ ach_t nop_h;
+};
+
+/*
+ * The IA-Setup Action Command.
+ */
+typedef struct ac_ias_t ac_ias_t;
+struct ac_ias_t
+{
+ ach_t ias_h;
+ unsigned char ias_addr[ADDR_LEN]; /* The (ethernet) address */
+};
+
+/*
+ * The Configure Action Command.
+ */
+typedef struct ac_cfg_t ac_cfg_t;
+struct ac_cfg_t
+{
+ ach_t cfg_h;
+ unsigned char cfg_byte_cnt; /* Size foll data: 4-12 */
+#define AC_CFG_BYTE_CNT(v) (((v) & 0xF) << 0)
+ unsigned char cfg_fifolim; /* FIFO threshold */
+#define AC_CFG_FIFOLIM(v) (((v) & 0xF) << 0)
+ unsigned char cfg_byte8;
+#define AC_CFG_SAV_BF(v) (((v) & 0x1) << 7) /* Save rxd bad frames */
+#define AC_CFG_SRDY(v) (((v) & 0x1) << 6) /* SRDY/ARDY pin means */
+ /* external sync. */
+ unsigned char cfg_byte9;
+#define AC_CFG_ELPBCK(v) (((v) & 0x1) << 7) /* External loopback */
+#define AC_CFG_ILPBCK(v) (((v) & 0x1) << 6) /* Internal loopback */
+#define AC_CFG_PRELEN(v) (((v) & 0x3) << 4) /* Preamble length */
+#define AC_CFG_PLEN_2 0 /* 2 bytes */
+#define AC_CFG_PLEN_4 1 /* 4 bytes */
+#define AC_CFG_PLEN_8 2 /* 8 bytes */
+#define AC_CFG_PLEN_16 3 /* 16 bytes */
+#define AC_CFG_ALOC(v) (((v) & 0x1) << 3) /* Addr/len data is */
+ /* explicit in buffers */
+#define AC_CFG_ADDRLEN(v) (((v) & 0x7) << 0) /* Bytes per address */
+ unsigned char cfg_byte10;
+#define AC_CFG_BOFMET(v) (((v) & 0x1) << 7) /* Use alternate expo. */
+ /* backoff method */
+#define AC_CFG_ACR(v) (((v) & 0x7) << 4) /* Accelerated cont. res. */
+#define AC_CFG_LINPRIO(v) (((v) & 0x7) << 0) /* Linear priority */
+ unsigned char cfg_ifs; /* Interframe spacing */
+ unsigned char cfg_slotl; /* Slot time (low byte) */
+ unsigned char cfg_byte13;
+#define AC_CFG_RETRYNUM(v) (((v) & 0xF) << 4) /* Max. collision retry */
+#define AC_CFG_SLTTMHI(v) (((v) & 0x7) << 0) /* Slot time (high bits) */
+ unsigned char cfg_byte14;
+#define AC_CFG_FLGPAD(v) (((v) & 0x1) << 7) /* Pad with HDLC flags */
+#define AC_CFG_BTSTF(v) (((v) & 0x1) << 6) /* Do HDLC bitstuffing */
+#define AC_CFG_CRC16(v) (((v) & 0x1) << 5) /* 16 bit CCITT CRC */
+#define AC_CFG_NCRC(v) (((v) & 0x1) << 4) /* Insert no CRC */
+#define AC_CFG_TNCRS(v) (((v) & 0x1) << 3) /* Tx even if no carrier */
+#define AC_CFG_MANCH(v) (((v) & 0x1) << 2) /* Manchester coding */
+#define AC_CFG_BCDIS(v) (((v) & 0x1) << 1) /* Disable broadcast */
+#define AC_CFG_PRM(v) (((v) & 0x1) << 0) /* Promiscuous mode */
+ unsigned char cfg_byte15;
+#define AC_CFG_ICDS(v) (((v) & 0x1) << 7) /* Internal collision */
+ /* detect source */
+#define AC_CFG_CDTF(v) (((v) & 0x7) << 4) /* Collision detect */
+ /* filter in bit times */
+#define AC_CFG_ICSS(v) (((v) & 0x1) << 3) /* Internal carrier */
+ /* sense source */
+#define AC_CFG_CSTF(v) (((v) & 0x7) << 0) /* Carrier sense */
+ /* filter in bit times */
+ unsigned short cfg_min_frm_len;
+#define AC_CFG_MNFRM(v) (((v) & 0xFF) << 0) /* Min. bytes/frame (<= 255) */
+};
+
+/*
+ * The MC-Setup Action Command.
+ */
+typedef struct ac_mcs_t ac_mcs_t;
+struct ac_mcs_t
+{
+ ach_t mcs_h;
+ unsigned short mcs_cnt; /* No. of bytes of MC addresses */
+ unsigned short mcs_data[3]; /* The first MC address .. */
+};
+
+/*
+ * The Transmit Action Command.
+ */
+typedef struct ac_tx_t ac_tx_t;
+struct ac_tx_t
+{
+ ach_t tx_h;
+ unsigned short tx_tbd_offset; /* Address of list of buffers. */
+#if 0
+Linux packets are passed down with the destination MAC address
+and length/type field already prepended to the data,
+so we do not need to insert it. Consistent with this
+we must also set the AC_CFG_ALOC(..) flag during the
+ac_cfg_t action command.
+ unsigned char tx_addr[ADDR_LEN]; /* The frame dest. address */
+ unsigned short tx_length; /* The frame length */
+#endif /* 0 */
+};
+
+/*
+ * The Time Domain Reflectometer Action Command.
+ */
+typedef struct ac_tdr_t ac_tdr_t;
+struct ac_tdr_t
+{
+ ach_t tdr_h;
+ unsigned short tdr_result; /* Result. */
+#define AC_TDR_LNK_OK (0x1 << 15) /* No link problem */
+#define AC_TDR_XCVR_PRB (0x1 << 14) /* Txcvr cable problem */
+#define AC_TDR_ET_OPN (0x1 << 13) /* Open on the link */
+#define AC_TDR_ET_SRT (0x1 << 12) /* Short on the link */
+#define AC_TDR_TIME (0x7FF << 0) /* Distance to problem */
+ /* site in transmit */
+ /* clock cycles */
+};
+
+/*
+ * The Dump Action Command.
+ */
+typedef struct ac_dmp_t ac_dmp_t;
+struct ac_dmp_t
+{
+ ach_t dmp_h;
+ unsigned short dmp_offset; /* Result. */
+};
+
+/*
+ * Size of the result of the dump command.
+ */
+#define DUMPBYTES 170
+
+/*
+ * The Diagnose Action Command.
+ */
+typedef struct ac_dgn_t ac_dgn_t;
+struct ac_dgn_t
+{
+ ach_t dgn_h;
+};
+
+/*
+ * Transmit Buffer Descriptor (TBD).
+ */
+typedef struct tbd_t tbd_t;
+struct tbd_t
+{
+ unsigned short tbd_status; /* Written by the CPU */
+#define TBD_STATUS_EOF (0x1 << 15) /* This TBD is the */
+ /* last for this frame */
+#define TBD_STATUS_ACNT (0x3FFF << 0) /* Actual count of data */
+ /* bytes in this buffer */
+ unsigned short tbd_next_bd_offset; /* Next in list */
+ unsigned short tbd_bufl; /* Buffer address (low) */
+ unsigned short tbd_bufh; /* " " (high) */
+};
+
+/*
+ * Receive Buffer Descriptor (RBD).
+ */
+typedef struct rbd_t rbd_t;
+struct rbd_t
+{
+ unsigned short rbd_status; /* Written by the 82586 */
+#define RBD_STATUS_EOF (0x1 << 15) /* This RBD is the */
+ /* last for this frame */
+#define RBD_STATUS_F (0x1 << 14) /* ACNT field is valid */
+#define RBD_STATUS_ACNT (0x3FFF << 0) /* Actual no. of data */
+ /* bytes in this buffer */
+ unsigned short rbd_next_rbd_offset; /* Next rbd in list */
+ unsigned short rbd_bufl; /* Data pointer (low) */
+ unsigned short rbd_bufh; /* " " (high) */
+ unsigned short rbd_el_size; /* EL+Data buf. size */
+#define RBD_EL (0x1 << 15) /* This BD is the */
+ /* last in the list */
+#define RBD_SIZE (0x3FFF << 0) /* No. of bytes the */
+ /* buffer can hold */
+};
+
+#define rbdoff(p,f) toff(rbd_t, p, f)
+
+/*
+ * Frame Descriptor (FD).
+ */
+typedef struct fd_t fd_t;
+struct fd_t
+{
+ unsigned short fd_status; /* Written by the 82586 */
+#define FD_STATUS_C (0x1 << 15) /* Completed storing frame */
+#define FD_STATUS_B (0x1 << 14) /* FD was consumed by RU */
+#define FD_STATUS_OK (0x1 << 13) /* Frame rxd successfully */
+#define FD_STATUS_S11 (0x1 << 11) /* CRC error */
+#define FD_STATUS_S10 (0x1 << 10) /* Alignment error */
+#define FD_STATUS_S9 (0x1 << 9) /* Ran out of resources */
+#define FD_STATUS_S8 (0x1 << 8) /* Rx DMA overrun */
+#define FD_STATUS_S7 (0x1 << 7) /* Frame too short */
+#define FD_STATUS_S6 (0x1 << 6) /* No EOF flag */
+ unsigned short fd_command; /* Command */
+#define FD_COMMAND_EL (0x1 << 15) /* Last FD in list */
+#define FD_COMMAND_S (0x1 << 14) /* Suspend RU after rx */
+ unsigned short fd_link_offset; /* Next FD */
+ unsigned short fd_rbd_offset; /* First RBD (data) */
+ /* Prepared by CPU, */
+ /* updated by 82586 */
+#if 0
+I think the rest is unused since we
+have set AC_CFG_ALOC(..). However, just
+in case, we leave the space.
+#endif /* 0 */
+ unsigned char fd_dest[ADDR_LEN]; /* Destination address */
+ /* Written by 82586 */
+ unsigned char fd_src[ADDR_LEN]; /* Source address */
+ /* Written by 82586 */
+ unsigned short fd_length; /* Frame length or type */
+ /* Written by 82586 */
+};
+
+#define fdoff(p,f) toff(fd_t, p, f)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * For more details, see wavelan.c.
+ */
diff --git a/i386/i386at/gpl/linux/net/iow.h b/i386/i386at/gpl/linux/net/iow.h
new file mode 100644
index 00000000..6e15688f
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/iow.h
@@ -0,0 +1,6 @@
+#ifndef _ASM_IOW_H
+#define _ASM_IOW_H
+
+/* no longer used */
+
+#endif
diff --git a/i386/i386at/gpl/linux/net/lance.c b/i386/i386at/gpl/linux/net/lance.c
new file mode 100644
index 00000000..4a388f77
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/lance.c
@@ -0,0 +1,1129 @@
+/* lance.c: An AMD LANCE ethernet driver for linux. */
+/*
+ Written 1993,1994,1995 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the Allied Telesis AT1500 and HP J2405A, and should work
+ with most other LANCE-based bus-master (NE2100 clone) ethercards.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static const char *version = "lance.c:v1.08 4/10/95 dplatt@3do.com\n";
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+static unsigned int lance_portlist[] = {0x300, 0x320, 0x340, 0x360, 0};
+void lance_probe1(int ioaddr);
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry lance_drv =
+{"lance", lance_probe1, LANCE_TOTAL_SIZE, lance_portlist};
+#endif
+
+#ifdef LANCE_DEBUG
+int lance_debug = LANCE_DEBUG;
+#else
+int lance_debug = 1;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the AMD 79C960, the "PCnet-ISA
+single-chip ethernet controller for ISA". This chip is used in a wide
+variety of boards from vendors such as Allied Telesis, HP, Kingston,
+and Boca. This driver is also intended to work with older AMD 7990
+designs, such as the NE1500 and NE2100, and newer 79C961. For convenience,
+I use the name LANCE to refer to all of the AMD chips, even though it properly
+refers only to the original 7990.
+
+II. Board-specific settings
+
+The driver is designed to work the boards that use the faster
+bus-master mode, rather than in shared memory mode. (Only older designs
+have on-board buffer memory needed to support the slower shared memory mode.)
+
+Most ISA boards have jumpered settings for the I/O base, IRQ line, and DMA
+channel. This driver probes the likely base addresses:
+{0x300, 0x320, 0x340, 0x360}.
+After the board is found it generates a DMA-timeout interrupt and uses
+autoIRQ to find the IRQ line. The DMA channel can be set with the low bits
+of the otherwise-unused dev->mem_start value (aka PARAM1). If unset it is
+probed for by enabling each free DMA channel in turn and checking if
+initialization succeeds.
+
+The HP-J2405A board is an exception: with this board it's easy to read the
+EEPROM-set values for the base, IRQ, and DMA. (Of course you must already
+_know_ the base address -- that field is for writing the EEPROM.)
+
+III. Driver operation
+
+IIIa. Ring buffers
+The LANCE uses ring buffers of Tx and Rx descriptors. Each entry describes
+the base and length of the data buffer, along with status bits. The length
+of these buffers is set by LANCE_LOG_{RX,TX}_BUFFERS, which is log_2() of
+the buffer length (rather than being directly the buffer length) for
+implementation ease. The current values are 2 (Tx) and 4 (Rx), which leads to
+ring sizes of 4 (Tx) and 16 (Rx). Increasing the number of ring entries
+needlessly uses extra space and reduces the chance that an upper layer will
+be able to reorder queued Tx packets based on priority. Decreasing the number
+of entries makes it more difficult to achieve back-to-back packet transmission
+and increases the chance that Rx ring will overflow. (Consider the worst case
+of receiving back-to-back minimum-sized packets.)
+
+The LANCE has the capability to "chain" both Rx and Tx buffers, but this driver
+statically allocates full-sized (slightly oversized -- PKT_BUF_SZ) buffers to
+avoid the administrative overhead. For the Rx side this avoids dynamically
+allocating full-sized buffers "just in case", at the expense of a
+memory-to-memory data copy for each packet received. For most systems this
+is a good tradeoff: the Rx buffer will always be in low memory, the copy
+is inexpensive, and it primes the cache for later packet processing. For Tx
+the buffers are only used when needed as low-memory bounce buffers.
+
+IIIB. 16M memory limitations.
+For the ISA bus master mode all structures used directly by the LANCE,
+the initialization block, Rx and Tx rings, and data buffers, must be
+accessible from the ISA bus, i.e. in the lower 16M of real memory.
+This is a problem for current Linux kernels on >16M machines. The network
+devices are initialized after memory initialization, and the kernel doles out
+memory from the top of memory downward. The current solution is to have a
+special network initialization routine that's called before memory
+initialization; this will eventually be generalized for all network devices.
+As mentioned before, low-memory "bounce-buffers" are used when needed.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'lp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'lp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+*/
+
+/* Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ Reasonable default values are 4 Tx buffers, and 16 Rx buffers.
+ That translates to 2 (4 == 2^^2) and 4 (16 == 2^^4). */
+#ifndef LANCE_LOG_TX_BUFFERS
+#define LANCE_LOG_TX_BUFFERS 4
+#define LANCE_LOG_RX_BUFFERS 4
+#endif
+
+#define TX_RING_SIZE (1 << (LANCE_LOG_TX_BUFFERS))
+#define TX_RING_MOD_MASK (TX_RING_SIZE - 1)
+#define TX_RING_LEN_BITS ((LANCE_LOG_TX_BUFFERS) << 29)
+
+#define RX_RING_SIZE (1 << (LANCE_LOG_RX_BUFFERS))
+#define RX_RING_MOD_MASK (RX_RING_SIZE - 1)
+#define RX_RING_LEN_BITS ((LANCE_LOG_RX_BUFFERS) << 29)
+
+#define PKT_BUF_SZ 1544
+
+/* Offsets from base I/O address. */
+#define LANCE_DATA 0x10
+#define LANCE_ADDR 0x12
+#define LANCE_RESET 0x14
+#define LANCE_BUS_IF 0x16
+#define LANCE_TOTAL_SIZE 0x18
+
+/* The LANCE Rx and Tx ring descriptors. */
+struct lance_rx_head {
+ int base;
+ short buf_length; /* This length is 2s complement (negative)! */
+ short msg_length; /* This length is "normal". */
+};
+
+struct lance_tx_head {
+ int base;
+ short length; /* Length is 2s complement (negative)! */
+ short misc;
+};
+
+/* The LANCE initialization block, described in databook. */
+struct lance_init_block {
+ unsigned short mode; /* Pre-set mode (reg. 15) */
+ unsigned char phys_addr[6]; /* Physical ethernet address */
+ unsigned filter[2]; /* Multicast filter (unused). */
+ /* Receive and transmit ring base, along with extra bits. */
+ unsigned rx_ring; /* Tx and Rx ring base pointers */
+ unsigned tx_ring;
+};
+
+struct lance_private {
+ /* The Tx and Rx ring entries must be aligned on 8-byte boundaries.
+ This is always true for kmalloc'ed memory */
+ struct lance_rx_head rx_ring[RX_RING_SIZE];
+ struct lance_tx_head tx_ring[TX_RING_SIZE];
+ struct lance_init_block init_block;
+ const char *name;
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ long rx_buffs; /* Address of Rx and Tx buffers. */
+ /* Tx low-memory "bounce buffer" address. */
+ char (*tx_bounce_buffs)[PKT_BUF_SZ];
+ int cur_rx, cur_tx; /* The next free ring entry */
+ int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ int dma;
+ struct enet_statistics stats;
+ unsigned char chip_version; /* See lance_chip_type. */
+ char tx_full;
+ char lock;
+};
+
+#define LANCE_MUST_PAD 0x00000001
+#define LANCE_ENABLE_AUTOSELECT 0x00000002
+#define LANCE_MUST_REINIT_RING 0x00000004
+#define LANCE_MUST_UNRESET 0x00000008
+#define LANCE_HAS_MISSED_FRAME 0x00000010
+
+/* A mapping from the chip ID number to the part number and features.
+ These are from the datasheets -- in real life the '970 version
+ reportedly has the same ID as the '965. */
+static struct lance_chip_type {
+ int id_number;
+ const char *name;
+ int flags;
+} chip_table[] = {
+ {0x0000, "LANCE 7990", /* Ancient lance chip. */
+ LANCE_MUST_PAD + LANCE_MUST_UNRESET},
+ {0x0003, "PCnet/ISA 79C960", /* 79C960 PCnet/ISA. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2260, "PCnet/ISA+ 79C961", /* 79C961 PCnet/ISA+, Plug-n-Play. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x2420, "PCnet/PCI 79C970", /* 79C970 or 79C974 PCnet-SCSI, PCI. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ /* Bug: the PCnet/PCI actually uses the PCnet/VLB ID number, so just call
+ it the PCnet32. */
+ {0x2430, "PCnet32", /* 79C965 PCnet for VL bus. */
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+ {0x0, "PCnet (unknown)",
+ LANCE_ENABLE_AUTOSELECT + LANCE_MUST_REINIT_RING +
+ LANCE_HAS_MISSED_FRAME},
+};
+
+enum {OLD_LANCE = 0, PCNET_ISA=1, PCNET_ISAP=2, PCNET_PCI=3, PCNET_VLB=4, LANCE_UNKNOWN=5};
+
+/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
+static unsigned char pci_irq_line = 0;
+
+/* Non-zero if lance_probe1() needs to allocate low-memory bounce buffers.
+ Assume yes until we know the memory size. */
+static unsigned char lance_need_isa_bounce_buffers = 1;
+
+static int lance_open(struct device *dev);
+static void lance_init_ring(struct device *dev);
+static int lance_start_xmit(struct sk_buff *skb, struct device *dev);
+static int lance_rx(struct device *dev);
+static void lance_interrupt(int irq, struct pt_regs *regs);
+static int lance_close(struct device *dev);
+static struct enet_statistics *lance_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+
+
+/* This lance probe is unlike the other board probes in 1.0.*. The LANCE may
+ have to allocate a contiguous low-memory region for bounce buffers.
+ This requirement is satisfied by having the lance initialization occur
+ before the memory management system is started, and thus well before the
+ other probes. */
+
+int lance_init(void)
+{
+ int *port;
+
+ if (high_memory <= 16*1024*1024)
+ lance_need_isa_bounce_buffers = 0;
+
+#if defined(CONFIG_PCI)
+ if (pcibios_present()) {
+ int pci_index;
+ printk("lance.c: PCI bios is present, checking for devices...\n");
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn;
+ unsigned int pci_ioaddr;
+ unsigned short pci_command;
+
+ if (pcibios_find_device (PCI_VENDOR_ID_AMD,
+ PCI_DEVICE_ID_AMD_LANCE, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ /* PCI Spec 2.1 states that it is either the driver or PCI card's
+ * responsibility to set the PCI Master Enable Bit if needed.
+ * (From Mark Stockton <marks@schooner.sys.hou.compaq.com>)
+ */
+ pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, &pci_command);
+ if ( ! (pci_command & PCI_COMMAND_MASTER)) {
+ printk("PCI Master Bit has not been set. Setting...\n");
+ pci_command |= PCI_COMMAND_MASTER;
+ pcibios_write_config_word(pci_bus, pci_device_fn,
+ PCI_COMMAND, pci_command);
+ }
+ printk("Found PCnet/PCI at %#x, irq %d.\n",
+ pci_ioaddr, pci_irq_line);
+ lance_probe1(pci_ioaddr);
+ pci_irq_line = 0;
+ }
+ }
+#endif /* defined(CONFIG_PCI) */
+
+ for (port = lance_portlist; *port; port++) {
+ int ioaddr = *port;
+
+ if ( check_region(ioaddr, LANCE_TOTAL_SIZE) == 0) {
+ /* Detect "normal" 0x57 0x57 and the NI6510EB 0x52 0x44
+ signatures w/ minimal I/O reads */
+ char offset15, offset14 = inb(ioaddr + 14);
+
+ if ((offset14 == 0x52 || offset14 == 0x57) &&
+ ((offset15 = inb(ioaddr + 15)) == 0x57 || offset15 == 0x44))
+ lance_probe1(ioaddr);
+ }
+ }
+
+ return 0;
+}
+
+void lance_probe1(int ioaddr)
+{
+ struct device *dev;
+ struct lance_private *lp;
+ short dma_channels; /* Mark spuriously-busy DMA channels */
+ int i, reset_val, lance_version;
+ const char *chipname;
+ /* Flags for specific chips or boards. */
+ unsigned char hpJ2405A = 0; /* HP ISA adaptor */
+ int hp_builtin = 0; /* HP on-board ethernet. */
+ static int did_version = 0; /* Already printed version info. */
+
+ /* First we look for special cases.
+ Check for HP's on-board ethernet by looking for 'HP' in the BIOS.
+ There are two HP versions, check the BIOS for the configuration port.
+ This method provided by L. Julliard, Laurent_Julliard@grenoble.hp.com.
+ */
+ if ( *((unsigned short *) 0x000f0102) == 0x5048) {
+ static const short ioaddr_table[] = { 0x300, 0x320, 0x340, 0x360};
+ int hp_port = ( *((unsigned char *) 0x000f00f1) & 1) ? 0x499 : 0x99;
+ /* We can have boards other than the built-in! Verify this is on-board. */
+ if ((inb(hp_port) & 0xc0) == 0x80
+ && ioaddr_table[inb(hp_port) & 3] == ioaddr)
+ hp_builtin = hp_port;
+ }
+ /* We also recognize the HP Vectra on-board here, but check below. */
+ hpJ2405A = (inb(ioaddr) == 0x08 && inb(ioaddr+1) == 0x00
+ && inb(ioaddr+2) == 0x09);
+
+ /* Reset the LANCE. */
+ reset_val = inw(ioaddr+LANCE_RESET); /* Reset the LANCE */
+
+ /* The Un-Reset needed is only needed for the real NE2100, and will
+ confuse the HP board. */
+ if (!hpJ2405A)
+ outw(reset_val, ioaddr+LANCE_RESET);
+
+ outw(0x0000, ioaddr+LANCE_ADDR); /* Switch to window 0 */
+ if (inw(ioaddr+LANCE_DATA) != 0x0004)
+ return;
+
+ /* Get the version of the chip. */
+ outw(88, ioaddr+LANCE_ADDR);
+ if (inw(ioaddr+LANCE_ADDR) != 88) {
+ lance_version = 0;
+ } else { /* Good, it's a newer chip. */
+ int chip_version = inw(ioaddr+LANCE_DATA);
+ outw(89, ioaddr+LANCE_ADDR);
+ chip_version |= inw(ioaddr+LANCE_DATA) << 16;
+ if (lance_debug > 2)
+ printk(" LANCE chip version is %#x.\n", chip_version);
+ if ((chip_version & 0xfff) != 0x003)
+ return;
+ chip_version = (chip_version >> 12) & 0xffff;
+ for (lance_version = 1; chip_table[lance_version].id_number; lance_version++) {
+ if (chip_table[lance_version].id_number == chip_version)
+ break;
+ }
+ }
+
+ dev = init_etherdev(0, 0);
+ chipname = chip_table[lance_version].name;
+ printk("%s: %s at %#3x,", dev->name, chipname, ioaddr);
+
+ /* There is a 16 byte station address PROM at the base address.
+ The first six bytes are the station address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = inb(ioaddr + i));
+
+ dev->base_addr = ioaddr;
+ request_region(ioaddr, LANCE_TOTAL_SIZE, chip_table[lance_version].name);
+
+ /* Make certain the data structures used by the LANCE are aligned and DMAble. */
+ lp = (struct lance_private *) kmalloc(sizeof(*lp), GFP_DMA | GFP_KERNEL);
+ memset(lp, 0, sizeof(*lp));
+ dev->priv = lp;
+ lp->name = chipname;
+ lp->rx_buffs = (unsigned long) kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_DMA | GFP_KERNEL);
+ lp->tx_bounce_buffs = NULL;
+ if (lance_need_isa_bounce_buffers)
+ lp->tx_bounce_buffs = kmalloc(PKT_BUF_SZ*TX_RING_SIZE, GFP_DMA | GFP_KERNEL);
+
+ lp->chip_version = lance_version;
+
+ lp->init_block.mode = 0x0003; /* Disable Rx and Tx. */
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
+ lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
+
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+ outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+ outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ inw(ioaddr+LANCE_ADDR);
+
+ if (pci_irq_line) {
+ dev->dma = 4; /* Native bus-master, no DMA channel needed. */
+ dev->irq = pci_irq_line;
+ } else if (hp_builtin) {
+ static const char dma_tbl[4] = {3, 5, 6, 0};
+ static const char irq_tbl[4] = {3, 4, 5, 9};
+ unsigned char port_val = inb(hp_builtin);
+ dev->dma = dma_tbl[(port_val >> 4) & 3];
+ dev->irq = irq_tbl[(port_val >> 2) & 3];
+ printk(" HP Vectra IRQ %d DMA %d.\n", dev->irq, dev->dma);
+ } else if (hpJ2405A) {
+ static const char dma_tbl[4] = {3, 5, 6, 7};
+ static const char irq_tbl[8] = {3, 4, 5, 9, 10, 11, 12, 15};
+ short reset_val = inw(ioaddr+LANCE_RESET);
+ dev->dma = dma_tbl[(reset_val >> 2) & 3];
+ dev->irq = irq_tbl[(reset_val >> 4) & 7];
+ printk(" HP J2405A IRQ %d DMA %d.\n", dev->irq, dev->dma);
+ } else if (lance_version == PCNET_ISAP) { /* The plug-n-play version. */
+ short bus_info;
+ outw(8, ioaddr+LANCE_ADDR);
+ bus_info = inw(ioaddr+LANCE_BUS_IF);
+ dev->dma = bus_info & 0x07;
+ dev->irq = (bus_info >> 4) & 0x0F;
+ } else {
+ /* The DMA channel may be passed in PARAM1. */
+ if (dev->mem_start & 0x07)
+ dev->dma = dev->mem_start & 0x07;
+ }
+
+ if (dev->dma == 0) {
+ /* Read the DMA channel status register, so that we can avoid
+ stuck DMA channels in the DMA detection below. */
+ dma_channels = ((inb(DMA1_STAT_REG) >> 4) & 0x0f) |
+ (inb(DMA2_STAT_REG) & 0xf0);
+ }
+ if (dev->irq >= 2)
+ printk(" assigned IRQ %d", dev->irq);
+ else {
+ /* To auto-IRQ we enable the initialization-done and DMA error
+ interrupts. For ISA boards we get a DMA error, but VLB and PCI
+ boards will work. */
+ autoirq_setup(0);
+
+ /* Trigger an initialization just for the interrupt. */
+ outw(0x0041, ioaddr+LANCE_DATA);
+
+ dev->irq = autoirq_report(1);
+ if (dev->irq)
+ printk(", probed IRQ %d", dev->irq);
+ else {
+ printk(", failed to detect IRQ line.\n");
+ return;
+ }
+
+ /* Check for the initialization done bit, 0x0100, which means
+ that we don't need a DMA channel. */
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ dev->dma = 4;
+ }
+
+ if (dev->dma == 4) {
+ printk(", no DMA needed.\n");
+ } else if (dev->dma) {
+ if (request_dma(dev->dma, chipname)) {
+ printk("DMA %d allocation failed.\n", dev->dma);
+ return;
+ } else
+ printk(", assigned DMA %d.\n", dev->dma);
+ } else { /* OK, we have to auto-DMA. */
+ for (i = 0; i < 4; i++) {
+ static const char dmas[] = { 5, 6, 7, 3 };
+ int dma = dmas[i];
+ int boguscnt;
+
+ /* Don't enable a permanently busy DMA channel, or the machine
+ will hang. */
+ if (test_bit(dma, &dma_channels))
+ continue;
+ outw(0x7f04, ioaddr+LANCE_DATA); /* Clear the memory error bits. */
+ if (request_dma(dma, chipname))
+ continue;
+ set_dma_mode(dma, DMA_MODE_CASCADE);
+ enable_dma(dma);
+
+ /* Trigger an initialization. */
+ outw(0x0001, ioaddr+LANCE_DATA);
+ for (boguscnt = 100; boguscnt > 0; --boguscnt)
+ if (inw(ioaddr+LANCE_DATA) & 0x0900)
+ break;
+ if (inw(ioaddr+LANCE_DATA) & 0x0100) {
+ dev->dma = dma;
+ printk(", DMA %d.\n", dev->dma);
+ break;
+ } else {
+ disable_dma(dma);
+ free_dma(dma);
+ }
+ }
+ if (i == 4) { /* Failure: bail. */
+ printk("DMA detection failed.\n");
+ return;
+ }
+ }
+
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
+ /* Turn on auto-select of media (10baseT or BNC) so that the user
+ can watch the LEDs even if the board isn't opened. */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(0x0002, ioaddr+LANCE_BUS_IF);
+ }
+
+ if (lance_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ /* The LANCE-specific entries in the device structure. */
+ dev->open = &lance_open;
+ dev->hard_start_xmit = &lance_start_xmit;
+ dev->stop = &lance_close;
+ dev->get_stats = &lance_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ return;
+}
+
+
+static int
+lance_open(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ if (dev->irq == 0 ||
+ request_irq(dev->irq, &lance_interrupt, 0, lp->name)) {
+ return -EAGAIN;
+ }
+
+ /* We used to allocate DMA here, but that was silly.
+ DMA lines can't be shared! We now permanently allocate them. */
+
+ irq2dev_map[dev->irq] = dev;
+
+ /* Reset the LANCE */
+ inw(ioaddr+LANCE_RESET);
+
+ /* The DMA controller is used as a no-operation slave, "cascade mode". */
+ if (dev->dma != 4) {
+ enable_dma(dev->dma);
+ set_dma_mode(dev->dma, DMA_MODE_CASCADE);
+ }
+
+ /* Un-Reset the LANCE, needed only for the NE2100. */
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_UNRESET)
+ outw(0, ioaddr+LANCE_RESET);
+
+ if (chip_table[lp->chip_version].flags & LANCE_ENABLE_AUTOSELECT) {
+ /* This is 79C960-specific: Turn on auto-select of media (AUI, BNC). */
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(0x0002, ioaddr+LANCE_BUS_IF);
+ }
+
+ if (lance_debug > 1)
+ printk("%s: lance_open() irq %d dma %d tx/rx rings %#x/%#x init %#x.\n",
+ dev->name, dev->irq, dev->dma, (int) lp->tx_ring, (int) lp->rx_ring,
+ (int) &lp->init_block);
+
+ lance_init_ring(dev);
+ /* Re-initialize the LANCE, and start it when done. */
+ outw(0x0001, ioaddr+LANCE_ADDR);
+ outw((short) (int) &lp->init_block, ioaddr+LANCE_DATA);
+ outw(0x0002, ioaddr+LANCE_ADDR);
+ outw(((int)&lp->init_block) >> 16, ioaddr+LANCE_DATA);
+
+ outw(0x0004, ioaddr+LANCE_ADDR);
+ outw(0x0915, ioaddr+LANCE_DATA);
+
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0001, ioaddr+LANCE_DATA);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ i = 0;
+ while (i++ < 100)
+ if (inw(ioaddr+LANCE_DATA) & 0x0100)
+ break;
+ /*
+ * We used to clear the InitDone bit, 0x0100, here but Mark Stockton
+ * reports that doing so triggers a bug in the '974.
+ */
+ outw(0x0042, ioaddr+LANCE_DATA);
+
+ if (lance_debug > 2)
+ printk("%s: LANCE open after %d ticks, init block %#x csr0 %4.4x.\n",
+ dev->name, i, (int) &lp->init_block, inw(ioaddr+LANCE_DATA));
+
+ return 0; /* Always succeed */
+}
+
+/* The LANCE has been halted for one reason or another (busmaster memory
+ arbitration error, Tx FIFO underflow, driver stopped it to reconfigure,
+ etc.). Modern LANCE variants always reload their ring-buffer
+ configuration when restarted, so we must reinitialize our ring
+ context before restarting. As part of this reinitialization,
+ find all packets still on the Tx ring and pretend that they had been
+ sent (in effect, drop the packets on the floor) - the higher-level
+ protocols will time out and retransmit. It'd be better to shuffle
+ these skbs to a temp list and then actually re-Tx them after
+ restarting the chip, but I'm too lazy to do so right now. dplatt@3do.com
+*/
+
+static void
+lance_purge_tx_ring(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
+
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ if (lp->tx_skbuff[i]) {
+ dev_kfree_skb(lp->tx_skbuff[i],FREE_WRITE);
+ lp->tx_skbuff[i] = NULL;
+ }
+ }
+}
+
+
+/* Initialize the LANCE Rx and Tx rings. */
+static void
+lance_init_ring(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int i;
+
+ lp->lock = 0, lp->tx_full = 0;
+ lp->cur_rx = lp->cur_tx = 0;
+ lp->dirty_rx = lp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ lp->rx_ring[i].base = (lp->rx_buffs + i*PKT_BUF_SZ) | 0x80000000;
+ lp->rx_ring[i].buf_length = -PKT_BUF_SZ;
+ }
+ /* The Tx buffer address is filled in as needed, but we do need to clear
+ the upper ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ lp->tx_ring[i].base = 0;
+ }
+
+ lp->init_block.mode = 0x0000;
+ for (i = 0; i < 6; i++)
+ lp->init_block.phys_addr[i] = dev->dev_addr[i];
+ lp->init_block.filter[0] = 0x00000000;
+ lp->init_block.filter[1] = 0x00000000;
+ lp->init_block.rx_ring = (int)lp->rx_ring | RX_RING_LEN_BITS;
+ lp->init_block.tx_ring = (int)lp->tx_ring | TX_RING_LEN_BITS;
+}
+
+static void
+lance_restart(struct device *dev, unsigned int csr0_bits, int must_reinit)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+
+ if (must_reinit ||
+ (chip_table[lp->chip_version].flags & LANCE_MUST_REINIT_RING)) {
+ lance_purge_tx_ring(dev);
+ lance_init_ring(dev);
+ }
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(csr0_bits, dev->base_addr + LANCE_DATA);
+}
+
+static int
+lance_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+ unsigned long flags;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 20)
+ return 1;
+ outw(0, ioaddr+LANCE_ADDR);
+ printk("%s: transmit timed out, status %4.4x, resetting.\n",
+ dev->name, inw(ioaddr+LANCE_DATA));
+ outw(0x0004, ioaddr+LANCE_DATA);
+ lp->stats.tx_errors++;
+#ifndef final_version
+ {
+ int i;
+ printk(" Ring data dump: dirty_tx %d cur_tx %d%s cur_rx %d.",
+ lp->dirty_tx, lp->cur_tx, lp->tx_full ? " (full)" : "",
+ lp->cur_rx);
+ for (i = 0 ; i < RX_RING_SIZE; i++)
+ printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ lp->rx_ring[i].base, -lp->rx_ring[i].buf_length,
+ lp->rx_ring[i].msg_length);
+ for (i = 0 ; i < TX_RING_SIZE; i++)
+ printk("%s %08x %04x %04x", i & 0x3 ? "" : "\n ",
+ lp->tx_ring[i].base, -lp->tx_ring[i].length,
+ lp->tx_ring[i].misc);
+ printk("\n");
+ }
+#endif
+ lance_restart(dev, 0x0043, 1);
+
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+
+ return 0;
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+
+ if (lance_debug > 3) {
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ printk("%s: lance_start_xmit() called, csr0 %4.4x.\n", dev->name,
+ inw(ioaddr+LANCE_DATA));
+ outw(0x0000, ioaddr+LANCE_DATA);
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ if (set_bit(0, (void*)&lp->lock) != 0) {
+ if (lance_debug > 0)
+ printk("%s: tx queue lock!.\n", dev->name);
+ /* don't clear dev->tbusy flag. */
+ return 1;
+ }
+
+ /* Fill in a Tx ring entry */
+
+ /* Mask to ring buffer boundary. */
+ entry = lp->cur_tx & TX_RING_MOD_MASK;
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ /* The old LANCE chips doesn't automatically pad buffers to min. size. */
+ if (chip_table[lp->chip_version].flags & LANCE_MUST_PAD) {
+ lp->tx_ring[entry].length =
+ -(ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN);
+ } else
+ lp->tx_ring[entry].length = -skb->len;
+
+ lp->tx_ring[entry].misc = 0x0000;
+
+ /* If any part of this buffer is >16M we must copy it to a low-memory
+ buffer. */
+ if ((int)(skb->data) + skb->len > 0x01000000) {
+ if (lance_debug > 5)
+ printk("%s: bouncing a high-memory packet (%#x).\n",
+ dev->name, (int)(skb->data));
+ memcpy(&lp->tx_bounce_buffs[entry], skb->data, skb->len);
+ lp->tx_ring[entry].base =
+ (int)(lp->tx_bounce_buffs + entry) | 0x83000000;
+ dev_kfree_skb (skb, FREE_WRITE);
+ } else {
+ lp->tx_skbuff[entry] = skb;
+ lp->tx_ring[entry].base = (int)(skb->data) | 0x83000000;
+ }
+ lp->cur_tx++;
+
+ /* Trigger an immediate send poll. */
+ outw(0x0000, ioaddr+LANCE_ADDR);
+ outw(0x0048, ioaddr+LANCE_DATA);
+
+ dev->trans_start = jiffies;
+
+ save_flags(flags);
+ cli();
+ lp->lock = 0;
+ if (lp->tx_ring[(entry+1) & TX_RING_MOD_MASK].base == 0)
+ dev->tbusy=0;
+ else
+ lp->tx_full = 1;
+ restore_flags(flags);
+
+ return 0;
+}
+
+/* The LANCE interrupt handler. */
+static void
+lance_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct lance_private *lp;
+ int csr0, ioaddr, boguscnt=10;
+ int must_restart;
+
+ if (dev == NULL) {
+ printk ("lance_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct lance_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ outw(0x00, dev->base_addr + LANCE_ADDR);
+ while ((csr0 = inw(dev->base_addr + LANCE_DATA)) & 0x8600
+ && --boguscnt >= 0) {
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outw(csr0 & ~0x004f, dev->base_addr + LANCE_DATA);
+
+ must_restart = 0;
+
+ if (lance_debug > 5)
+ printk("%s: interrupt csr0=%#2.2x new csr=%#2.2x.\n",
+ dev->name, csr0, inw(dev->base_addr + LANCE_DATA));
+
+ if (csr0 & 0x0400) /* Rx interrupt */
+ lance_rx(dev);
+
+ if (csr0 & 0x0200) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx & TX_RING_MOD_MASK;
+ int status = lp->tx_ring[entry].base;
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ lp->tx_ring[entry].base = 0;
+
+ if (status & 0x40000000) {
+ /* There was an major error, log it. */
+ int err_status = lp->tx_ring[entry].misc;
+ lp->stats.tx_errors++;
+ if (err_status & 0x0400) lp->stats.tx_aborted_errors++;
+ if (err_status & 0x0800) lp->stats.tx_carrier_errors++;
+ if (err_status & 0x1000) lp->stats.tx_window_errors++;
+ if (err_status & 0x4000) {
+ /* Ackk! On FIFO errors the Tx unit is turned off! */
+ lp->stats.tx_fifo_errors++;
+ /* Remove this verbosity later! */
+ printk("%s: Tx FIFO error! Status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ must_restart = 1;
+ }
+ } else {
+ if (status & 0x18000000)
+ lp->stats.collisions++;
+ lp->stats.tx_packets++;
+ }
+
+ /* We must free the original skb if it's not a data-only copy
+ in the bounce buffer. */
+ if (lp->tx_skbuff[entry]) {
+ dev_kfree_skb(lp->tx_skbuff[entry],FREE_WRITE);
+ lp->tx_skbuff[entry] = 0;
+ }
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (lp->tx_full && dev->tbusy
+ && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log misc errors. */
+ if (csr0 & 0x4000) lp->stats.tx_errors++; /* Tx babble. */
+ if (csr0 & 0x1000) lp->stats.rx_errors++; /* Missed a Rx frame. */
+ if (csr0 & 0x0800) {
+ printk("%s: Bus master arbitration failure, status %4.4x.\n",
+ dev->name, csr0);
+ /* Restart the chip. */
+ must_restart = 1;
+ }
+
+ if (must_restart) {
+ /* stop the chip to clear the error condition, then restart */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x0004, dev->base_addr + LANCE_DATA);
+ lance_restart(dev, 0x0002, 0);
+ }
+ }
+
+ /* Clear any other interrupt, and set interrupt enable. */
+ outw(0x0000, dev->base_addr + LANCE_ADDR);
+ outw(0x7940, dev->base_addr + LANCE_DATA);
+
+ if (lance_debug > 4)
+ printk("%s: exiting interrupt, csr%d=%#4.4x.\n",
+ dev->name, inw(ioaddr + LANCE_ADDR),
+ inw(dev->base_addr + LANCE_DATA));
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+lance_rx(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ int entry = lp->cur_rx & RX_RING_MOD_MASK;
+ int i;
+
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (lp->rx_ring[entry].base >= 0) {
+ int status = lp->rx_ring[entry].base >> 24;
+
+ if (status != 0x03) { /* There was an error. */
+ /* There is a tricky error noted by John Murphy,
+ <murf@perftech.com> to Russ Nelson: Even with full-sized
+ buffers it's possible for a jabber packet to use two
+ buffers, with only the last correctly noting the error. */
+ if (status & 0x01) /* Only count a general error at the */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x20) lp->stats.rx_frame_errors++;
+ if (status & 0x10) lp->stats.rx_over_errors++;
+ if (status & 0x08) lp->stats.rx_crc_errors++;
+ if (status & 0x04) lp->stats.rx_fifo_errors++;
+ lp->rx_ring[entry].base &= 0x03ffffff;
+ }
+ else
+ {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = (lp->rx_ring[entry].msg_length & 0xfff)-4;
+ struct sk_buff *skb;
+
+ if(pkt_len<60)
+ {
+ printk("%s: Runt packet!\n",dev->name);
+ lp->stats.rx_errors++;
+ }
+ else
+ {
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL)
+ {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ for (i=0; i < RX_RING_SIZE; i++)
+ if (lp->rx_ring[(entry+i) & RX_RING_MOD_MASK].base < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2)
+ {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].base |= 0x80000000;
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2); /* 16 byte align */
+ skb_put(skb,pkt_len); /* Make room */
+ eth_copy_and_sum(skb,
+ (unsigned char *)(lp->rx_ring[entry].base & 0x00ffffff),
+ pkt_len,0);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ }
+ /* The docs say that the buffer length isn't touched, but Andrew Boyd
+ of QNX reports that some revs of the 79C965 clear it. */
+ lp->rx_ring[entry].buf_length = -PKT_BUF_SZ;
+ lp->rx_ring[entry].base |= 0x80000000;
+ entry = (++lp->cur_rx) & RX_RING_MOD_MASK;
+ }
+
+ /* We should check that at least two ring entries are free. If not,
+ we should free one and mark stats->rx_dropped++. */
+
+ return 0;
+}
+
+static int
+lance_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ outw(112, ioaddr+LANCE_ADDR);
+ lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ }
+ outw(0, ioaddr+LANCE_ADDR);
+
+ if (lance_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inw(ioaddr+LANCE_DATA));
+
+ /* We stop the LANCE here -- it occasionally polls
+ memory if we don't. */
+ outw(0x0004, ioaddr+LANCE_DATA);
+
+ if (dev->dma != 4)
+ disable_dma(dev->dma);
+
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ return 0;
+}
+
+static struct enet_statistics *
+lance_get_stats(struct device *dev)
+{
+ struct lance_private *lp = (struct lance_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+ short saved_addr;
+ unsigned long flags;
+
+ if (chip_table[lp->chip_version].flags & LANCE_HAS_MISSED_FRAME) {
+ save_flags(flags);
+ cli();
+ saved_addr = inw(ioaddr+LANCE_ADDR);
+ outw(112, ioaddr+LANCE_ADDR);
+ lp->stats.rx_missed_errors = inw(ioaddr+LANCE_DATA);
+ outw(saved_addr, ioaddr+LANCE_ADDR);
+ restore_flags(flags);
+ }
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ outw(0, ioaddr+LANCE_ADDR);
+ outw(0x0004, ioaddr+LANCE_DATA); /* Temporarily stop the lance. */
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ outw(15, ioaddr+LANCE_ADDR);
+ outw(0x8000, ioaddr+LANCE_DATA); /* Set promiscuous mode */
+ } else {
+ short multicast_table[4];
+ int i;
+ int num_addrs=dev->mc_count;
+ if(dev->flags&IFF_ALLMULTI)
+ num_addrs=1;
+ /* FIXIT: We don't use the multicast table, but rely on upper-layer filtering. */
+ memset(multicast_table, (num_addrs == 0) ? 0 : -1, sizeof(multicast_table));
+ for (i = 0; i < 4; i++) {
+ outw(8 + i, ioaddr+LANCE_ADDR);
+ outw(multicast_table[i], ioaddr+LANCE_DATA);
+ }
+ outw(15, ioaddr+LANCE_ADDR);
+ outw(0x0000, ioaddr+LANCE_DATA); /* Unset promiscuous mode */
+ }
+
+ lance_restart(dev, 0x0142, 0); /* Resume normal operation */
+
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c lance.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/ne.c b/i386/i386at/gpl/linux/net/ne.c
new file mode 100644
index 00000000..4da4efb7
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ne.c
@@ -0,0 +1,733 @@
+/* ne.c: A general non-shared-memory NS8390 ethernet driver for linux. */
+/*
+ Written 1992-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This driver should work with many programmed-I/O 8390-based ethernet
+ boards. Currently it supports the NE1000, NE2000, many clones,
+ and some Cabletron products.
+
+ Changelog:
+
+ Paul Gortmaker : use ENISR_RDC to monitor Tx PIO uploads, made
+ sanity checks and bad clone support optional.
+ Paul Gortmaker : new reset code, reset card after probe at boot.
+ Paul Gortmaker : multiple card support for module users.
+ Paul Gortmaker : Support for PCI ne2k clones, similar to lance.c
+
+*/
+
+/* Routines for the NatSemi-based designs (NE[12]000). */
+
+static const char *version =
+ "ne.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/system.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* Some defines that people can play with if so inclined. */
+
+/* Do we support clones that don't adhere to 14,15 of the SAprom ? */
+#define SUPPORT_NE_BAD_CLONES
+
+/* Do we perform extra sanity checks on stuff ? */
+/* #define NE_SANITY_CHECK */
+
+/* Do we implement the read before write bugfix ? */
+/* #define NE_RW_BUGFIX */
+
+/* Do we have a non std. amount of memory? (in units of 256 byte pages) */
+/* #define PACKETBUF_MEMSIZE 0x40 */
+
+/* ---- No user-serviceable parts below ---- */
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int netcard_portlist[] =
+{ 0x300, 0x280, 0x320, 0x340, 0x360, 0};
+
+#ifdef SUPPORT_NE_BAD_CLONES
+/* A list of bad clones that we none-the-less recognize. */
+static struct { const char *name8, *name16; unsigned char SAprefix[4];}
+bad_clone_list[] = {
+ {"DE100", "DE200", {0x00, 0xDE, 0x01,}},
+ {"DE120", "DE220", {0x00, 0x80, 0xc8,}},
+ {"DFI1000", "DFI2000", {'D', 'F', 'I',}}, /* Original, eh? */
+ {"EtherNext UTP8", "EtherNext UTP16", {0x00, 0x00, 0x79}},
+ {"NE1000","NE2000-invalid", {0x00, 0x00, 0xd8}}, /* Ancient real NE1000. */
+ {"NN1000", "NN2000", {0x08, 0x03, 0x08}}, /* Outlaw no-name clone. */
+ {"4-DIM8","4-DIM16", {0x00,0x00,0x4d,}}, /* Outlaw 4-Dimension cards. */
+ {"Con-Intl_8", "Con-Intl_16", {0x00, 0x00, 0x24}}, /* Connect Int'nl */
+ {0,}
+};
+#endif
+
+#define NE_BASE (dev->base_addr)
+#define NE_CMD 0x00
+#define NE_DATAPORT 0x10 /* NatSemi-defined port window offset. */
+#define NE_RESET 0x1f /* Issue a read to reset, a write to clear. */
+#define NE_IO_EXTENT 0x20
+
+#define NE1SM_START_PG 0x20 /* First page of TX buffer */
+#define NE1SM_STOP_PG 0x40 /* Last page +1 of RX ring */
+#define NESM_START_PG 0x40 /* First page of TX buffer */
+#define NESM_STOP_PG 0x80 /* Last page +1 of RX ring */
+
+/* Non-zero only if the current card is a PCI with BIOS-set IRQ. */
+static unsigned char pci_irq_line = 0;
+
+int ne_probe(struct device *dev);
+static int ne_probe1(struct device *dev, int ioaddr);
+
+static int ne_open(struct device *dev);
+static int ne_close(struct device *dev);
+
+static void ne_reset_8390(struct device *dev);
+static void ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ne_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ne_block_output(struct device *dev, const int count,
+ const unsigned char *buf, const int start_page);
+
+
+/* Probe for various non-shared-memory ethercards.
+
+ NEx000-clone boards have a Station Address PROM (SAPROM) in the packet
+ buffer memory space. NE2000 clones have 0x57,0x57 in bytes 0x0e,0x0f of
+ the SAPROM, while other supposed NE2000 clones must be detected by their
+ SA prefix.
+
+ Reading the SAPROM from a word-wide card with the 8390 set in byte-wide
+ mode results in doubled values, which can be detected and compensated for.
+
+ The probe is also responsible for initializing the card and filling
+ in the 'dev' and 'ei_status' structures.
+
+ We use the minimum memory size for some ethercard product lines, iff we can't
+ distinguish models. You can increase the packet buffer size by setting
+ PACKETBUF_MEMSIZE. Reported Cabletron packet buffer locations are:
+ E1010 starts at 0x100 and ends at 0x2000.
+ E1010-x starts at 0x100 and ends at 0x8000. ("-x" means "more memory")
+ E2010 starts at 0x100 and ends at 0x4000.
+ E2010-x starts at 0x100 and ends at 0xffff. */
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry netcard_drv =
+{"ne", ne_probe1, NE_IO_EXTENT, netcard_portlist};
+#else
+
+/* Note that this probe only picks up one card at a time, even for multiple
+ PCI ne2k cards. Use "ether=0,0,eth1" if you have a second PCI ne2k card.
+ This keeps things consistent regardless of the bus type of the card. */
+
+int ne_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ /* First check any supplied i/o locations. User knows best. <cough> */
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ne_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ /* Then look for any installed PCI clones */
+#if defined(CONFIG_PCI)
+ if (pcibios_present()) {
+ int pci_index;
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn;
+ unsigned int pci_ioaddr;
+
+ /* Currently only Realtek are making PCI ne2k clones. */
+ if (pcibios_find_device (PCI_VENDOR_ID_REALTEK,
+ PCI_DEVICE_ID_REALTEK_8029, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break; /* OK, now try to probe for std. ISA card */
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Strip the I/O address out of the returned value */
+ pci_ioaddr &= PCI_BASE_ADDRESS_IO_MASK;
+ /* Avoid already found cards from previous ne_probe() calls */
+ if (check_region(pci_ioaddr, NE_IO_EXTENT))
+ continue;
+ printk("ne.c: PCI BIOS reports ne2000 clone at i/o %#x, irq %d.\n",
+ pci_ioaddr, pci_irq_line);
+ if (ne_probe1(dev, pci_ioaddr) != 0) { /* Shouldn't happen. */
+ printk(KERN_ERR "ne.c: Probe of PCI card at %#x failed.\n", pci_ioaddr);
+ break; /* Hrmm, try to probe for ISA card... */
+ }
+ pci_irq_line = 0;
+ return 0;
+ }
+ }
+#endif /* defined(CONFIG_PCI) */
+
+ /* Last resort. The semi-risky ISA auto-probe. */
+ for (i = 0; netcard_portlist[i]; i++) {
+ int ioaddr = netcard_portlist[i];
+ if (check_region(ioaddr, NE_IO_EXTENT))
+ continue;
+ if (ne_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+static int ne_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ unsigned char SA_prom[32];
+ int wordlength = 2;
+ const char *name = NULL;
+ int start_page, stop_page;
+ int neX000, ctron;
+ int reg0 = inb_p(ioaddr);
+ static unsigned version_printed = 0;
+
+ if (reg0 == 0xFF)
+ return ENODEV;
+
+ /* Do a preliminary verification that we have a 8390. */
+ { int regd;
+ outb_p(E8390_NODMA+E8390_PAGE1+E8390_STOP, ioaddr + E8390_CMD);
+ regd = inb_p(ioaddr + 0x0d);
+ outb_p(0xff, ioaddr + 0x0d);
+ outb_p(E8390_NODMA+E8390_PAGE0, ioaddr + E8390_CMD);
+ inb_p(ioaddr + EN0_COUNTER0); /* Clear the counter by reading. */
+ if (inb_p(ioaddr + EN0_COUNTER0) != 0) {
+ outb_p(reg0, ioaddr);
+ outb_p(regd, ioaddr + 0x0d); /* Restore the old values. */
+ return ENODEV;
+ }
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("NE*000 ethercard probe at %#3x:", ioaddr);
+
+ /* Reset card. Who knows what dain-bramaged state it was left in. */
+ { unsigned long reset_start_time = jiffies;
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(ioaddr + NE_RESET), ioaddr + NE_RESET);
+
+ while ((inb_p(ioaddr + EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2*HZ/100) {
+ printk(" not found (no reset ack).\n");
+ return ENODEV;
+ }
+
+ outb_p(0xff, ioaddr + EN0_ISR); /* Ack all intr. */
+ }
+
+ /* Read the 16 bytes of station address PROM.
+ We must first initialize registers, similar to NS8390_init(eifdev, 0).
+ We can't reliably read the SAPROM address without this.
+ (I learned the hard way!). */
+ {
+ struct {unsigned char value, offset; } program_seq[] = {
+ {E8390_NODMA+E8390_PAGE0+E8390_STOP, E8390_CMD}, /* Select page 0*/
+ {0x48, EN0_DCFG}, /* Set byte-wide (0x48) access. */
+ {0x00, EN0_RCNTLO}, /* Clear the count regs. */
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_IMR}, /* Mask completion irq. */
+ {0xFF, EN0_ISR},
+ {E8390_RXOFF, EN0_RXCR}, /* 0x20 Set to monitor */
+ {E8390_TXOFF, EN0_TXCR}, /* 0x02 and loopback mode. */
+ {32, EN0_RCNTLO},
+ {0x00, EN0_RCNTHI},
+ {0x00, EN0_RSARLO}, /* DMA starting at 0x0000. */
+ {0x00, EN0_RSARHI},
+ {E8390_RREAD+E8390_START, E8390_CMD},
+ };
+ for (i = 0; i < sizeof(program_seq)/sizeof(program_seq[0]); i++)
+ outb_p(program_seq[i].value, ioaddr + program_seq[i].offset);
+
+ }
+ for(i = 0; i < 32 /*sizeof(SA_prom)*/; i+=2) {
+ SA_prom[i] = inb(ioaddr + NE_DATAPORT);
+ SA_prom[i+1] = inb(ioaddr + NE_DATAPORT);
+ if (SA_prom[i] != SA_prom[i+1])
+ wordlength = 1;
+ }
+
+ if (wordlength == 2) {
+ /* We must set the 8390 for word mode. */
+ outb_p(0x49, ioaddr + EN0_DCFG);
+ /* We used to reset the ethercard here, but it doesn't seem
+ to be necessary. */
+ /* Un-double the SA_prom values. */
+ for (i = 0; i < 16; i++)
+ SA_prom[i] = SA_prom[i+i];
+ start_page = NESM_START_PG;
+ stop_page = NESM_STOP_PG;
+ } else {
+ start_page = NE1SM_START_PG;
+ stop_page = NE1SM_STOP_PG;
+ }
+
+ neX000 = (SA_prom[14] == 0x57 && SA_prom[15] == 0x57);
+ ctron = (SA_prom[0] == 0x00 && SA_prom[1] == 0x00 && SA_prom[2] == 0x1d);
+
+ /* Set up the rest of the parameters. */
+ if (neX000) {
+ name = (wordlength == 2) ? "NE2000" : "NE1000";
+ } else if (ctron) {
+ name = (wordlength == 2) ? "Ctron-8" : "Ctron-16";
+ start_page = 0x01;
+ stop_page = (wordlength == 2) ? 0x40 : 0x20;
+ } else {
+#ifdef SUPPORT_NE_BAD_CLONES
+ /* Ack! Well, there might be a *bad* NE*000 clone there.
+ Check for total bogus addresses. */
+ for (i = 0; bad_clone_list[i].name8; i++) {
+ if (SA_prom[0] == bad_clone_list[i].SAprefix[0] &&
+ SA_prom[1] == bad_clone_list[i].SAprefix[1] &&
+ SA_prom[2] == bad_clone_list[i].SAprefix[2]) {
+ if (wordlength == 2) {
+ name = bad_clone_list[i].name16;
+ } else {
+ name = bad_clone_list[i].name8;
+ }
+ break;
+ }
+ }
+ if (bad_clone_list[i].name8 == NULL) {
+ printk(" not found (invalid signature %2.2x %2.2x).\n",
+ SA_prom[14], SA_prom[15]);
+ return ENXIO;
+ }
+#else
+ printk(" not found.\n");
+ return ENXIO;
+#endif
+
+ }
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("ne.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (pci_irq_line) {
+ dev->irq = pci_irq_line;
+ }
+
+ if (dev->irq < 2) {
+ autoirq_setup(0);
+ outb_p(0x50, ioaddr + EN0_IMR); /* Enable one interrupt. */
+ outb_p(0x00, ioaddr + EN0_RCNTLO);
+ outb_p(0x00, ioaddr + EN0_RCNTHI);
+ outb_p(E8390_RREAD+E8390_START, ioaddr); /* Trigger it... */
+ outb_p(0x00, ioaddr + EN0_IMR); /* Mask it again. */
+ dev->irq = autoirq_report(0);
+ if (ei_debug > 2)
+ printk(" autoirq is %d\n", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ or don't know which one to set. */
+ dev->irq = 9;
+
+ if (! dev->irq) {
+ printk(" failed to detect IRQ line.\n");
+ return EAGAIN;
+ }
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ {
+ int irqval = request_irq(dev->irq, ei_interrupt, 0, name);
+ if (irqval) {
+ printk (" unable to get IRQ %d (irqval=%d).\n", dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+
+ dev->base_addr = ioaddr;
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ request_region(ioaddr, NE_IO_EXTENT, name);
+
+ for(i = 0; i < ETHER_ADDR_LEN; i++) {
+ printk(" %2.2x", SA_prom[i]);
+ dev->dev_addr[i] = SA_prom[i];
+ }
+
+ printk("\n%s: %s found at %#x, using IRQ %d.\n",
+ dev->name, name, ioaddr, dev->irq);
+
+ ei_status.name = name;
+ ei_status.tx_start_page = start_page;
+ ei_status.stop_page = stop_page;
+ ei_status.word16 = (wordlength == 2);
+
+ ei_status.rx_start_page = start_page + TX_PAGES;
+#ifdef PACKETBUF_MEMSIZE
+ /* Allow the packet buffer size to be overridden by know-it-alls. */
+ ei_status.stop_page = ei_status.tx_start_page + PACKETBUF_MEMSIZE;
+#endif
+
+ ei_status.reset_8390 = &ne_reset_8390;
+ ei_status.block_input = &ne_block_input;
+ ei_status.block_output = &ne_block_output;
+ ei_status.get_8390_hdr = &ne_get_8390_hdr;
+ dev->open = &ne_open;
+ dev->stop = &ne_close;
+ NS8390_init(dev, 0);
+ return 0;
+}
+
+static int
+ne_open(struct device *dev)
+{
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int
+ne_close(struct device *dev)
+{
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ ei_close(dev);
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+/* Hard reset the card. This used to pause for the same period that a
+ 8390 reset command required, but that shouldn't be necessary. */
+static void
+ne_reset_8390(struct device *dev)
+{
+ unsigned long reset_start_time = jiffies;
+
+ if (ei_debug > 1) printk("resetting the 8390 t=%ld...", jiffies);
+
+ /* DON'T change these to inb_p/outb_p or reset will fail on clones. */
+ outb(inb(NE_BASE + NE_RESET), NE_BASE + NE_RESET);
+
+ ei_status.txing = 0;
+ ei_status.dmaing = 0;
+
+ /* This check _should_not_ be necessary, omit eventually. */
+ while ((inb_p(NE_BASE+EN0_ISR) & ENISR_RESET) == 0)
+ if (jiffies - reset_start_time > 2*HZ/100) {
+ printk("%s: ne_reset_8390() did not complete.\n", dev->name);
+ break;
+ }
+ outb_p(ENISR_RESET, NE_BASE + EN0_ISR); /* Ack intr. */
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ne_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int nic_base = dev->base_addr;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne_get_8390_hdr "
+ "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock,
+ dev->interrupt);
+ return;
+ }
+
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb_p(sizeof(struct e8390_pkt_hdr), nic_base + EN0_RCNTLO);
+ outb_p(0, nic_base + EN0_RCNTHI);
+ outb_p(0, nic_base + EN0_RSARLO); /* On page boundary */
+ outb_p(ring_page, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+
+ if (ei_status.word16)
+ insw(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr)>>1);
+ else
+ insb(NE_BASE + NE_DATAPORT, hdr, sizeof(struct e8390_pkt_hdr));
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+/* Block input and output, similar to the Crynwr packet driver. If you
+ are porting to a new ethercard, look at the packet driver source for hints.
+ The NEx000 doesn't share the on-board packet memory -- you have to put
+ the packet out through the "remote DMA" dataport using outb. */
+
+static void
+ne_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+#ifdef NE_SANITY_CHECK
+ int xfer_count = count;
+#endif
+ int nic_base = dev->base_addr;
+ char *buf = skb->data;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne_block_input "
+ "[DMAstat:%d][irqlock:%d][intr:%d].\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock,
+ dev->interrupt);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ outb_p(E8390_NODMA+E8390_PAGE0+E8390_START, nic_base+ NE_CMD);
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(ring_offset & 0xff, nic_base + EN0_RSARLO);
+ outb_p(ring_offset >> 8, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ insw(NE_BASE + NE_DATAPORT,buf,count>>1);
+ if (count & 0x01) {
+ buf[count-1] = inb(NE_BASE + NE_DATAPORT);
+#ifdef NE_SANITY_CHECK
+ xfer_count++;
+#endif
+ }
+ } else {
+ insb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. If you see
+ this message you either 1) have a slightly incompatible clone
+ or 2) have noise/speed problems with your bus. */
+ if (ei_debug > 1) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ /* DON'T check for 'inb_p(EN0_ISR) & ENISR_RDC' here
+ -- it's broken for Rx on some cards! */
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if (((ring_offset + xfer_count) & 0xff) == low)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0)
+ printk("%s: RX transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, ring_offset + xfer_count, addr);
+ }
+#endif
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+}
+
+static void
+ne_block_output(struct device *dev, int count,
+ const unsigned char *buf, const int start_page)
+{
+ int nic_base = NE_BASE;
+ unsigned long dma_start;
+#ifdef NE_SANITY_CHECK
+ int retries = 0;
+#endif
+
+ /* Round the count up for word writes. Do we need to do this?
+ What effect will an odd byte count have on the 8390?
+ I should check someday. */
+ if (ei_status.word16 && (count & 0x01))
+ count++;
+
+ /* This *shouldn't* happen. If it does, it's the last thing you'll see */
+ if (ei_status.dmaing) {
+ printk("%s: DMAing conflict in ne_block_output."
+ "[DMAstat:%d][irqlock:%d][intr:%d]\n",
+ dev->name, ei_status.dmaing, ei_status.irqlock,
+ dev->interrupt);
+ return;
+ }
+ ei_status.dmaing |= 0x01;
+ /* We should already be in page 0, but to be safe... */
+ outb_p(E8390_PAGE0+E8390_START+E8390_NODMA, nic_base + NE_CMD);
+
+#ifdef NE_SANITY_CHECK
+ retry:
+#endif
+
+#ifdef NE8390_RW_BUGFIX
+ /* Handle the read-before-write bug the same way as the
+ Crynwr packet driver -- the NatSemi method doesn't work.
+ Actually this doesn't always work either, but if you have
+ problems with your NEx000 this is better than nothing! */
+ outb_p(0x42, nic_base + EN0_RCNTLO);
+ outb_p(0x00, nic_base + EN0_RCNTHI);
+ outb_p(0x42, nic_base + EN0_RSARLO);
+ outb_p(0x00, nic_base + EN0_RSARHI);
+ outb_p(E8390_RREAD+E8390_START, nic_base + NE_CMD);
+ /* Make certain that the dummy read has occurred. */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+#endif
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR);
+
+ /* Now the normal output. */
+ outb_p(count & 0xff, nic_base + EN0_RCNTLO);
+ outb_p(count >> 8, nic_base + EN0_RCNTHI);
+ outb_p(0x00, nic_base + EN0_RSARLO);
+ outb_p(start_page, nic_base + EN0_RSARHI);
+
+ outb_p(E8390_RWRITE+E8390_START, nic_base + NE_CMD);
+ if (ei_status.word16) {
+ outsw(NE_BASE + NE_DATAPORT, buf, count>>1);
+ } else {
+ outsb(NE_BASE + NE_DATAPORT, buf, count);
+ }
+
+ dma_start = jiffies;
+
+#ifdef NE_SANITY_CHECK
+ /* This was for the ALPHA version only, but enough people have
+ been encountering problems so it is still here. */
+ if (ei_debug > 1) { /* DMA termination address check... */
+ int addr, tries = 20;
+ do {
+ int high = inb_p(nic_base + EN0_RSARHI);
+ int low = inb_p(nic_base + EN0_RSARLO);
+ addr = (high << 8) + low;
+ if ((start_page << 8) + count == addr)
+ break;
+ } while (--tries > 0);
+ if (tries <= 0) {
+ printk("%s: Tx packet transfer address mismatch,"
+ "%#4.4x (expected) vs. %#4.4x (actual).\n",
+ dev->name, (start_page << 8) + count, addr);
+ if (retries++ == 0)
+ goto retry;
+ }
+ }
+#endif
+
+ while ((inb_p(nic_base + EN0_ISR) & ENISR_RDC) == 0)
+ if (jiffies - dma_start > 2*HZ/100) { /* 20ms */
+ printk("%s: timeout waiting for Tx RDC.\n", dev->name);
+ ne_reset_8390(dev);
+ NS8390_init(dev,1);
+ break;
+ }
+
+ outb_p(ENISR_RDC, nic_base + EN0_ISR); /* Ack intr. */
+ ei_status.dmaing &= ~0x01;
+ return;
+}
+
+
+#ifdef MODULE
+#define MAX_NE_CARDS 4 /* Max number of NE cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_NE_CARDS] = { 0, };
+static struct device dev_ne[MAX_NE_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_NE_CARDS] = { 0, };
+static int irq[MAX_NE_CARDS] = { 0, };
+
+/* This is set up so that no autoprobe takes place. We can't guarantee
+that the ne2k probe is the last 8390 based probe to take place (as it
+is at boot) and so the probe will get confused by any other 8390 cards.
+ISA device autoprobes on a running machine are not recommended anyway. */
+
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct device *dev = &dev_ne[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = ne_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only complain once */
+ printk(KERN_NOTICE "ne.c: Module autoprobing not allowed. Append \"io=0xNNN\" value(s).\n");
+ return -EPERM;
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "ne.c: No NE*000 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_NE_CARDS; this_dev++) {
+ struct device *dev = &dev_ne[this_dev];
+ if (dev->priv != NULL) {
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(dev->base_addr, NE_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -DKERNEL -Wall -O6 -fomit-frame-pointer -I/usr/src/linux/net/tcp -c ne.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/net_init.c b/i386/i386at/gpl/linux/net/net_init.c
new file mode 100644
index 00000000..cedee941
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/net_init.c
@@ -0,0 +1,380 @@
+/* netdrv_init.c: Initialization for network devices. */
+/*
+ Written 1993,1994,1995 by Donald Becker.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov or
+ C/O Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This file contains the initialization for the "pl14+" style ethernet
+ drivers. It should eventually replace most of drivers/net/Space.c.
+ It's primary advantage is that it's able to allocate low-memory buffers.
+ A secondary advantage is that the dangerous NE*000 netcards can reserve
+ their I/O port region before the SCSI probes start.
+
+ Modifications/additions by Bjorn Ekwall <bj0rn@blox.se>:
+ ethdev_index[MAX_ETH_CARDS]
+ register_netdev() / unregister_netdev()
+
+ Modifications by Wolfgang Walter
+ Use dev_close cleanly so we always shut things down tidily.
+
+ Changed 29/10/95, Alan Cox to pass sockaddr's around for mac addresses.
+*/
+
+#include <linux/config.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fs.h>
+#include <linux/malloc.h>
+#include <linux/if_ether.h>
+#include <linux/if_arp.h>
+#include <linux/string.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/trdevice.h>
+#ifdef CONFIG_NET_ALIAS
+#include <linux/net_alias.h>
+#endif
+
+/* The network devices currently exist only in the socket namespace, so these
+ entries are unused. The only ones that make sense are
+ open start the ethercard
+ close stop the ethercard
+ ioctl To get statistics, perhaps set the interface port (AUI, BNC, etc.)
+ One can also imagine getting raw packets using
+ read & write
+ but this is probably better handled by a raw packet socket.
+
+ Given that almost all of these functions are handled in the current
+ socket-based scheme, putting ethercard devices in /dev/ seems pointless.
+
+ [Removed all support for /dev network devices. When someone adds
+ streams then by magic we get them, but otherwise they are un-needed
+ and a space waste]
+*/
+
+/* The list of used and available "eth" slots (for "eth0", "eth1", etc.) */
+#define MAX_ETH_CARDS 16 /* same as the number if irq's in irq2dev[] */
+static struct device *ethdev_index[MAX_ETH_CARDS];
+
+/* Fill in the fields of the device structure with ethernet-generic values.
+
+ If no device structure is passed, a new one is constructed, complete with
+ a SIZEOF_PRIVATE private data area.
+
+ If an empty string area is passed as dev->name, or a new structure is made,
+ a new name string is constructed. The passed string area should be 8 bytes
+ long.
+ */
+
+struct device *
+init_etherdev(struct device *dev, int sizeof_priv)
+{
+ int new_device = 0;
+ int i;
+
+ /* Use an existing correctly named device in Space.c:dev_base. */
+ if (dev == NULL) {
+ int alloc_size = sizeof(struct device) + sizeof("eth%d ")
+ + sizeof_priv + 3;
+ struct device *cur_dev;
+ char pname[8]; /* Putative name for the device. */
+
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(pname, "eth%d", i);
+ for (cur_dev = dev_base; cur_dev; cur_dev = cur_dev->next)
+ if (strcmp(pname, cur_dev->name) == 0) {
+ dev = cur_dev;
+ dev->init = NULL;
+ sizeof_priv = (sizeof_priv + 3) & ~3;
+ dev->priv = sizeof_priv
+ ? kmalloc(sizeof_priv, GFP_KERNEL)
+ : NULL;
+ if (dev->priv) memset(dev->priv, 0, sizeof_priv);
+ goto found;
+ }
+ }
+
+ alloc_size &= ~3; /* Round to dword boundary. */
+
+ dev = (struct device *)kmalloc(alloc_size, GFP_KERNEL);
+ memset(dev, 0, alloc_size);
+ if (sizeof_priv)
+ dev->priv = (void *) (dev + 1);
+ dev->name = sizeof_priv + (char *)(dev + 1);
+ new_device = 1;
+ }
+
+ found: /* From the double loop above. */
+
+ if (dev->name &&
+ ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(dev->name, "eth%d", i);
+ ethdev_index[i] = dev;
+ break;
+ }
+ }
+
+ ether_setup(dev); /* Hmmm, should this be called here? */
+
+ if (new_device) {
+ /* Append the device to the device queue. */
+ struct device **old_devp = &dev_base;
+ while ((*old_devp)->next)
+ old_devp = & (*old_devp)->next;
+ (*old_devp)->next = dev;
+ dev->next = 0;
+ }
+ return dev;
+}
+
+
+static int eth_mac_addr(struct device *dev, void *p)
+{
+ struct sockaddr *addr=p;
+ if(dev->start)
+ return -EBUSY;
+ memcpy(dev->dev_addr, addr->sa_data,dev->addr_len);
+ return 0;
+}
+
+void ether_setup(struct device *dev)
+{
+ int i;
+
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ /* register boot-defined "eth" devices */
+ if (dev->name && (strncmp(dev->name, "eth", 3) == 0)) {
+ i = simple_strtoul(dev->name + 3, NULL, 0);
+ if (ethdev_index[i] == NULL) {
+ ethdev_index[i] = dev;
+ }
+ else if (dev != ethdev_index[i]) {
+ /* Really shouldn't happen! */
+#ifdef MACH
+ panic ("ether_setup: Ouch! Someone else took %s, i = %d\n",
+ dev->name, i);
+#else
+ printk("ether_setup: Ouch! Someone else took %s, i = %d\n",
+ dev->name, i);
+#endif
+ }
+ }
+
+#ifndef MACH
+ dev->hard_header = eth_header;
+ dev->rebuild_header = eth_rebuild_header;
+ dev->set_mac_address = eth_mac_addr;
+ dev->header_cache_bind = eth_header_cache_bind;
+ dev->header_cache_update= eth_header_cache_update;
+#endif
+
+ dev->type = ARPHRD_ETHER;
+ dev->hard_header_len = ETH_HLEN;
+ dev->mtu = 1500; /* eth_mtu */
+ dev->addr_len = ETH_ALEN;
+ dev->tx_queue_len = 100; /* Ethernet wants good queues */
+
+ memset(dev->broadcast,0xFF, ETH_ALEN);
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST|IFF_MULTICAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = 4;
+}
+
+#ifdef CONFIG_TR
+
+void tr_setup(struct device *dev)
+{
+ int i;
+ /* Fill in the fields of the device structure with ethernet-generic values.
+ This should be in a common file instead of per-driver. */
+ for (i = 0; i < DEV_NUMBUFFS; i++)
+ skb_queue_head_init(&dev->buffs[i]);
+
+ dev->hard_header = tr_header;
+ dev->rebuild_header = tr_rebuild_header;
+
+ dev->type = ARPHRD_IEEE802;
+ dev->hard_header_len = TR_HLEN;
+ dev->mtu = 2000; /* bug in fragmenter...*/
+ dev->addr_len = TR_ALEN;
+ dev->tx_queue_len = 100; /* Long queues on tr */
+
+ memset(dev->broadcast,0xFF, TR_ALEN);
+
+ /* New-style flags. */
+ dev->flags = IFF_BROADCAST;
+ dev->family = AF_INET;
+ dev->pa_addr = 0;
+ dev->pa_brdaddr = 0;
+ dev->pa_mask = 0;
+ dev->pa_alen = 4;
+}
+
+#endif
+
+int ether_config(struct device *dev, struct ifmap *map)
+{
+ if (map->mem_start != (u_long)(-1))
+ dev->mem_start = map->mem_start;
+ if (map->mem_end != (u_long)(-1))
+ dev->mem_end = map->mem_end;
+ if (map->base_addr != (u_short)(-1))
+ dev->base_addr = map->base_addr;
+ if (map->irq != (u_char)(-1))
+ dev->irq = map->irq;
+ if (map->dma != (u_char)(-1))
+ dev->dma = map->dma;
+ if (map->port != (u_char)(-1))
+ dev->if_port = map->port;
+ return 0;
+}
+
+int register_netdev(struct device *dev)
+{
+ struct device *d = dev_base;
+ unsigned long flags;
+ int i=MAX_ETH_CARDS;
+
+ save_flags(flags);
+ cli();
+
+ if (dev && dev->init) {
+ if (dev->name &&
+ ((dev->name[0] == '\0') || (dev->name[0] == ' '))) {
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ if (ethdev_index[i] == NULL) {
+ sprintf(dev->name, "eth%d", i);
+ printk("loading device '%s'...\n", dev->name);
+ ethdev_index[i] = dev;
+ break;
+ }
+ }
+
+ sti(); /* device probes assume interrupts enabled */
+ if (dev->init(dev) != 0) {
+ if (i < MAX_ETH_CARDS) ethdev_index[i] = NULL;
+ restore_flags(flags);
+ return -EIO;
+ }
+ cli();
+
+ /* Add device to end of chain */
+ if (dev_base) {
+ while (d->next)
+ d = d->next;
+ d->next = dev;
+ }
+ else
+ dev_base = dev;
+ dev->next = NULL;
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+void unregister_netdev(struct device *dev)
+{
+ struct device *d = dev_base;
+ unsigned long flags;
+ int i;
+
+ save_flags(flags);
+ cli();
+
+ if (dev == NULL)
+ {
+ printk("was NULL\n");
+ restore_flags(flags);
+ return;
+ }
+ /* else */
+ if (dev->start)
+ printk("ERROR '%s' busy and not MOD_IN_USE.\n", dev->name);
+
+ /*
+ * must jump over main_device+aliases
+ * avoid alias devices unregistration so that only
+ * net_alias module manages them
+ */
+#ifdef CONFIG_NET_ALIAS
+ if (dev_base == dev)
+ dev_base = net_alias_nextdev(dev);
+ else
+ {
+ while(d && (net_alias_nextdev(d) != dev)) /* skip aliases */
+ d = net_alias_nextdev(d);
+
+ if (d && (net_alias_nextdev(d) == dev))
+ {
+ /*
+ * Critical: Bypass by consider devices as blocks (maindev+aliases)
+ */
+ net_alias_nextdev_set(d, net_alias_nextdev(dev));
+ }
+#else
+ if (dev_base == dev)
+ dev_base = dev->next;
+ else
+ {
+ while (d && (d->next != dev))
+ d = d->next;
+
+ if (d && (d->next == dev))
+ {
+ d->next = dev->next;
+ }
+#endif
+ else
+ {
+ printk("unregister_netdev: '%s' not found\n", dev->name);
+ restore_flags(flags);
+ return;
+ }
+ }
+ for (i = 0; i < MAX_ETH_CARDS; ++i)
+ {
+ if (ethdev_index[i] == dev)
+ {
+ ethdev_index[i] = NULL;
+ break;
+ }
+ }
+
+ restore_flags(flags);
+
+ /*
+ * You can i.e use a interfaces in a route though it is not up.
+ * We call close_dev (which is changed: it will down a device even if
+ * dev->flags==0 (but it will not call dev->stop if IFF_UP
+ * is not set).
+ * This will call notifier_call_chain(&netdev_chain, NETDEV_DOWN, dev),
+ * dev_mc_discard(dev), ....
+ */
+
+ dev_close(dev);
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c net_init.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/ni52.c b/i386/i386at/gpl/linux/net/ni52.c
new file mode 100644
index 00000000..13d12359
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni52.c
@@ -0,0 +1,1110 @@
+/*
+ * net-3-driver for the NI5210 card (i82586 Ethernet chip)
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * Alphacode 0.62 (95/01/19) for Linux 1.1.82 (or later)
+ * Copyrights (c) 1994,1995 by M.Hipp (Michael.Hipp@student.uni-tuebingen.de)
+ * [feel free to mail ....]
+ *
+ * CAN YOU PLEASE REPORT ME YOUR PERFORMANCE EXPERIENCES !!.
+ *
+ * If you find a bug, please report me:
+ * The kernel panic output and any kmsg from the ni52 driver
+ * the ni5210-driver-version and the linux-kernel version
+ * how many shared memory (memsize) on the netcard,
+ * bootprom: yes/no, base_addr, mem_start
+ * maybe the ni5210-card revision and the i82586 version
+ *
+ * autoprobe for: base_addr: 0x300,0x280,0x360,0x320,0x340
+ * mem_start: 0xc8000,0xd0000,0xd4000,0xd8000 (8K and 16K)
+ *
+ * sources:
+ * skeleton.c from Donald Becker
+ *
+ * I have also done a look in the following sources: (mail me if you need them)
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's (fourth) i82586-driver for BSD
+ * (before getting an i82596 (yes 596 not 586) manual, the existing drivers helped
+ * me a lot to understand this tricky chip.)
+ *
+ * Known Problems:
+ * The internal sysbus seems to be slow. So we often lose packets because of
+ * overruns while receiving from a fast remote host.
+ * This can slow down TCP connections. Maybe the newer ni5210 cards are better.
+ *
+ * IMPORTANT NOTE:
+ * On fast networks, it's a (very) good idea to have 16K shared memory. With
+ * 8K, we can store only 4 receive frames, so it can (easily) happen that a remote
+ * machine 'overruns' our system.
+ *
+ * Known i82586 bugs (I'm sure, there are many more!):
+ * Running the NOP-mode, the i82586 sometimes seems to forget to report
+ * every xmit-interrupt until we restart the CU.
+ * Another MAJOR bug is, that the RU sometimes seems to ignore the EL-Bit
+ * in the RBD-Struct which indicates an end of the RBD queue.
+ * Instead, the RU fetches another (randomly selected and
+ * usually used) RBD and begins to fill it. (Maybe, this happens only if
+ * the last buffer from the previous RFD fits exact into the queue and
+ * the next RFD can't fetch an initial RBD. Anyone knows more? )
+ */
+
+/*
+ * 18.Nov.95: Mcast changes (AC).
+ *
+ * 19.Jan.95: verified (MH)
+ *
+ * 19.Sep.94: Added Multicast support (not tested yet) (MH)
+ *
+ * 18.Sep.94: Workaround for 'EL-Bug'. Removed flexible RBD-handling.
+ * Now, every RFD has exact one RBD. (MH)
+ *
+ * 14.Sep.94: added promiscuous mode, a few cleanups (MH)
+ *
+ * 19.Aug.94: changed request_irq() parameter (MH)
+ *
+ * 20.July.94: removed cleanup bugs, removed a 16K-mem-probe-bug (MH)
+ *
+ * 19.July.94: lotsa cleanups .. (MH)
+ *
+ * 17.July.94: some patches ... verified to run with 1.1.29 (MH)
+ *
+ * 4.July.94: patches for Linux 1.1.24 (MH)
+ *
+ * 26.March.94: patches for Linux 1.0 and iomem-auto-probe (MH)
+ *
+ * 30.Sep.93: Added nop-chain .. driver now runs with only one Xmit-Buff, too (MH)
+ *
+ * < 30.Sep.93: first versions
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/delay.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ni52.h"
+
+#define DEBUG /* debug on */
+#define SYSBUSVAL 1 /* 8 Bit */
+
+#define ni_attn586() {outb(0,dev->base_addr+NI52_ATTENTION);}
+#define ni_reset586() {outb(0,dev->base_addr+NI52_RESET);}
+
+#define make32(ptr16) (p->memtop + (short) (ptr16) )
+#define make24(ptr32) ((char *) (ptr32) - p->base)
+#define make16(ptr32) ((unsigned short) ((unsigned long) (ptr32) - (unsigned long) p->memtop ))
+
+/******************* how to calculate the buffers *****************************
+
+ * IMPORTANT NOTE: if you configure only one NUM_XMIT_BUFFS, the driver works
+ * --------------- in a different (more stable?) mode. Only in this mode it's
+ * possible to configure the driver with 'NO_NOPCOMMANDS'
+
+sizeof(scp)=12; sizeof(scb)=16; sizeof(iscp)=8;
+sizeof(scp)+sizeof(iscp)+sizeof(scb) = 36 = INIT
+sizeof(rfd) = 24; sizeof(rbd) = 12;
+sizeof(tbd) = 8; sizeof(transmit_cmd) = 16;
+sizeof(nop_cmd) = 8;
+
+ * if you don't know the driver, better do not change this values: */
+
+#define RECV_BUFF_SIZE 1524 /* slightly oversized */
+#define XMIT_BUFF_SIZE 1524 /* slightly oversized */
+#define NUM_XMIT_BUFFS 1 /* config for both, 8K and 16K shmem */
+#define NUM_RECV_BUFFS_8 4 /* config for 8K shared mem */
+#define NUM_RECV_BUFFS_16 9 /* config for 16K shared mem */
+#define NO_NOPCOMMANDS /* only possible with NUM_XMIT_BUFFS=1 */
+
+/**************************************************************************/
+
+#define DELAY(x) {int i=jiffies; \
+ if(loops_per_sec == 1) \
+ while(i+(x)>jiffies); \
+ else \
+ __delay((loops_per_sec>>5)*x); \
+ }
+
+/* a much shorter delay: */
+#define DELAY_16(); { __delay( (loops_per_sec>>16)+1 ); }
+
+/* wait for command with timeout: */
+#define WAIT_4_SCB_CMD() { int i; \
+ for(i=0;i<1024;i++) { \
+ if(!p->scb->cmd) break; \
+ DELAY_16(); \
+ if(i == 1023) { \
+ printk("%s: scb_cmd timed out .. resetting i82586\n",dev->name); \
+ ni_reset586(); } } }
+
+
+#define NI52_TOTAL_SIZE 16
+#define NI52_ADDR0 0x02
+#define NI52_ADDR1 0x07
+#define NI52_ADDR2 0x01
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+#endif
+
+static int ni52_probe1(struct device *dev,int ioaddr);
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr);
+static int ni52_open(struct device *dev);
+static int ni52_close(struct device *dev);
+static int ni52_send_packet(struct sk_buff *,struct device *);
+static struct enet_statistics *ni52_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/* helper-functions */
+static int init586(struct device *dev);
+static int check586(struct device *dev,char *where,unsigned size);
+static void alloc586(struct device *dev);
+static void startrecv586(struct device *dev);
+static void *alloc_rfa(struct device *dev,void *ptr);
+static void ni52_rcv_int(struct device *dev);
+static void ni52_xmt_int(struct device *dev);
+static void ni52_rnr_int(struct device *dev);
+
+struct priv
+{
+ struct enet_statistics stats;
+ unsigned long base;
+ char *memtop;
+ volatile struct rfd_struct *rfd_last,*rfd_top,*rfd_first;
+ volatile struct scp_struct *scp; /* volatile is important */
+ volatile struct iscp_struct *iscp; /* volatile is important */
+ volatile struct scb_struct *scb; /* volatile is important */
+ volatile struct tbd_struct *xmit_buffs[NUM_XMIT_BUFFS];
+ volatile struct transmit_cmd_struct *xmit_cmds[NUM_XMIT_BUFFS];
+#if (NUM_XMIT_BUFFS == 1)
+ volatile struct nop_cmd_struct *nop_cmds[2];
+#else
+ volatile struct nop_cmd_struct *nop_cmds[NUM_XMIT_BUFFS];
+#endif
+ volatile int nop_point,num_recv_buffs;
+ volatile char *xmit_cbuffs[NUM_XMIT_BUFFS];
+ volatile int xmit_count,xmit_last;
+};
+
+
+/**********************************************
+ * close device
+ */
+
+static int ni52_close(struct device *dev)
+{
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ ni_reset586(); /* the hard way to stop the receiver */
+
+ dev->start = 0;
+ dev->tbusy = 0;
+
+ return 0;
+}
+
+/**********************************************
+ * open device
+ */
+
+static int ni52_open(struct device *dev)
+{
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+
+ if(request_irq(dev->irq, &ni52_interrupt,0,"ni52"))
+ {
+ ni_reset586();
+ return -EAGAIN;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 1;
+
+ return 0; /* most done by init */
+}
+
+/**********************************************
+ * Check to see if there's an 82586 out there.
+ */
+
+static int check586(struct device *dev,char *where,unsigned size)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ char *iscp_addrs[2];
+ int i;
+
+ p->base = (unsigned long) where + size - 0x01000000;
+ p->memtop = where + size;
+ p->scp = (struct scp_struct *)(p->base + SCP_DEFAULT_ADDRESS);
+ memset((char *)p->scp,0, sizeof(struct scp_struct));
+ p->scp->sysbus = SYSBUSVAL; /* 1 = 8Bit-Bus, 0 = 16 Bit */
+
+ iscp_addrs[0] = where;
+ iscp_addrs[1]= (char *) p->scp - sizeof(struct iscp_struct);
+
+ for(i=0;i<2;i++)
+ {
+ p->iscp = (struct iscp_struct *) iscp_addrs[i];
+ memset((char *)p->iscp,0, sizeof(struct iscp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->iscp->busy = 1;
+
+ ni_reset586();
+ ni_attn586();
+ DELAY(2); /* wait a while... */
+
+ if(p->iscp->busy) /* i82586 clears 'busy' after successful init */
+ return 0;
+ }
+ return 1;
+}
+
+/******************************************************************
+ * set iscp at the right place, called by ni52_probe1 and open586.
+ */
+
+void alloc586(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+
+ ni_reset586();
+ DELAY(2);
+
+ p->scp = (struct scp_struct *) (p->base + SCP_DEFAULT_ADDRESS);
+ p->scb = (struct scb_struct *) (dev->mem_start);
+ p->iscp = (struct iscp_struct *) ((char *)p->scp - sizeof(struct iscp_struct));
+
+ memset((char *) p->iscp,0,sizeof(struct iscp_struct));
+ memset((char *) p->scp ,0,sizeof(struct scp_struct));
+
+ p->scp->iscp = make24(p->iscp);
+ p->scp->sysbus = SYSBUSVAL;
+ p->iscp->scb_offset = make16(p->scb);
+
+ p->iscp->busy = 1;
+ ni_reset586();
+ ni_attn586();
+
+ DELAY(2);
+
+ if(p->iscp->busy)
+ printk("%s: Init-Problems (alloc).\n",dev->name);
+
+ memset((char *)p->scb,0,sizeof(struct scb_struct));
+}
+
+/**********************************************
+ * probe the ni5210-card
+ */
+
+int ni52_probe(struct device *dev)
+{
+ int *port, ports[] = {0x300, 0x280, 0x360 , 0x320 , 0x340, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ if( (inb(base_addr+NI52_MAGIC1) == NI52_MAGICVAL1) &&
+ (inb(base_addr+NI52_MAGIC2) == NI52_MAGICVAL2))
+ return ni52_probe1(dev, base_addr);
+ else if (base_addr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = ports; *port; port++) {
+ int ioaddr = *port;
+ if (check_region(ioaddr, NI52_TOTAL_SIZE))
+ continue;
+ if( !(inb(ioaddr+NI52_MAGIC1) == NI52_MAGICVAL1) ||
+ !(inb(ioaddr+NI52_MAGIC2) == NI52_MAGICVAL2))
+ continue;
+
+ dev->base_addr = ioaddr;
+ if (ni52_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ dev->base_addr = base_addr;
+ return ENODEV;
+}
+
+static int ni52_probe1(struct device *dev,int ioaddr)
+{
+ long memaddrs[] = { 0xd0000,0xd2000,0xc8000,0xca000,0xd4000,0xd6000,0xd8000, 0 };
+ int i,size;
+
+ for(i=0;i<ETH_ALEN;i++)
+ dev->dev_addr[i] = inb(dev->base_addr+i);
+
+ if(dev->dev_addr[0] != NI52_ADDR0 || dev->dev_addr[1] != NI52_ADDR1
+ || dev->dev_addr[2] != NI52_ADDR2)
+ return ENODEV;
+
+ printk("%s: Ni52 found at %#3lx, ",dev->name,dev->base_addr);
+
+ request_region(ioaddr,NI52_TOTAL_SIZE,"ni52");
+
+ dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
+ /* warning: we don't free it on errors */
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset((char *) dev->priv,0,sizeof(struct priv));
+
+ /*
+ * check (or search) IO-Memory, 8K and 16K
+ */
+ if(dev->mem_start != 0) /* no auto-mem-probe */
+ {
+ size = 0x4000; /* check for 16K mem */
+ if(!check586(dev,(char *) dev->mem_start,size)) {
+ size = 0x2000; /* check for 8K mem */
+ if(!check586(dev,(char *) dev->mem_start,size)) {
+ printk("?memprobe, Can't find memory at 0x%lx!\n",dev->mem_start);
+ return ENODEV;
+ }
+ }
+ }
+ else
+ {
+ for(i=0;;i++)
+ {
+ if(!memaddrs[i]) {
+ printk("?memprobe, Can't find io-memory!\n");
+ return ENODEV;
+ }
+ dev->mem_start = memaddrs[i];
+ size = 0x2000; /* check for 8K mem */
+ if(check586(dev,(char *)dev->mem_start,size)) /* 8K-check */
+ break;
+ size = 0x4000; /* check for 16K mem */
+ if(check586(dev,(char *)dev->mem_start,size)) /* 16K-check */
+ break;
+ }
+ }
+ dev->mem_end = dev->mem_start + size; /* set mem_end showed by 'ifconfig' */
+
+ ((struct priv *) (dev->priv))->base = dev->mem_start + size - 0x01000000;
+ alloc586(dev);
+
+ /* set number of receive-buffs according to memsize */
+ if(size == 0x2000)
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_8;
+ else
+ ((struct priv *) dev->priv)->num_recv_buffs = NUM_RECV_BUFFS_16;
+
+ printk("Memaddr: 0x%lx, Memsize: %d, ",dev->mem_start,size);
+
+ if(dev->irq < 2)
+ {
+ autoirq_setup(0);
+ ni_reset586();
+ ni_attn586();
+ if(!(dev->irq = autoirq_report(2)))
+ {
+ printk("?autoirq, Failed to detect IRQ line!\n");
+ return 1;
+ }
+ }
+ else if(dev->irq == 2)
+ dev->irq = 9;
+
+ printk("IRQ %d.\n",dev->irq);
+
+ dev->open = &ni52_open;
+ dev->stop = &ni52_close;
+ dev->get_stats = &ni52_get_stats;
+ dev->hard_start_xmit = &ni52_send_packet;
+ dev->set_multicast_list = &set_multicast_list;
+
+ dev->if_port = 0;
+
+ ether_setup(dev);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 0;
+
+ return 0;
+}
+
+/**********************************************
+ * init the chip (ni52-interrupt should be disabled?!)
+ * needs a correct 'allocated' memory
+ */
+
+static int init586(struct device *dev)
+{
+ void *ptr;
+ unsigned long s;
+ int i,result=0;
+ struct priv *p = (struct priv *) dev->priv;
+ volatile struct configure_cmd_struct *cfg_cmd;
+ volatile struct iasetup_cmd_struct *ias_cmd;
+ volatile struct tdr_cmd_struct *tdr_cmd;
+ volatile struct mcsetup_cmd_struct *mc_cmd;
+ struct dev_mc_list *dmi=dev->mc_list;
+ int num_addrs=dev->mc_count;
+
+ ptr = (void *) ((char *)p->scb + sizeof(struct scb_struct));
+
+ cfg_cmd = (struct configure_cmd_struct *)ptr; /* configure-command */
+ cfg_cmd->cmd_status = 0;
+ cfg_cmd->cmd_cmd = CMD_CONFIGURE | CMD_LAST;
+ cfg_cmd->cmd_link = 0xffff;
+
+ cfg_cmd->byte_cnt = 0x0a; /* number of cfg bytes */
+ cfg_cmd->fifo = 0x08; /* fifo-limit (8=tx:32/rx:64) */
+ cfg_cmd->sav_bf = 0x40; /* hold or discard bad recv frames (bit 7) */
+ cfg_cmd->adr_len = 0x2e; /* addr_len |!src_insert |pre-len |loopback */
+ cfg_cmd->priority = 0x00;
+ cfg_cmd->ifs = 0x60;
+ cfg_cmd->time_low = 0x00;
+ cfg_cmd->time_high = 0xf2;
+ cfg_cmd->promisc = 0;
+ if(dev->flags&(IFF_ALLMULTI|IFF_PROMISC))
+ {
+ cfg_cmd->promisc=1;
+ dev->flags|=IFF_PROMISC;
+ }
+ cfg_cmd->carr_coll = 0x00;
+
+ p->scb->cbl_offset = make16(cfg_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ ni_attn586();
+
+ s = jiffies; /* warning: only active with interrupts on !! */
+ while(!(cfg_cmd->cmd_status & STAT_COMPL))
+ if(jiffies-s > 30) break;
+
+ if((cfg_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_COMPL|STAT_OK))
+ {
+ printk("%s (ni52): configure command failed: %x\n",dev->name,cfg_cmd->cmd_status);
+ return 1;
+ }
+
+ /*
+ * individual address setup
+ */
+ ias_cmd = (struct iasetup_cmd_struct *)ptr;
+
+ ias_cmd->cmd_status = 0;
+ ias_cmd->cmd_cmd = CMD_IASETUP | CMD_LAST;
+ ias_cmd->cmd_link = 0xffff;
+
+ memcpy((char *)&ias_cmd->iaddr,(char *) dev->dev_addr,ETH_ALEN);
+
+ p->scb->cbl_offset = make16(ias_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ ni_attn586();
+
+ s = jiffies;
+ while(!(ias_cmd->cmd_status & STAT_COMPL))
+ if(jiffies-s > 30) break;
+
+ if((ias_cmd->cmd_status & (STAT_OK|STAT_COMPL)) != (STAT_OK|STAT_COMPL)) {
+ printk("%s (ni52): individual address setup command failed: %04x\n",dev->name,ias_cmd->cmd_status);
+ return 1;
+ }
+
+ /*
+ * TDR, wire check .. e.g. no resistor e.t.c
+ */
+ tdr_cmd = (struct tdr_cmd_struct *)ptr;
+
+ tdr_cmd->cmd_status = 0;
+ tdr_cmd->cmd_cmd = CMD_TDR | CMD_LAST;
+ tdr_cmd->cmd_link = 0xffff;
+ tdr_cmd->status = 0;
+
+ p->scb->cbl_offset = make16(tdr_cmd);
+
+ p->scb->cmd = CUC_START; /* cmd.-unit start */
+ ni_attn586();
+
+ s = jiffies;
+ while(!(tdr_cmd->cmd_status & STAT_COMPL))
+ if(jiffies - s > 30) {
+ printk("%s: Problems while running the TDR.\n",dev->name);
+ result = 1;
+ }
+
+ if(!result)
+ {
+ DELAY(2); /* wait for result */
+ result = tdr_cmd->status;
+
+ p->scb->cmd = p->scb->status & STAT_MASK;
+ ni_attn586(); /* ack the interrupts */
+
+ if(result & TDR_LNK_OK) ;
+ else if(result & TDR_XCVR_PRB)
+ printk("%s: TDR: Transceiver problem!\n",dev->name);
+ else if(result & TDR_ET_OPN)
+ printk("%s: TDR: No correct termination %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+ else if(result & TDR_ET_SRT)
+ {
+ if (result & TDR_TIMEMASK) /* time == 0 -> strange :-) */
+ printk("%s: TDR: Detected a short circuit %d clocks away.\n",dev->name,result & TDR_TIMEMASK);
+ }
+ else
+ printk("%s: TDR: Unknown status %04x\n",dev->name,result);
+ }
+
+ /*
+ * ack interrupts
+ */
+ p->scb->cmd = p->scb->status & STAT_MASK;
+ ni_attn586();
+
+ /*
+ * alloc nop/xmit-cmds
+ */
+#if (NUM_XMIT_BUFFS == 1)
+ for(i=0;i<2;i++)
+ {
+ p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ }
+ p->xmit_cmds[0] = (struct transmit_cmd_struct *)ptr; /* transmit cmd/buff 0 */
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+#else
+ for(i=0;i<NUM_XMIT_BUFFS;i++)
+ {
+ p->nop_cmds[i] = (struct nop_cmd_struct *)ptr;
+ p->nop_cmds[i]->cmd_cmd = CMD_NOP;
+ p->nop_cmds[i]->cmd_status = 0;
+ p->nop_cmds[i]->cmd_link = make16((p->nop_cmds[i]));
+ ptr = (char *) ptr + sizeof(struct nop_cmd_struct);
+ p->xmit_cmds[i] = (struct transmit_cmd_struct *)ptr; /*transmit cmd/buff 0*/
+ ptr = (char *) ptr + sizeof(struct transmit_cmd_struct);
+ }
+#endif
+
+ ptr = alloc_rfa(dev,(void *)ptr); /* init receive-frame-area */
+
+ /*
+ * Multicast setup
+ */
+
+ if(dev->mc_count)
+ { /* I don't understand this: do we really need memory after the init? */
+ int len = ((char *) p->iscp - (char *) ptr - 8) / 6;
+ if(len <= 0)
+ {
+ printk("%s: Ooooops, no memory for MC-Setup!\n",dev->name);
+ }
+ else
+ {
+ if(len < num_addrs)
+ {
+ /* BUG - should go ALLMULTI in this case */
+ num_addrs = len;
+ printk("%s: Sorry, can only apply %d MC-Address(es).\n",dev->name,num_addrs);
+ }
+ mc_cmd = (struct mcsetup_cmd_struct *) ptr;
+ mc_cmd->cmd_status = 0;
+ mc_cmd->cmd_cmd = CMD_MCSETUP | CMD_LAST;
+ mc_cmd->cmd_link = 0xffff;
+ mc_cmd->mc_cnt = num_addrs * 6;
+ for(i=0;i<num_addrs;i++)
+ {
+ memcpy((char *) mc_cmd->mc_list[i], dmi->dmi_addr,6);
+ dmi=dmi->next;
+ }
+ p->scb->cbl_offset = make16(mc_cmd);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ s = jiffies;
+ while(!(mc_cmd->cmd_status & STAT_COMPL))
+ if(jiffies - s > 30)
+ break;
+ if(!(mc_cmd->cmd_status & STAT_COMPL))
+ printk("%s: Can't apply multicast-address-list.\n",dev->name);
+ }
+ }
+
+ /*
+ * alloc xmit-buffs / init xmit_cmds
+ */
+ for(i=0;i<NUM_XMIT_BUFFS;i++)
+ {
+ p->xmit_cbuffs[i] = (char *)ptr; /* char-buffs */
+ ptr = (char *) ptr + XMIT_BUFF_SIZE;
+ p->xmit_buffs[i] = (struct tbd_struct *)ptr; /* TBD */
+ ptr = (char *) ptr + sizeof(struct tbd_struct);
+ if((void *)ptr > (void *)p->iscp)
+ {
+ printk("%s: not enough shared-mem for your configuration!\n",dev->name);
+ return 1;
+ }
+ memset((char *)(p->xmit_cmds[i]) ,0, sizeof(struct transmit_cmd_struct));
+ memset((char *)(p->xmit_buffs[i]),0, sizeof(struct tbd_struct));
+ p->xmit_cmds[i]->cmd_status = STAT_COMPL;
+ p->xmit_cmds[i]->cmd_cmd = CMD_XMIT | CMD_INT;
+ p->xmit_cmds[i]->tbd_offset = make16((p->xmit_buffs[i]));
+ p->xmit_buffs[i]->next = 0xffff;
+ p->xmit_buffs[i]->buffer = make24((p->xmit_cbuffs[i]));
+ }
+
+ p->xmit_count = 0;
+ p->xmit_last = 0;
+#ifndef NO_NOPCOMMANDS
+ p->nop_point = 0;
+#endif
+
+ /*
+ * 'start transmitter' (nop-loop)
+ */
+#ifndef NO_NOPCOMMANDS
+ p->scb->cbl_offset = make16(p->nop_cmds[0]);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+#else
+ p->xmit_cmds[0]->cmd_link = 0xffff;
+ p->xmit_cmds[0]->cmd_cmd = CMD_XMIT | CMD_LAST | CMD_INT;
+#endif
+
+ return 0;
+}
+
+/******************************************************
+ * This is a helper routine for ni52_rnr_int() and init586().
+ * It sets up the Receive Frame Area (RFA).
+ */
+
+static void *alloc_rfa(struct device *dev,void *ptr)
+{
+ volatile struct rfd_struct *rfd = (struct rfd_struct *)ptr;
+ volatile struct rbd_struct *rbd;
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+
+ memset((char *) rfd,0,sizeof(struct rfd_struct)*p->num_recv_buffs);
+ p->rfd_first = rfd;
+
+ for(i = 0; i < p->num_recv_buffs; i++)
+ rfd[i].next = make16(rfd + (i+1) % p->num_recv_buffs);
+ rfd[p->num_recv_buffs-1].last = RFD_SUSP; /* RU suspend */
+
+ ptr = (void *) (rfd + p->num_recv_buffs);
+
+ rbd = (struct rbd_struct *) ptr;
+ ptr = (void *) (rbd + p->num_recv_buffs);
+
+ /* clr descriptors */
+ memset((char *) rbd,0,sizeof(struct rbd_struct)*p->num_recv_buffs);
+
+ for(i=0;i<p->num_recv_buffs;i++)
+ {
+ rbd[i].next = make16((rbd + (i+1) % p->num_recv_buffs));
+ rbd[i].size = RECV_BUFF_SIZE;
+ rbd[i].buffer = make24(ptr);
+ ptr = (char *) ptr + RECV_BUFF_SIZE;
+ }
+
+ p->rfd_top = p->rfd_first;
+ p->rfd_last = p->rfd_first + p->num_recv_buffs - 1;
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->rfd_first->rbd_offset = make16(rbd);
+
+ return ptr;
+}
+
+
+/**************************************************
+ * Interrupt Handler ...
+ */
+
+static void ni52_interrupt(int irq,struct pt_regs *reg_ptr)
+{
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ unsigned short stat;
+ struct priv *p;
+
+ if (dev == NULL) {
+ printk ("ni52-interrupt: irq %d for unknown device.\n",(int) -(((struct pt_regs *)reg_ptr)->orig_eax+2));
+ return;
+ }
+ p = (struct priv *) dev->priv;
+
+ dev->interrupt = 1;
+
+ while((stat=p->scb->status & STAT_MASK))
+ {
+ p->scb->cmd = stat;
+ ni_attn586(); /* ack inter. */
+
+ if(stat & STAT_CX) /* command with I-bit set complete */
+ ni52_xmt_int(dev);
+
+ if(stat & STAT_FR) /* received a frame */
+ ni52_rcv_int(dev);
+
+#ifndef NO_NOPCOMMANDS
+ if(stat & STAT_CNA) /* CU went 'not ready' */
+ {
+ if(dev->start)
+ printk("%s: oops! CU has left active state. stat: %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+ }
+#endif
+
+ if(stat & STAT_RNR) /* RU went 'not ready' */
+ {
+ if(p->scb->status & RU_SUSPEND) /* special case: RU_SUSPEND */
+ {
+ WAIT_4_SCB_CMD();
+ p->scb->cmd = RUC_RESUME;
+ ni_attn586();
+ }
+ else
+ {
+ printk("%s: Receiver-Unit went 'NOT READY': %04x/%04x.\n",dev->name,(int) stat,(int) p->scb->status);
+ ni52_rnr_int(dev);
+ }
+ }
+ WAIT_4_SCB_CMD(); /* wait for ack. (ni52_xmt_int can be faster than ack!!) */
+ if(p->scb->cmd) /* timed out? */
+ break;
+ }
+
+ dev->interrupt = 0;
+}
+
+/*******************************************************
+ * receive-interrupt
+ */
+
+static void ni52_rcv_int(struct device *dev)
+{
+ int status;
+ unsigned short totlen;
+ struct sk_buff *skb;
+ struct rbd_struct *rbd;
+ struct priv *p = (struct priv *) dev->priv;
+
+ for(;(status = p->rfd_top->status) & STAT_COMPL;)
+ {
+ rbd = (struct rbd_struct *) make32(p->rfd_top->rbd_offset);
+
+ if(status & STAT_OK) /* frame received without error? */
+ {
+ if( (totlen = rbd->status) & RBD_LAST) /* the first and the last buffer? */
+ {
+ totlen &= RBD_MASK; /* length of this frame */
+ rbd->status = 0;
+ skb = (struct sk_buff *) dev_alloc_skb(totlen+2);
+ if(skb != NULL)
+ {
+ skb->dev = dev;
+ skb_reserve(skb,2); /* 16 byte alignment */
+ memcpy(skb_put(skb,totlen),(char *) p->base+(unsigned long) rbd->buffer, totlen);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ p->stats.rx_packets++;
+ }
+ else
+ p->stats.rx_dropped++;
+ }
+ else
+ {
+ printk("%s: received oversized frame.\n",dev->name);
+ p->stats.rx_dropped++;
+ }
+ }
+ else /* frame !(ok), only with 'save-bad-frames' */
+ {
+ printk("%s: oops! rfd-error-status: %04x\n",dev->name,status);
+ p->stats.rx_errors++;
+ }
+ p->rfd_top->status = 0;
+ p->rfd_top->last = RFD_SUSP;
+ p->rfd_last->last = 0; /* delete RU_SUSP */
+ p->rfd_last = p->rfd_top;
+ p->rfd_top = (struct rfd_struct *) make32(p->rfd_top->next); /* step to next RFD */
+ }
+}
+
+/**********************************************************
+ * handle 'Receiver went not ready'.
+ */
+
+static void ni52_rnr_int(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+
+ p->stats.rx_errors++;
+
+ WAIT_4_SCB_CMD(); /* wait for the last cmd */
+ p->scb->cmd = RUC_ABORT; /* usually the RU is in the 'no resource'-state .. abort it now. */
+ ni_attn586();
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. */
+
+ alloc_rfa(dev,(char *)p->rfd_first);
+ startrecv586(dev); /* restart RU */
+
+ printk("%s: Receive-Unit restarted. Status: %04x\n",dev->name,p->scb->status);
+
+}
+
+/**********************************************************
+ * handle xmit - interrupt
+ */
+
+static void ni52_xmt_int(struct device *dev)
+{
+ int status;
+ struct priv *p = (struct priv *) dev->priv;
+
+ status = p->xmit_cmds[p->xmit_last]->cmd_status;
+ if(!(status & STAT_COMPL))
+ printk("%s: strange .. xmit-int without a 'COMPLETE'\n",dev->name);
+
+ if(status & STAT_OK)
+ {
+ p->stats.tx_packets++;
+ p->stats.collisions += (status & TCMD_MAXCOLLMASK);
+ }
+ else
+ {
+ p->stats.tx_errors++;
+ if(status & TCMD_LATECOLL) {
+ printk("%s: late collision detected.\n",dev->name);
+ p->stats.collisions++;
+ }
+ else if(status & TCMD_NOCARRIER) {
+ p->stats.tx_carrier_errors++;
+ printk("%s: no carrier detected.\n",dev->name);
+ }
+ else if(status & TCMD_LOSTCTS)
+ printk("%s: loss of CTS detected.\n",dev->name);
+ else if(status & TCMD_UNDERRUN) {
+ p->stats.tx_fifo_errors++;
+ printk("%s: DMA underrun detected.\n",dev->name);
+ }
+ else if(status & TCMD_MAXCOLL) {
+ printk("%s: Max. collisions exceeded.\n",dev->name);
+ p->stats.collisions += 16;
+ }
+ }
+
+#if (NUM_XMIT_BUFFS != 1)
+ if( (++p->xmit_last) == NUM_XMIT_BUFFS)
+ p->xmit_last = 0;
+#endif
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+}
+
+/***********************************************************
+ * (re)start the receiver
+ */
+
+static void startrecv586(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+
+ p->scb->rfa_offset = make16(p->rfd_first);
+ p->scb->cmd = RUC_START;
+ ni_attn586(); /* start cmd. */
+ WAIT_4_SCB_CMD(); /* wait for accept cmd. (no timeout!!) */
+}
+
+/******************************************************
+ * send frame
+ */
+
+static int ni52_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int len,i;
+#ifndef NO_NOPCOMMANDS
+ int next_nop;
+#endif
+ struct priv *p = (struct priv *) dev->priv;
+
+ if(dev->tbusy)
+ {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+
+ if(p->scb->status & CU_ACTIVE) /* COMMAND-UNIT active? */
+ {
+ dev->tbusy = 0;
+#ifdef DEBUG
+ printk("%s: strange ... timeout with CU active?!?\n",dev->name);
+ printk("%s: X0: %04x N0: %04x N1: %04x %d\n",dev->name,(int)p->xmit_cmds[0]->cmd_status,(int)p->nop_cmds[0]->cmd_status,(int)p->nop_cmds[1]->cmd_status,(int)p->nop_point);
+#endif
+ p->scb->cmd = CUC_ABORT;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ p->scb->cbl_offset = make16(p->nop_cmds[p->nop_point]);
+ p->scb->cmd = CUC_START;
+ ni_attn586();
+ WAIT_4_SCB_CMD();
+ dev->trans_start = jiffies;
+ return 0;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("%s: xmitter timed out, try to restart! stat: %04x\n",dev->name,p->scb->status);
+ printk("%s: command-stats: %04x %04x\n",dev->name,p->xmit_cmds[0]->cmd_status,p->xmit_cmds[1]->cmd_status);
+#endif
+ ni52_close(dev);
+ ni52_open(dev);
+ }
+ dev->trans_start = jiffies;
+ return 0;
+ }
+
+ if(skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+ if(skb->len > XMIT_BUFF_SIZE)
+ {
+ printk("%s: Sorry, max. framelength is %d bytes. The length of your frame is %ld bytes.\n",dev->name,XMIT_BUFF_SIZE,skb->len);
+ return 0;
+ }
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else
+ {
+ memcpy((char *)p->xmit_cbuffs[p->xmit_count],(char *)(skb->data),skb->len);
+ len = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+
+#if (NUM_XMIT_BUFFS == 1)
+# ifdef NO_NOPCOMMANDS
+ p->xmit_buffs[0]->size = TBD_LAST | len;
+ for(i=0;i<16;i++)
+ {
+ p->scb->cbl_offset = make16(p->xmit_cmds[0]);
+ p->scb->cmd = CUC_START;
+ p->xmit_cmds[0]->cmd_status = 0;
+
+ ni_attn586();
+ dev->trans_start = jiffies;
+ if(!i)
+ dev_kfree_skb(skb,FREE_WRITE);
+ WAIT_4_SCB_CMD();
+ if( (p->scb->status & CU_ACTIVE)) /* test it, because CU sometimes doesn't start immediately */
+ break;
+ if(p->xmit_cmds[0]->cmd_status)
+ break;
+ if(i==15)
+ printk("%s: Can't start transmit-command.\n",dev->name);
+ }
+# else
+ next_nop = (p->nop_point + 1) & 0x1;
+ p->xmit_buffs[0]->size = TBD_LAST | len;
+
+ p->xmit_cmds[0]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->xmit_cmds[0]->cmd_status = p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->nop_point]->cmd_link = make16((p->xmit_cmds[0]));
+ dev->trans_start = jiffies;
+ p->nop_point = next_nop;
+ dev_kfree_skb(skb,FREE_WRITE);
+# endif
+#else
+ p->xmit_buffs[p->xmit_count]->size = TBD_LAST | len;
+ if( (next_nop = p->xmit_count + 1) == NUM_XMIT_BUFFS )
+ next_nop = 0;
+
+ p->xmit_cmds[p->xmit_count]->cmd_status = 0;
+ p->xmit_cmds[p->xmit_count]->cmd_link = p->nop_cmds[next_nop]->cmd_link
+ = make16((p->nop_cmds[next_nop]));
+ p->nop_cmds[next_nop]->cmd_status = 0;
+
+ p->nop_cmds[p->xmit_count]->cmd_link = make16((p->xmit_cmds[p->xmit_count]));
+ dev->trans_start = jiffies;
+ p->xmit_count = next_nop;
+
+ cli();
+ if(p->xmit_count != p->xmit_last)
+ dev->tbusy = 0;
+ sti();
+ dev_kfree_skb(skb,FREE_WRITE);
+#endif
+ }
+ return 0;
+}
+
+/*******************************************
+ * Someone wanna have the statistics
+ */
+
+static struct enet_statistics *ni52_get_stats(struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ unsigned short crc,aln,rsc,ovrn;
+
+ crc = p->scb->crc_errs; /* get error-statistic from the ni82586 */
+ p->scb->crc_errs -= crc;
+ aln = p->scb->aln_errs;
+ p->scb->aln_errs -= aln;
+ rsc = p->scb->rsc_errs;
+ p->scb->rsc_errs -= rsc;
+ ovrn = p->scb->ovrn_errs;
+ p->scb->ovrn_errs -= ovrn;
+
+ p->stats.rx_crc_errors += crc;
+ p->stats.rx_fifo_errors += ovrn;
+ p->stats.rx_frame_errors += aln;
+ p->stats.rx_dropped += rsc;
+
+ return &p->stats;
+}
+
+/********************************************************
+ * Set MC list ..
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ if(!dev->start)
+ {
+ printk("%s: Can't apply promiscuous/multicastmode to a not running interface.\n",dev->name);
+ return;
+ }
+
+ dev->start = 0;
+ alloc586(dev);
+ init586(dev);
+ startrecv586(dev);
+ dev->start = 1;
+}
+
+/*
+ * END: linux/drivers/net/ni52.c
+ */
diff --git a/i386/i386at/gpl/linux/net/ni52.h b/i386/i386at/gpl/linux/net/ni52.h
new file mode 100644
index 00000000..23b0a0e8
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni52.h
@@ -0,0 +1,284 @@
+/*
+ * Intel i82586 Ethernet definitions
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers that work.
+ *
+ * copyrights (c) 1994 by Michael Hipp (mhipp@student.uni-tuebingen.de)
+ *
+ * I have done a look in the following sources:
+ * crynwr-packet-driver by Russ Nelson
+ * Garret A. Wollman's i82586-driver for BSD
+ */
+
+
+#define NI52_RESET 0 /* writing to this address, resets the i82586 */
+#define NI52_ATTENTION 1 /* channel attention, kick the 586 */
+#define NI52_TENA 3 /* 2-5 possibly wrong, Xmit enable */
+#define NI52_TDIS 2 /* Xmit disable */
+#define NI52_INTENA 5 /* Interrupt enable */
+#define NI52_INTDIS 4 /* Interrupt disable */
+#define NI52_MAGIC1 6 /* dunno exact function */
+#define NI52_MAGIC2 7 /* dunno exact function */
+
+#define NI52_MAGICVAL1 0x00 /* magic-values for ni5210 card */
+#define NI52_MAGICVAL2 0x55
+
+/*
+ * where to find the System Configuration Pointer (SCP)
+ */
+#define SCP_DEFAULT_ADDRESS 0xfffff4
+
+
+/*
+ * System Configuration Pointer Struct
+ */
+
+struct scp_struct
+{
+ unsigned short zero_dum0; /* has to be zero */
+ unsigned char sysbus; /* 0=16Bit,1=8Bit */
+ unsigned char zero_dum1; /* has to be zero for 586 */
+ unsigned short zero_dum2;
+ unsigned short zero_dum3;
+ char *iscp; /* pointer to the iscp-block */
+};
+
+
+/*
+ * Intermediate System Configuration Pointer (ISCP)
+ */
+struct iscp_struct
+{
+ unsigned char busy; /* 586 clears after successful init */
+ unsigned char zero_dummy; /* hast to be zero */
+ unsigned short scb_offset; /* pointeroffset to the scb_base */
+ char *scb_base; /* base-address of all 16-bit offsets */
+};
+
+/*
+ * System Control Block (SCB)
+ */
+struct scb_struct
+{
+ unsigned short status; /* status word */
+ unsigned short cmd; /* command word */
+ unsigned short cbl_offset; /* pointeroffset, command block list */
+ unsigned short rfa_offset; /* pointeroffset, receive frame area */
+ unsigned short crc_errs; /* CRC-Error counter */
+ unsigned short aln_errs; /* allignmenterror counter */
+ unsigned short rsc_errs; /* Resourceerror counter */
+ unsigned short ovrn_errs; /* OVerrunerror counter */
+};
+
+/*
+ * possible command values for the command word
+ */
+#define RUC_MASK 0x0070 /* mask for RU commands */
+#define RUC_NOP 0x0000 /* NOP-command */
+#define RUC_START 0x0010 /* start RU */
+#define RUC_RESUME 0x0020 /* resume RU after suspend */
+#define RUC_SUSPEND 0x0030 /* suspend RU */
+#define RUC_ABORT 0x0040 /* abort receiver operation immediately */
+
+#define CUC_MASK 0x0700 /* mask for CU command */
+#define CUC_NOP 0x0000 /* NOP-command */
+#define CUC_START 0x0100 /* start execution of 1. cmd on the CBL */
+#define CUC_RESUME 0x0200 /* resume after suspend */
+#define CUC_SUSPEND 0x0300 /* Suspend CU */
+#define CUC_ABORT 0x0400 /* abort command operation immediately */
+
+#define ACK_MASK 0xf000 /* mask for ACK command */
+#define ACK_CX 0x8000 /* acknowledges STAT_CX */
+#define ACK_FR 0x4000 /* ack. STAT_FR */
+#define ACK_CNA 0x2000 /* ack. STAT_CNA */
+#define ACK_RNR 0x1000 /* ack. STAT_RNR */
+
+/*
+ * possible status values for the status word
+ */
+#define STAT_MASK 0xf000 /* mask for cause of interrupt */
+#define STAT_CX 0x8000 /* CU finished cmd with its I bit set */
+#define STAT_FR 0x4000 /* RU finished receiving a frame */
+#define STAT_CNA 0x2000 /* CU left active state */
+#define STAT_RNR 0x1000 /* RU left ready state */
+
+#define CU_STATUS 0x700 /* CU status, 0=idle */
+#define CU_SUSPEND 0x100 /* CU is suspended */
+#define CU_ACTIVE 0x200 /* CU is active */
+
+#define RU_STATUS 0x70 /* RU status, 0=idle */
+#define RU_SUSPEND 0x10 /* RU suspended */
+#define RU_NOSPACE 0x20 /* RU no resources */
+#define RU_READY 0x40 /* RU is ready */
+
+/*
+ * Receive Frame Descriptor (RFD)
+ */
+struct rfd_struct
+{
+ unsigned short status; /* status word */
+ unsigned short last; /* Bit15,Last Frame on List / Bit14,suspend */
+ unsigned short next; /* linkoffset to next RFD */
+ unsigned short rbd_offset; /* pointeroffset to RBD-buffer */
+ unsigned char dest[6]; /* ethernet-address, destination */
+ unsigned char source[6]; /* ethernet-address, source */
+ unsigned short length; /* 802.3 frame-length */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RFD_LAST 0x8000 /* last: last rfd in the list */
+#define RFD_SUSP 0x4000 /* last: suspend RU after */
+#define RFD_ERRMASK 0x0fe1 /* status: errormask */
+#define RFD_MATCHADD 0x0002 /* status: Destinationaddress !matches IA */
+#define RFD_RNR 0x0200 /* status: receiver out of resources */
+
+/*
+ * Receive Buffer Descriptor (RBD)
+ */
+struct rbd_struct
+{
+ unsigned short status; /* status word,number of used bytes in buff */
+ unsigned short next; /* pointeroffset to next RBD */
+ char *buffer; /* receive buffer address pointer */
+ unsigned short size; /* size of this buffer */
+ unsigned short zero_dummy; /* dummy */
+};
+
+#define RBD_LAST 0x8000 /* last buffer */
+#define RBD_USED 0x4000 /* this buffer has data */
+#define RBD_MASK 0x3fff /* size-mask for length */
+
+/*
+ * Statusvalues for Commands/RFD
+ */
+#define STAT_COMPL 0x8000 /* status: frame/command is complete */
+#define STAT_BUSY 0x4000 /* status: frame/command is busy */
+#define STAT_OK 0x2000 /* status: frame/command is ok */
+
+/*
+ * Action-Commands
+ */
+#define CMD_NOP 0x0000 /* NOP */
+#define CMD_IASETUP 0x0001 /* initial address setup command */
+#define CMD_CONFIGURE 0x0002 /* configure command */
+#define CMD_MCSETUP 0x0003 /* MC setup command */
+#define CMD_XMIT 0x0004 /* transmit command */
+#define CMD_TDR 0x0005 /* time domain reflectometer (TDR) command */
+#define CMD_DUMP 0x0006 /* dump command */
+#define CMD_DIAGNOSE 0x0007 /* diagnose command */
+
+/*
+ * Action command bits
+ */
+#define CMD_LAST 0x8000 /* indicates last command in the CBL */
+#define CMD_SUSPEND 0x4000 /* suspend CU after this CB */
+#define CMD_INT 0x2000 /* generate interrupt after execution */
+
+/*
+ * NOP - command
+ */
+struct nop_cmd_struct
+{
+ unsigned short cmd_status; /* status of this command */
+ unsigned short cmd_cmd; /* the command itself (+bits) */
+ unsigned short cmd_link; /* offsetpointer to next command */
+};
+
+/*
+ * IA Setup command
+ */
+struct iasetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char iaddr[6];
+};
+
+/*
+ * Configure command
+ */
+struct configure_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned char byte_cnt; /* size of the config-cmd */
+ unsigned char fifo; /* fifo/recv monitor */
+ unsigned char sav_bf; /* save bad frames (bit7=1)*/
+ unsigned char adr_len; /* adr_len(0-2),al_loc(3),pream(4-5),loopbak(6-7)*/
+ unsigned char priority; /* lin_prio(0-2),exp_prio(4-6),bof_metd(7) */
+ unsigned char ifs; /* inter frame spacing */
+ unsigned char time_low; /* slot time low */
+ unsigned char time_high; /* slot time high(0-2) and max. retries(4-7) */
+ unsigned char promisc; /* promisc-mode(0) , et al (1-7) */
+ unsigned char carr_coll; /* carrier(0-3)/collision(4-7) stuff */
+ unsigned char fram_len; /* minimal frame len */
+ unsigned char dummy; /* dummy */
+};
+
+/*
+ * Multicast Setup command
+ */
+struct mcsetup_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short mc_cnt; /* number of bytes in the MC-List */
+ unsigned char mc_list[0][6]; /* pointer to 6 bytes entries */
+};
+
+/*
+ * transmit command
+ */
+struct transmit_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short tbd_offset; /* pointeroffset to TBD */
+ unsigned char dest[6]; /* destination address of the frame */
+ unsigned short length; /* user defined: 802.3 length / Ether type */
+};
+
+#define TCMD_ERRMASK 0x0fa0
+#define TCMD_MAXCOLLMASK 0x000f
+#define TCMD_MAXCOLL 0x0020
+#define TCMD_HEARTBEAT 0x0040
+#define TCMD_DEFERRED 0x0080
+#define TCMD_UNDERRUN 0x0100
+#define TCMD_LOSTCTS 0x0200
+#define TCMD_NOCARRIER 0x0400
+#define TCMD_LATECOLL 0x0800
+
+struct tdr_cmd_struct
+{
+ unsigned short cmd_status;
+ unsigned short cmd_cmd;
+ unsigned short cmd_link;
+ unsigned short status;
+};
+
+#define TDR_LNK_OK 0x8000 /* No link problem identified */
+#define TDR_XCVR_PRB 0x4000 /* indicates a transceiver problem */
+#define TDR_ET_OPN 0x2000 /* open, no correct termination */
+#define TDR_ET_SRT 0x1000 /* TDR detected a short circuit */
+#define TDR_TIMEMASK 0x07ff /* mask for the time field */
+
+/*
+ * Transmit Buffer Descriptor (TBD)
+ */
+struct tbd_struct
+{
+ unsigned short size; /* size + EOF-Flag(15) */
+ unsigned short next; /* pointeroffset to next TBD */
+ char *buffer; /* pointer to buffer */
+};
+
+#define TBD_LAST 0x8000 /* EOF-Flag, indicates last buffer in list */
+
+
+
+
diff --git a/i386/i386at/gpl/linux/net/ni65.c b/i386/i386at/gpl/linux/net/ni65.c
new file mode 100644
index 00000000..44a58112
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni65.c
@@ -0,0 +1,648 @@
+/*
+ * ni6510 (am7990 'lance' chip) driver for Linux-net-3 by MH
+ * Alphacode v0.33 (94/08/22) for 1.1.47 (or later)
+ *
+ * ----------------------------------------------------------
+ * WARNING: DOESN'T WORK ON MACHINES WITH MORE THAN 16MB !!!!
+ * ----------------------------------------------------------
+ *
+ * copyright (c) 1994 M.Hipp
+ *
+ * This is an extension to the Linux operating system, and is covered by the
+ * same Gnu Public License that covers the Linux-kernel.
+ *
+ * comments/bugs/suggestions can be sent to:
+ * Michael Hipp
+ * email: mhipp@student.uni-tuebingen.de
+ *
+ * sources:
+ * some things are from the 'ni6510-packet-driver for dos by Russ Nelson'
+ * and from the original drivers by D.Becker
+ */
+
+/*
+ * Nov.18: multicast tweaked (AC).
+ *
+ * Aug.22: changes in xmit_intr (ack more than one xmitted-packet), ni65_send_packet (p->lock) (MH)
+ *
+ * July.16: fixed bugs in recv_skb and skb-alloc stuff (MH)
+ */
+
+/*
+ * known BUGS: 16MB limit
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "ni65.h"
+
+/************************************
+ * skeleton-stuff
+ */
+
+#ifndef HAVE_PORTRESERVE
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+#endif
+
+#ifndef NET_DEBUG
+#define NET_DEBUG 2
+#endif
+/*
+static unsigned int net_debug = NET_DEBUG;
+*/
+
+#define NI65_TOTAL_SIZE 16
+
+#define SA_ADDR0 0x02
+#define SA_ADDR1 0x07
+#define SA_ADDR2 0x01
+#define CARD_ID0 0x00
+#define CARD_ID1 0x55
+
+/*****************************************/
+
+#define PORT dev->base_addr
+
+#define RMDNUM 8
+#define RMDNUMMASK 0x6000 /* log2(RMDNUM)<<13 */
+#define TMDNUM 4
+#define TMDNUMMASK 0x4000 /* log2(TMDNUM)<<13 */
+
+#define R_BUF_SIZE 1518
+#define T_BUF_SIZE 1518
+
+#define MEMSIZE 8+RMDNUM*8+TMDNUM*8
+
+#define L_DATAREG 0x00
+#define L_ADDRREG 0x02
+
+#define L_RESET 0x04
+#define L_CONFIG 0x05
+#define L_EBASE 0x08
+
+/*
+ * to access the am7990-regs, you have to write
+ * reg-number into L_ADDRREG, then you can access it using L_DATAREG
+ */
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+/* if you #define NO_STATIC the driver is faster but you will have (more) problems with >16MB memory */
+#undef NO_STATIC
+
+#define writereg(val,reg) {outw(reg,PORT+L_ADDRREG);inw(PORT+L_ADDRREG); \
+ outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
+#define readreg(reg) (outw(reg,PORT+L_ADDRREG),inw(PORT+L_ADDRREG),\
+ inw(PORT+L_DATAREG))
+#define writedatareg(val) {outw(val,PORT+L_DATAREG);inw(PORT+L_DATAREG);}
+
+static int ni65_probe1(struct device *dev,int);
+static void ni65_interrupt(int irq, struct pt_regs *regs);
+ static void recv_intr(struct device *dev);
+ static void xmit_intr(struct device *dev);
+static int ni65_open(struct device *dev);
+ static int am7990_reinit(struct device *dev);
+static int ni65_send_packet(struct sk_buff *skb, struct device *dev);
+static int ni65_close(struct device *dev);
+static struct enet_statistics *ni65_get_stats(struct device *);
+
+static void set_multicast_list(struct device *dev);
+
+struct priv
+{
+ struct init_block ib;
+ void *memptr;
+ struct rmd *rmdhead;
+ struct tmd *tmdhead;
+ int rmdnum;
+ int tmdnum,tmdlast;
+ struct sk_buff *recv_skb[RMDNUM];
+ void *tmdbufs[TMDNUM];
+ int lock,xmit_queued;
+ struct enet_statistics stats;
+};
+
+int irqtab[] = { 9,12,15,5 }; /* irq config-translate */
+int dmatab[] = { 0,3,5,6 }; /* dma config-translate */
+
+/*
+ * open (most done by init)
+ */
+
+static int ni65_open(struct device *dev)
+{
+ if(am7990_reinit(dev))
+ {
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+ }
+ else
+ {
+ dev->start = 0;
+ return -EAGAIN;
+ }
+}
+
+static int ni65_close(struct device *dev)
+{
+ outw(0,PORT+L_RESET); /* that's the hard way */
+ dev->tbusy = 1;
+ dev->start = 0;
+ return 0;
+}
+
+/*
+ * Probe The Card (not the lance-chip)
+ * and set hardaddress
+ */
+
+int ni65_probe(struct device *dev)
+{
+ int *port, ports[] = {0x300,0x320,0x340,0x360, 0};
+ int base_addr = dev->base_addr;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ni65_probe1(dev, base_addr);
+ else if (base_addr > 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (port = ports; *port; port++)
+ {
+ int ioaddr = *port;
+ if (check_region(ioaddr, NI65_TOTAL_SIZE))
+ continue;
+ if( !(inb(ioaddr+L_EBASE+6) == CARD_ID0) ||
+ !(inb(ioaddr+L_EBASE+7) == CARD_ID1) )
+ continue;
+ dev->base_addr = ioaddr;
+ if (ni65_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ dev->base_addr = base_addr;
+ return ENODEV;
+}
+
+
+static int ni65_probe1(struct device *dev,int ioaddr)
+{
+ int i;
+ unsigned char station_addr[6];
+ struct priv *p;
+
+ for(i=0;i<6;i++)
+ station_addr[i] = dev->dev_addr[i] = inb(PORT+L_EBASE+i);
+
+ if(station_addr[0] != SA_ADDR0 || station_addr[1] != SA_ADDR1)
+ {
+ printk("%s: wrong Hardaddress \n",dev->name);
+ return ENODEV;
+ }
+
+ if(dev->irq == 0)
+ dev->irq = irqtab[(inw(PORT+L_CONFIG)>>2)&3];
+ if(dev->dma == 0)
+ dev->dma = dmatab[inw(PORT+L_CONFIG)&3];
+
+ printk("%s: %s found at %#3lx, IRQ %d DMA %d.\n", dev->name,
+ "network card", dev->base_addr, dev->irq,dev->dma);
+
+ {
+ int irqval = request_irq(dev->irq, &ni65_interrupt,0,"ni65");
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n",
+ dev->name,dev->irq, irqval);
+ return EAGAIN;
+ }
+ if(request_dma(dev->dma, "ni65") != 0)
+ {
+ printk("%s: Can't request dma-channel %d\n",dev->name,(int) dev->dma);
+ free_irq(dev->irq);
+ return EAGAIN;
+ }
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr,NI65_TOTAL_SIZE,"ni65");
+
+ p = dev->priv = (void *) kmalloc(sizeof(struct priv),GFP_KERNEL);
+ if (p == NULL)
+ return -ENOMEM;
+ memset((char *) dev->priv,0,sizeof(struct priv));
+
+ dev->open = ni65_open;
+ dev->stop = ni65_close;
+ dev->hard_start_xmit = ni65_send_packet;
+ dev->get_stats = ni65_get_stats;
+ dev->set_multicast_list = set_multicast_list;
+
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST;
+ dev->interrupt = 0;
+ dev->tbusy = 0;
+ dev->start = 0;
+
+ if( (p->memptr = kmalloc(MEMSIZE,GFP_KERNEL)) == NULL) {
+ printk("%s: Can't alloc TMD/RMD-buffer.\n",dev->name);
+ return EAGAIN;
+ }
+ if( (unsigned long) (p->memptr + MEMSIZE) & 0xff000000) {
+ printk("%s: Can't alloc TMD/RMD buffer in lower 16MB!\n",dev->name);
+ return EAGAIN;
+ }
+ p->tmdhead = (struct tmd *) ((( (unsigned long)p->memptr ) + 8) & 0xfffffff8);
+ p->rmdhead = (struct rmd *) (p->tmdhead + TMDNUM);
+
+#ifndef NO_STATIC
+ for(i=0;i<TMDNUM;i++)
+ {
+ if( (p->tmdbufs[i] = kmalloc(T_BUF_SIZE,GFP_ATOMIC)) == NULL) {
+ printk("%s: Can't alloc Xmit-Mem.\n",dev->name);
+ return EAGAIN;
+ }
+ if( (unsigned long) (p->tmdbufs[i]+T_BUF_SIZE) & 0xff000000) {
+ printk("%s: Can't alloc Xmit-Mem in lower 16MB!\n",dev->name);
+ return EAGAIN;
+ }
+ }
+#endif
+
+ for(i=0;i<RMDNUM;i++)
+ {
+ if( (p->recv_skb[i] = dev_alloc_skb(R_BUF_SIZE)) == NULL) {
+ printk("%s: unable to alloc recv-mem\n",dev->name);
+ return EAGAIN;
+ }
+ if( (unsigned long) (p->recv_skb[i]->data + R_BUF_SIZE) & 0xff000000) {
+ printk("%s: unable to alloc receive-memory in lower 16MB!\n",dev->name);
+ return EAGAIN;
+ }
+ }
+
+ return 0; /* we've found everything */
+}
+
+/*
+ * init lance (write init-values .. init-buffers) (open-helper)
+ */
+
+static int am7990_reinit(struct device *dev)
+{
+ int i,j;
+ struct tmd *tmdp;
+ struct rmd *rmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+ p->lock = 0;
+ p->xmit_queued = 0;
+
+ disable_dma(dev->dma); /* I've never worked with dma, but we do it like the packetdriver */
+ set_dma_mode(dev->dma,DMA_MODE_CASCADE);
+ enable_dma(dev->dma);
+
+ outw(0,PORT+L_RESET); /* first: reset the card */
+ if(inw(PORT+L_DATAREG) != 0x4)
+ {
+ printk("%s: can't RESET ni6510 card: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ disable_dma(dev->dma);
+ free_dma(dev->dma);
+ free_irq(dev->irq);
+ return 0;
+ }
+
+ /* here: memset all buffs to zero */
+
+ memset(p->memptr,0,MEMSIZE);
+
+ p->tmdnum = 0; p->tmdlast = 0;
+ for(i=0;i<TMDNUM;i++)
+ {
+ tmdp = p->tmdhead + i;
+#ifndef NO_STATIC
+ tmdp->u.buffer = (unsigned long) p->tmdbufs[i];
+#endif
+ tmdp->u.s.status = XMIT_START | XMIT_END;
+ }
+
+ p->rmdnum = 0;
+ for(i=0;i<RMDNUM;i++)
+ {
+ rmdp = p->rmdhead + i;
+ rmdp->u.buffer = (unsigned long) p->recv_skb[i]->data;
+ rmdp->u.s.status = RCV_OWN;
+ rmdp->blen = -R_BUF_SIZE;
+ rmdp->mlen = 0;
+ }
+
+ for(i=0;i<6;i++)
+ {
+ p->ib.eaddr[i] = dev->dev_addr[i];
+ }
+ p->ib.mode = 0;
+ for(i=0;i<8;i++)
+ p->ib.filter[i] = 0;
+ p->ib.trplow = (unsigned short) (( (unsigned long) p->tmdhead ) & 0xffff);
+ p->ib.trphigh = (unsigned short) ((( (unsigned long) p->tmdhead )>>16) & 0x00ff) | TMDNUMMASK;
+ p->ib.rrplow = (unsigned short) (( (unsigned long) p->rmdhead ) & 0xffff);
+ p->ib.rrphigh = (unsigned short) ((( (unsigned long) p->rmdhead )>>16) & 0x00ff) | RMDNUMMASK;
+
+ writereg(0,CSR3); /* busmaster/no word-swap */
+ writereg((unsigned short) (((unsigned long) &(p->ib)) & 0xffff),CSR1);
+ writereg((unsigned short) (((unsigned long) &(p->ib))>>16),CSR2);
+
+ writereg(CSR0_INIT,CSR0); /* this changes L_ADDRREG to CSR0 */
+
+ /*
+ * NOW, WE NEVER WILL CHANGE THE L_ADDRREG, CSR0 IS ALWAYS SELECTED
+ */
+
+ for(i=0;i<5;i++)
+ {
+ for(j=0;j<2000000;j++); /* wait a while */
+ if(inw(PORT+L_DATAREG) & CSR0_IDON) break; /* init ok ? */
+ }
+ if(i == 5)
+ {
+ printk("%s: can't init am7990, status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ disable_dma(dev->dma);
+ free_dma(dev->dma);
+ free_irq(dev->irq);
+ return 0; /* false */
+ }
+
+ writedatareg(CSR0_CLRALL | CSR0_INEA | CSR0_STRT); /* start lance , enable interrupts */
+
+ return 1; /* OK */
+}
+
+/*
+ * interrupt handler
+ */
+
+static void ni65_interrupt(int irq, struct pt_regs * regs)
+{
+ int csr0;
+ struct device *dev = (struct device *) irq2dev_map[irq];
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ csr0 = inw(PORT+L_DATAREG);
+ writedatareg(csr0 & CSR0_CLRALL); /* ack interrupts, disable int. */
+
+ dev->interrupt = 1;
+
+ if(csr0 & CSR0_ERR)
+ {
+ struct priv *p = (struct priv *) dev->priv;
+
+ if(csr0 & CSR0_BABL)
+ p->stats.tx_errors++;
+ if(csr0 & CSR0_MISS)
+ p->stats.rx_errors++;
+ }
+
+ if(csr0 & CSR0_RINT) /* RECV-int? */
+ {
+ recv_intr(dev);
+ }
+ if(csr0 & CSR0_TINT) /* XMIT-int? */
+ {
+ xmit_intr(dev);
+ }
+
+ writedatareg(CSR0_INEA); /* reenable inter. */
+ dev->interrupt = 0;
+
+ return;
+}
+
+/*
+ * We have received an Xmit-Interrupt ..
+ * send a new packet if necessary
+ */
+
+static void xmit_intr(struct device *dev)
+{
+ int tmdstat;
+ struct tmd *tmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+#ifdef NO_STATIC
+ struct sk_buff *skb;
+#endif
+
+ while(p->xmit_queued)
+ {
+ tmdp = p->tmdhead + p->tmdlast;
+ tmdstat = tmdp->u.s.status;
+ if(tmdstat & XMIT_OWN)
+ break;
+#ifdef NO_STATIC
+ skb = (struct sk_buff *) p->tmdbufs[p->tmdlast];
+ dev_kfree_skb(skb,FREE_WRITE);
+#endif
+
+ if(tmdstat & XMIT_ERR)
+ {
+ printk("%s: xmit-error: %04x %04x\n",dev->name,(int) tmdstat,(int) tmdp->status2);
+ if(tmdp->status2 & XMIT_TDRMASK)
+ printk("%s: tdr-problems (e.g. no resistor)\n",dev->name);
+
+ /* checking some errors */
+ if(tmdp->status2 & XMIT_RTRY)
+ p->stats.tx_aborted_errors++;
+ if(tmdp->status2 & XMIT_LCAR)
+ p->stats.tx_carrier_errors++;
+ p->stats.tx_errors++;
+ tmdp->status2 = 0;
+ }
+ else
+ p->stats.tx_packets++;
+
+ p->tmdlast = (p->tmdlast + 1) & (TMDNUM-1);
+ if(p->tmdlast == p->tmdnum)
+ p->xmit_queued = 0;
+ }
+
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+}
+
+/*
+ * We have received a packet
+ */
+
+static void recv_intr(struct device *dev)
+{
+ struct rmd *rmdp;
+ int rmdstat,len;
+ struct sk_buff *skb,*skb1;
+ struct priv *p = (struct priv *) dev->priv;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ while(!( (rmdstat = rmdp->u.s.status) & RCV_OWN))
+ {
+ if( (rmdstat & (RCV_START | RCV_END)) != (RCV_START | RCV_END) ) /* is packet start & end? */
+ {
+ if(rmdstat & RCV_START)
+ {
+ p->stats.rx_errors++;
+ p->stats.rx_length_errors++;
+ printk("%s: packet too long\n",dev->name);
+ }
+ rmdp->u.s.status = RCV_OWN; /* change owner */
+ }
+ else if(rmdstat & RCV_ERR)
+ {
+ printk("%s: receive-error: %04x\n",dev->name,(int) rmdstat );
+ p->stats.rx_errors++;
+ if(rmdstat & RCV_FRAM) p->stats.rx_frame_errors++;
+ if(rmdstat & RCV_OFLO) p->stats.rx_over_errors++;
+ if(rmdstat & RCV_CRC) p->stats.rx_crc_errors++;
+ rmdp->u.s.status = RCV_OWN;
+ printk("%s: lance-status: %04x\n",dev->name,(int) inw(PORT+L_DATAREG));
+ }
+ else
+ {
+ len = (rmdp->mlen & 0x0fff) - 4; /* -4: ignore FCS */
+ skb = dev_alloc_skb(R_BUF_SIZE);
+ if(skb != NULL)
+ {
+ if( (unsigned long) (skb->data + R_BUF_SIZE) & 0xff000000) {
+ memcpy(skb_put(skb,len),p->recv_skb[p->rmdnum]->data,len);
+ skb1 = skb;
+ }
+ else {
+ skb1 = p->recv_skb[p->rmdnum];
+ p->recv_skb[p->rmdnum] = skb;
+ rmdp->u.buffer = (unsigned long) skb_put(skb1,len);
+ }
+ rmdp->u.s.status = RCV_OWN;
+ rmdp->mlen = 0; /* not necc ???? */
+ skb1->dev = dev;
+ p->stats.rx_packets++;
+ skb1->protocol=eth_type_trans(skb1,dev);
+ netif_rx(skb1);
+ }
+ else
+ {
+ rmdp->u.s.status = RCV_OWN;
+ printk("%s: can't alloc new sk_buff\n",dev->name);
+ p->stats.rx_dropped++;
+ }
+ }
+ p->rmdnum++; p->rmdnum &= RMDNUM-1;
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+}
+
+/*
+ * kick xmitter ..
+ */
+
+static int ni65_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+
+ if(dev->tbusy)
+ {
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 25)
+ return 1;
+
+ printk("%s: xmitter timed out, try to restart!\n",dev->name);
+ am7990_reinit(dev);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ if(skb == NULL)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ if (skb->len <= 0)
+ return 0;
+
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+ if(set_bit(0,(void*) &p->lock) != 0)
+ {
+ printk("%s: Queue was locked!\n",dev->name);
+ return 1;
+ }
+
+ {
+ short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+ tmdp = p->tmdhead + p->tmdnum;
+
+#ifdef NO_STATIC
+ tmdp->u.buffer = (unsigned long) (skb->data);
+ p->tmdbufs[p->tmdnum] = skb;
+#else
+ memcpy((char *) (tmdp->u.buffer & 0x00ffffff),(char *)skb->data,skb->len);
+ dev_kfree_skb (skb, FREE_WRITE);
+#endif
+ tmdp->blen = -len;
+ tmdp->u.s.status = XMIT_OWN | XMIT_START | XMIT_END;
+
+ cli();
+ p->xmit_queued = 1;
+ writedatareg(CSR0_TDMD | CSR0_INEA); /* enable xmit & interrupt */
+ p->tmdnum++; p->tmdnum &= TMDNUM-1;
+
+ if( !((p->tmdhead + p->tmdnum)->u.s.status & XMIT_OWN) )
+ dev->tbusy = 0;
+ p->lock = 0;
+ sti();
+
+ dev->trans_start = jiffies;
+
+ }
+
+ return 0;
+}
+
+static struct enet_statistics *ni65_get_stats(struct device *dev)
+{
+ return &((struct priv *) dev->priv)->stats;
+}
+
+static void set_multicast_list(struct device *dev)
+{
+}
+
+/*
+ * END of ni65.c
+ */
+
diff --git a/i386/i386at/gpl/linux/net/ni65.h b/i386/i386at/gpl/linux/net/ni65.h
new file mode 100644
index 00000000..144523fa
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/ni65.h
@@ -0,0 +1,130 @@
+/* am7990 (lance) definitions
+ *
+ * This is a extension to the Linux operating system, and is covered by
+ * same Gnu Public License that covers that work.
+ *
+ * Michael Hipp
+ * email: mhipp@student.uni-tuebingen.de
+ *
+ * sources: (mail me or ask archie if you need them)
+ * crynwr-packet-driver
+ */
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define M_PROM 0x8000 /* Promiscuous Mode */
+#define M_INTL 0x0040 /* Internal Loopback */
+#define M_DRTY 0x0020 /* Disable Retry */
+#define M_COLL 0x0010 /* Force Collision */
+#define M_DTCR 0x0008 /* Disable Transmit CRC) */
+#define M_LOOP 0x0004 /* Loopback */
+#define M_DTX 0x0002 /* Disable the Transmitter */
+#define M_DRX 0x0001 /* Disable the Receiver */
+
+
+/*
+ * Receive message descriptor bit definitions.
+ */
+
+#define RCV_OWN 0x80 /* owner bit 0 = host, 1 = lance */
+#define RCV_ERR 0x40 /* Error Summary */
+#define RCV_FRAM 0x20 /* Framing Error */
+#define RCV_OFLO 0x10 /* Overflow Error */
+#define RCV_CRC 0x08 /* CRC Error */
+#define RCV_BUF_ERR 0x04 /* Buffer Error */
+#define RCV_START 0x02 /* Start of Packet */
+#define RCV_END 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor bit definitions.
+ */
+
+#define XMIT_OWN 0x80 /* owner bit 0 = host, 1 = lance */
+#define XMIT_ERR 0x40 /* Error Summary */
+#define XMIT_RETRY 0x10 /* more the 1 retry needed to Xmit */
+#define XMIT_1_RETRY 0x08 /* one retry needed to Xmit */
+#define XMIT_DEF 0x04 /* Deferred */
+#define XMIT_START 0x02 /* Start of Packet */
+#define XMIT_END 0x01 /* End of Packet */
+
+/*
+ * transmit status (2) (valid if XMIT_ERR == 1)
+ */
+
+#define XMIT_RTRY 0x0200 /* Failed after 16 retransmissions */
+#define XMIT_LCAR 0x0400 /* Loss of Carrier */
+#define XMIT_LCOL 0x1000 /* Late collision */
+#define XMIT_RESERV 0x2000 /* Reserved */
+#define XMIT_UFLO 0x4000 /* Underflow (late memory) */
+#define XMIT_BUFF 0x8000 /* Buffering error (no ENP) */
+#define XMIT_TDRMASK 0x003f /* time-domain-reflectometer-value */
+
+struct init_block
+{
+ unsigned short mode;
+ unsigned char eaddr[6];
+ unsigned char filter[8];
+ unsigned short rrplow; /* receive ring pointer (align 8) */
+ unsigned short rrphigh; /* bit 13-15: number of rmd's (power of 2) */
+ unsigned short trplow; /* transmit ring pointer (align 8) */
+ unsigned short trphigh; /* bit 13-15: number of tmd's (power of 2) */
+};
+
+struct rmd /* Receive Message Descriptor */
+{
+ union
+ {
+ volatile unsigned long buffer;
+ struct
+ {
+ volatile unsigned char dummy[3];
+ volatile unsigned char status;
+ } s;
+ } u;
+ short blen;
+ volatile unsigned short mlen;
+};
+
+struct tmd
+{
+ union
+ {
+ volatile unsigned long buffer;
+ struct
+ {
+ volatile unsigned char dummy[3];
+ volatile unsigned char status;
+ } s;
+ } u;
+ unsigned short blen;
+ volatile unsigned short status2;
+};
+
+
diff --git a/i386/i386at/gpl/linux/net/seeq8005.c b/i386/i386at/gpl/linux/net/seeq8005.c
new file mode 100644
index 00000000..5799d80c
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/seeq8005.c
@@ -0,0 +1,760 @@
+/* seeq8005.c: A network driver for linux. */
+/*
+ Based on skeleton.c,
+ Written 1993-94 by Donald Becker.
+ See the skeleton.c file for further copyright information.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as hamish@zot.apana.org.au
+
+ This file is a network device driver for the SEEQ 8005 chipset and
+ the Linux operating system.
+
+*/
+
+static const char *version =
+ "seeq8005.c:v1.00 8/07/95 Hamish Coleman (hamish@zot.apana.org.au)\n";
+
+/*
+ Sources:
+ SEEQ 8005 databook
+
+ Version history:
+ 1.00 Public release. cosmetic changes (no warnings now)
+ 0.68 Turning per- packet,interrupt debug messages off - testing for release.
+ 0.67 timing problems/bad buffer reads seem to be fixed now
+ 0.63 *!@$ protocol=eth_type_trans -- now packets flow
+ 0.56 Send working
+ 0.48 Receive working
+*/
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/malloc.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include "seeq8005.h"
+
+/* First, a few definitions that the brave might change. */
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int seeq8005_portlist[] =
+ { 0x300, 0x320, 0x340, 0x360, 0};
+
+/* use 0 for production, 1 for verification, >2 for debug */
+#ifndef NET_DEBUG
+#define NET_DEBUG 1
+#endif
+static unsigned int net_debug = NET_DEBUG;
+
+/* Information that need to be kept for each board. */
+struct net_local {
+ struct enet_statistics stats;
+ unsigned short receive_ptr; /* What address in packet memory do we expect a recv_pkt_header? */
+ long open_time; /* Useless example local info. */
+};
+
+/* The station (ethernet) address prefix, used for IDing the board. */
+#define SA_ADDR0 0x00
+#define SA_ADDR1 0x80
+#define SA_ADDR2 0x4b
+
+/* Index to functions, as function prototypes. */
+
+extern int seeq8005_probe(struct device *dev);
+
+static int seeq8005_probe1(struct device *dev, int ioaddr);
+static int seeq8005_open(struct device *dev);
+static int seeq8005_send_packet(struct sk_buff *skb, struct device *dev);
+static void seeq8005_interrupt(int irq, struct pt_regs *regs);
+static void seeq8005_rx(struct device *dev);
+static int seeq8005_close(struct device *dev);
+static struct enet_statistics *seeq8005_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+
+/* Example routines you must write ;->. */
+#define tx_done(dev) (inw(SEEQ_STATUS) & SEEQSTAT_TX_ON)
+extern void hardware_send_packet(struct device *dev, char *buf, int length);
+extern void seeq8005_init(struct device *dev, int startp);
+inline void wait_for_buffer(struct device *dev);
+
+
+/* Check for a network adaptor of this type, and return '0' iff one exists.
+ If dev->base_addr == 0, probe all likely locations.
+ If dev->base_addr == 1, always return failure.
+ If dev->base_addr == 2, allocate space for the device and return success
+ (detachable devices only).
+ */
+#ifdef HAVE_DEVLIST
+/* Support for a alternate probe manager, which will eliminate the
+ boilerplate below. */
+struct netdev_entry seeq8005_drv =
+{"seeq8005", seeq8005_probe1, SEEQ8005_IO_EXTENT, seeq8005_portlist};
+#else
+int
+seeq8005_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return seeq8005_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; seeq8005_portlist[i]; i++) {
+ int ioaddr = seeq8005_portlist[i];
+ if (check_region(ioaddr, SEEQ8005_IO_EXTENT))
+ continue;
+ if (seeq8005_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+/* This is the real probe routine. Linux has a history of friendly device
+ probes on the ISA bus. A good device probes avoids doing writes, and
+ verifies that the correct device exists and functions. */
+
+static int seeq8005_probe1(struct device *dev, int ioaddr)
+{
+ static unsigned version_printed = 0;
+ int i,j;
+ unsigned char SA_prom[32];
+ int old_cfg1;
+ int old_cfg2;
+ int old_stat;
+ int old_dmaar;
+ int old_rear;
+
+ if (net_debug>1)
+ printk("seeq8005: probing at 0x%x\n",ioaddr);
+
+ old_stat = inw(SEEQ_STATUS); /* read status register */
+ if (old_stat == 0xffff)
+ return ENODEV; /* assume that 0xffff == no device */
+ if ( (old_stat & 0x1800) != 0x1800 ) { /* assume that unused bits are 1, as my manual says */
+ if (net_debug>1) {
+ printk("seeq8005: reserved stat bits != 0x1800\n");
+ printk(" == 0x%04x\n",old_stat);
+ }
+ return ENODEV;
+ }
+
+ old_rear = inw(SEEQ_REA);
+ if (old_rear == 0xffff) {
+ outw(0,SEEQ_REA);
+ if (inw(SEEQ_REA) == 0xffff) { /* assume that 0xffff == no device */
+ return ENODEV;
+ }
+ } else if ((old_rear & 0xff00) != 0xff00) { /* assume that unused bits are 1 */
+ if (net_debug>1) {
+ printk("seeq8005: unused rear bits != 0xff00\n");
+ printk(" == 0x%04x\n",old_rear);
+ }
+ return ENODEV;
+ }
+
+ old_cfg2 = inw(SEEQ_CFG2); /* read CFG2 register */
+ old_cfg1 = inw(SEEQ_CFG1);
+ old_dmaar = inw(SEEQ_DMAAR);
+
+ if (net_debug>4) {
+ printk("seeq8005: stat = 0x%04x\n",old_stat);
+ printk("seeq8005: cfg1 = 0x%04x\n",old_cfg1);
+ printk("seeq8005: cfg2 = 0x%04x\n",old_cfg2);
+ printk("seeq8005: raer = 0x%04x\n",old_rear);
+ printk("seeq8005: dmaar= 0x%04x\n",old_dmaar);
+ }
+
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD); /* setup for reading PROM */
+ outw( 0, SEEQ_DMAAR); /* set starting PROM address */
+ outw( SEEQCFG1_BUFFER_PROM, SEEQ_CFG1); /* set buffer to look at PROM */
+
+
+ j=0;
+ for(i=0; i <32; i++) {
+ j+= SA_prom[i] = inw(SEEQ_BUFFER) & 0xff;
+ }
+
+#if 0
+ /* untested because I only have the one card */
+ if ( (j&0xff) != 0 ) { /* checksum appears to be 8bit = 0 */
+ if (net_debug>1) { /* check this before deciding that we have a card */
+ printk("seeq8005: prom sum error\n");
+ }
+ outw( old_stat, SEEQ_STATUS);
+ outw( old_dmaar, SEEQ_DMAAR);
+ outw( old_cfg1, SEEQ_CFG1);
+ return ENODEV;
+ }
+#endif
+
+ outw( SEEQCFG2_RESET, SEEQ_CFG2); /* reset the card */
+ SLOW_DOWN_IO; /* have to wait 4us after a reset - should be fixed */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+
+ if (net_debug) {
+ printk("seeq8005: prom sum = 0x%08x\n",j);
+ for(j=0; j<32; j+=16) {
+ printk("seeq8005: prom %02x: ",j);
+ for(i=0;i<16;i++) {
+ printk("%02x ",SA_prom[j|i]);
+ }
+ printk(" ");
+ for(i=0;i<16;i++) {
+ if ((SA_prom[j|i]>31)&&(SA_prom[j|i]<127)) {
+ printk("%c", SA_prom[j|i]);
+ } else {
+ printk(" ");
+ }
+ }
+ printk("\n");
+ }
+ }
+
+#if 0
+ /*
+ * testing the packet buffer memory doesnt work yet
+ * but all other buffer accesses do
+ * - fixing is not a priority
+ */
+ if (net_debug>1) { /* test packet buffer memory */
+ printk("seeq8005: testing packet buffer ... ");
+ outw( SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1);
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ outw( 0 , SEEQ_DMAAR);
+ for(i=0;i<32768;i++) {
+ outw(0x5a5a, SEEQ_BUFFER);
+ }
+ j=jiffies+HZ;
+ while ( ((inw(SEEQ_STATUS) & SEEQSTAT_FIFO_EMPTY) != SEEQSTAT_FIFO_EMPTY) && jiffies < j )
+ mb();
+ outw( 0 , SEEQ_DMAAR);
+ while ( ((inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < j+HZ)
+ mb();
+ if ( (inw(SEEQ_STATUS) & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
+ outw( SEEQCMD_WINDOW_INT_ACK | (inw(SEEQ_STATUS)& SEEQCMD_INT_MASK), SEEQ_CMD);
+ outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ j=0;
+ for(i=0;i<32768;i++) {
+ if (inw(SEEQ_BUFFER) != 0x5a5a)
+ j++;
+ }
+ if (j) {
+ printk("%i\n",j);
+ } else {
+ printk("ok.\n");
+ }
+ }
+#endif
+
+ /* Allocate a new 'dev' if needed. */
+ if (dev == NULL)
+ dev = init_etherdev(0, sizeof(struct net_local));
+
+ if (net_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: %s found at %#3x, ", dev->name, "seeq8005", ioaddr);
+
+ /* Fill in the 'dev' fields. */
+ dev->base_addr = ioaddr;
+
+ /* Retrieve and print the ethernet address. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = SA_prom[i+6]);
+
+ if (dev->irq == 0xff)
+ ; /* Do nothing: a user-level program will set it. */
+ else if (dev->irq < 2) { /* "Auto-IRQ" */
+ autoirq_setup(0);
+
+ outw( SEEQCMD_RX_INT_EN | SEEQCMD_SET_RX_ON | SEEQCMD_SET_RX_OFF, SEEQ_CMD );
+
+ dev->irq = autoirq_report(0);
+
+ if (net_debug >= 2)
+ printk(" autoirq is %d\n", dev->irq);
+ } else if (dev->irq == 2)
+ /* Fixup for users that don't know that IRQ 2 is really IRQ 9,
+ * or don't know which one to set.
+ */
+ dev->irq = 9;
+
+#if 0
+ {
+ int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005");
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+#endif
+
+ /* Grab the region so we can find another board if autoIRQ fails. */
+ request_region(ioaddr, SEEQ8005_IO_EXTENT,"seeq8005");
+
+ /* Initialize the device structure. */
+ dev->priv = kmalloc(sizeof(struct net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0, sizeof(struct net_local));
+
+ dev->open = seeq8005_open;
+ dev->stop = seeq8005_close;
+ dev->hard_start_xmit = seeq8005_send_packet;
+ dev->get_stats = seeq8005_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the fields of the device structure with ethernet values. */
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST;
+
+ return 0;
+}
+
+
+/* Open/initialize the board. This is called (in the current kernel)
+ sometime after booting when the 'ifconfig' program is run.
+
+ This routine should set everything up anew at each open, even
+ registers that "should" only need to be set once at boot, so that
+ there is non-reboot way to recover if something goes wrong.
+ */
+static int
+seeq8005_open(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ {
+ int irqval = request_irq(dev->irq, &seeq8005_interrupt, 0, "seeq8005");
+ if (irqval) {
+ printk ("%s: unable to get IRQ %d (irqval=%d).\n", dev->name,
+ dev->irq, irqval);
+ return EAGAIN;
+ }
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Reset the hardware here. Don't forget to set the station address. */
+ seeq8005_init(dev, 1);
+
+ lp->open_time = jiffies;
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+ return 0;
+}
+
+static int
+seeq8005_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (dev->tbusy) {
+ /* If we get here, some higher level has decided we are broken.
+ There should really be a "kick me" function call instead. */
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 5)
+ return 1;
+ printk("%s: transmit timed out, %s?\n", dev->name,
+ tx_done(dev) ? "IRQ conflict" : "network cable problem");
+ /* Try to restart the adaptor. */
+ seeq8005_init(dev, 1);
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ }
+
+ /* If some higher layer thinks we've missed an tx-done interrupt
+ we are passed NULL. Caution: dev_tint() handles the cli()/sti()
+ itself. */
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ dev->trans_start = jiffies;
+ }
+ dev_kfree_skb (skb, FREE_WRITE);
+
+ /* You might need to clean up and record Tx statistics here. */
+
+ return 0;
+}
+
+/* The typical workload of the driver:
+ Handle the network interface interrupts. */
+static void
+seeq8005_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct net_local *lp;
+ int ioaddr, status, boguscount = 0;
+
+ if (dev == NULL) {
+ printk ("net_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ if (dev->interrupt)
+ printk ("%s: Re-entering the interrupt handler.\n", dev->name);
+ dev->interrupt = 1;
+
+ ioaddr = dev->base_addr;
+ lp = (struct net_local *)dev->priv;
+
+ status = inw(SEEQ_STATUS);
+ do {
+ if (net_debug >2) {
+ printk("%s: int, status=0x%04x\n",dev->name,status);
+ }
+
+ if (status & SEEQSTAT_WINDOW_INT) {
+ outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ if (net_debug) {
+ printk("%s: window int!\n",dev->name);
+ }
+ }
+ if (status & SEEQSTAT_TX_INT) {
+ outw( SEEQCMD_TX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ lp->stats.tx_packets++;
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+ if (status & SEEQSTAT_RX_INT) {
+ /* Got a packet(s). */
+ seeq8005_rx(dev);
+ }
+ status = inw(SEEQ_STATUS);
+ } while ( (++boguscount < 10) && (status & SEEQSTAT_ANY_INT)) ;
+
+ if(net_debug>2) {
+ printk("%s: eoi\n",dev->name);
+ }
+ dev->interrupt = 0;
+ return;
+}
+
+/* We have a good packet(s), get it/them out of the buffers. */
+static void
+seeq8005_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int boguscount = 10;
+ int pkt_hdr;
+ int ioaddr = dev->base_addr;
+
+ do {
+ int next_packet;
+ int pkt_len;
+ int i;
+ int status;
+
+ status = inw(SEEQ_STATUS);
+ outw( lp->receive_ptr, SEEQ_DMAAR);
+ outw(SEEQCMD_FIFO_READ | SEEQCMD_RX_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ wait_for_buffer(dev);
+ next_packet = ntohs(inw(SEEQ_BUFFER));
+ pkt_hdr = inw(SEEQ_BUFFER);
+
+ if (net_debug>2) {
+ printk("%s: 0x%04x recv next=0x%04x, hdr=0x%04x\n",dev->name,lp->receive_ptr,next_packet,pkt_hdr);
+ }
+
+ if ((next_packet == 0) || ((pkt_hdr & SEEQPKTH_CHAIN)==0)) { /* Read all the frames? */
+ return; /* Done for now */
+ }
+
+ if ((pkt_hdr & SEEQPKTS_DONE)==0)
+ break;
+
+ if (next_packet < lp->receive_ptr) {
+ pkt_len = (next_packet + 0x10000 - ((DEFAULT_TEA+1)<<8)) - lp->receive_ptr - 4;
+ } else {
+ pkt_len = next_packet - lp->receive_ptr - 4;
+ }
+
+ if (next_packet < ((DEFAULT_TEA+1)<<8)) { /* is the next_packet address sane? */
+ printk("%s: recv packet ring corrupt, resetting board\n",dev->name);
+ seeq8005_init(dev,1);
+ return;
+ }
+
+ lp->receive_ptr = next_packet;
+
+ if (net_debug>2) {
+ printk("%s: recv len=0x%04x\n",dev->name,pkt_len);
+ }
+
+ if (pkt_hdr & SEEQPKTS_ANY_ERROR) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (pkt_hdr & SEEQPKTS_SHORT) lp->stats.rx_frame_errors++;
+ if (pkt_hdr & SEEQPKTS_DRIB) lp->stats.rx_frame_errors++;
+ if (pkt_hdr & SEEQPKTS_OVERSIZE) lp->stats.rx_over_errors++;
+ if (pkt_hdr & SEEQPKTS_CRC_ERR) lp->stats.rx_crc_errors++;
+ /* skip over this packet */
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_DMA_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ outw( (lp->receive_ptr & 0xff00)>>8, SEEQ_REA);
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+ unsigned char *buf;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb, 2); /* align data on 16 byte */
+ buf = skb_put(skb,pkt_len);
+
+ insw(SEEQ_BUFFER, buf, (pkt_len + 1) >> 1);
+
+ if (net_debug>2) {
+ char * p = buf;
+ printk("%s: recv ",dev->name);
+ for(i=0;i<14;i++) {
+ printk("%02x ",*(p++)&0xff);
+ }
+ printk("\n");
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ } while ((--boguscount) && (pkt_hdr & SEEQPKTH_CHAIN));
+
+ /* If any worth-while packets have been received, netif_rx()
+ has done a mark_bh(NET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* The inverse routine to net_open(). */
+static int
+seeq8005_close(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ lp->open_time = 0;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /* Flush the Tx and disable Rx here. */
+ outw( SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+
+ free_irq(dev->irq);
+
+ irq2dev_map[dev->irq] = 0;
+
+ /* Update the statistics here. */
+
+ return 0;
+
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *
+seeq8005_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ num_addrs == -1 Promiscuous mode, receive all packets
+ num_addrs == 0 Normal mode, clear multicast list
+ num_addrs > 0 Multicast mode, receive normal and MC packets, and do
+ best-effort filtering.
+ */
+static void
+set_multicast_list(struct device *dev)
+{
+/*
+ * I _could_ do upto 6 addresses here, but wont (yet?)
+ */
+
+#if 0
+ int ioaddr = dev->base_addr;
+/*
+ * hmm, not even sure if my matching works _anyway_ - seem to be receiving
+ * _everything_ . . .
+ */
+
+ if (num_addrs) { /* Enable promiscuous mode */
+ outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_ALL, SEEQ_CFG1);
+ dev->flags|=IFF_PROMISC;
+ } else { /* Disable promiscuous mode, use normal mode */
+ outw( (inw(SEEQ_CFG1) & ~SEEQCFG1_MATCH_MASK)| SEEQCFG1_MATCH_BROAD, SEEQ_CFG1);
+ }
+#endif
+}
+
+void seeq8005_init(struct device *dev, int startp)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int i;
+
+ outw(SEEQCFG2_RESET, SEEQ_CFG2); /* reset device */
+ SLOW_DOWN_IO; /* have to wait 4us after a reset - should be fixed */
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+ SLOW_DOWN_IO;
+
+ outw( SEEQCMD_FIFO_WRITE | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ outw( 0, SEEQ_DMAAR); /* load start address into both low and high byte */
+/* wait_for_buffer(dev); */ /* I think that you only need a wait for memory buffer */
+ outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1);
+
+ for(i=0;i<6;i++) { /* set Station address */
+ outb(dev->dev_addr[i], SEEQ_BUFFER);
+ SLOW_DOWN_IO;
+ }
+
+ outw( SEEQCFG1_BUFFER_TEA, SEEQ_CFG1); /* set xmit end area pointer to 16K */
+ outb( DEFAULT_TEA, SEEQ_BUFFER); /* this gives us 16K of send buffer and 48K of recv buffer */
+
+ lp->receive_ptr = (DEFAULT_TEA+1)<<8; /* so we can find our packet_header */
+ outw( lp->receive_ptr, SEEQ_RPR); /* Receive Pointer Register is set to recv buffer memory */
+
+ outw( 0x00ff, SEEQ_REA); /* Receive Area End */
+
+ if (net_debug>4) {
+ printk("%s: SA0 = ",dev->name);
+
+ outw( SEEQCMD_FIFO_READ | SEEQCMD_SET_ALL_OFF, SEEQ_CMD);
+ outw( 0, SEEQ_DMAAR);
+ outw( SEEQCFG1_BUFFER_MAC0, SEEQ_CFG1);
+
+ for(i=0;i<6;i++) {
+ printk("%02x ",inb(SEEQ_BUFFER));
+ }
+ printk("\n");
+ }
+
+ outw( SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD | SEEQCFG1_BUFFER_BUFFER, SEEQ_CFG1);
+ outw( SEEQCFG2_AUTO_REA | SEEQCFG2_CTRLO, SEEQ_CFG2);
+ outw( SEEQCMD_SET_RX_ON | SEEQCMD_TX_INT_EN | SEEQCMD_RX_INT_EN, SEEQ_CMD);
+
+ if (net_debug>4) {
+ int old_cfg1;
+ old_cfg1 = inw(SEEQ_CFG1);
+ printk("%s: stat = 0x%04x\n",dev->name,inw(SEEQ_STATUS));
+ printk("%s: cfg1 = 0x%04x\n",dev->name,old_cfg1);
+ printk("%s: cfg2 = 0x%04x\n",dev->name,inw(SEEQ_CFG2));
+ printk("%s: raer = 0x%04x\n",dev->name,inw(SEEQ_REA));
+ printk("%s: dmaar= 0x%04x\n",dev->name,inw(SEEQ_DMAAR));
+
+ }
+}
+
+
+void hardware_send_packet(struct device * dev, char *buf, int length)
+{
+ int ioaddr = dev->base_addr;
+ int status = inw(SEEQ_STATUS);
+ int transmit_ptr = 0;
+ int tmp;
+
+ if (net_debug>4) {
+ printk("%s: send 0x%04x\n",dev->name,length);
+ }
+
+ /* Set FIFO to writemode and set packet-buffer address */
+ outw( SEEQCMD_FIFO_WRITE | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+ outw( transmit_ptr, SEEQ_DMAAR);
+
+ /* output SEEQ Packet header barfage */
+ outw( htons(length + 4), SEEQ_BUFFER);
+ outw( SEEQPKTH_XMIT | SEEQPKTH_DATA_FOLLOWS | SEEQPKTH_XMIT_INT_EN, SEEQ_BUFFER );
+
+ /* blat the buffer */
+ outsw( SEEQ_BUFFER, buf, (length +1) >> 1);
+ /* paranoia !! */
+ outw( 0, SEEQ_BUFFER);
+ outw( 0, SEEQ_BUFFER);
+
+ /* set address of start of transmit chain */
+ outw( transmit_ptr, SEEQ_TPR);
+
+ /* drain FIFO */
+ tmp = jiffies;
+ while ( (((status=inw(SEEQ_STATUS)) & SEEQSTAT_FIFO_EMPTY) == 0) && (jiffies < tmp + HZ))
+ mb();
+
+ /* doit ! */
+ outw( SEEQCMD_WINDOW_INT_ACK | SEEQCMD_SET_TX_ON | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+
+}
+
+
+/*
+ * wait_for_buffer
+ *
+ * This routine waits for the SEEQ chip to assert that the FIFO is ready
+ * by checking for a window interrupt, and then clearing it
+ */
+inline void wait_for_buffer(struct device * dev)
+{
+ int ioaddr = dev->base_addr;
+ int tmp;
+ int status;
+
+ tmp = jiffies + HZ;
+ while ( ( ((status=inw(SEEQ_STATUS)) & SEEQSTAT_WINDOW_INT) != SEEQSTAT_WINDOW_INT) && jiffies < tmp)
+ mb();
+
+ if ( (status & SEEQSTAT_WINDOW_INT) == SEEQSTAT_WINDOW_INT)
+ outw( SEEQCMD_WINDOW_INT_ACK | (status & SEEQCMD_INT_MASK), SEEQ_CMD);
+}
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c skeleton.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/seeq8005.h b/i386/i386at/gpl/linux/net/seeq8005.h
new file mode 100644
index 00000000..7122340c
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/seeq8005.h
@@ -0,0 +1,156 @@
+/*
+ * defines, etc for the seeq8005
+ */
+
+/*
+ * This file is distributed under GPL.
+ *
+ * This style and layout of this file is also copied
+ * from many of the other linux network device drivers.
+ */
+
+/* The number of low I/O ports used by the ethercard. */
+#define SEEQ8005_IO_EXTENT 16
+
+#define SEEQ_B (ioaddr)
+
+#define SEEQ_CMD (SEEQ_B) /* Write only */
+#define SEEQ_STATUS (SEEQ_B) /* Read only */
+#define SEEQ_CFG1 (SEEQ_B + 2)
+#define SEEQ_CFG2 (SEEQ_B + 4)
+#define SEEQ_REA (SEEQ_B + 6) /* Receive End Area Register */
+#define SEEQ_RPR (SEEQ_B + 10) /* Receive Pointer Register */
+#define SEEQ_TPR (SEEQ_B + 12) /* Transmit Pointer Register */
+#define SEEQ_DMAAR (SEEQ_B + 14) /* DMA Address Register */
+#define SEEQ_BUFFER (SEEQ_B + 8) /* Buffer Window Register */
+
+#define DEFAULT_TEA (0x3f)
+
+#define SEEQCMD_DMA_INT_EN (0x0001) /* DMA Interrupt Enable */
+#define SEEQCMD_RX_INT_EN (0x0002) /* Receive Interrupt Enable */
+#define SEEQCMD_TX_INT_EN (0x0004) /* Transmit Interrupt Enable */
+#define SEEQCMD_WINDOW_INT_EN (0x0008) /* What the hell is this for?? */
+#define SEEQCMD_INT_MASK (0x000f)
+
+#define SEEQCMD_DMA_INT_ACK (0x0010) /* DMA ack */
+#define SEEQCMD_RX_INT_ACK (0x0020)
+#define SEEQCMD_TX_INT_ACK (0x0040)
+#define SEEQCMD_WINDOW_INT_ACK (0x0080)
+#define SEEQCMD_ACK_ALL (0x00f0)
+
+#define SEEQCMD_SET_DMA_ON (0x0100) /* Enables DMA Request logic */
+#define SEEQCMD_SET_RX_ON (0x0200) /* Enables Packet RX */
+#define SEEQCMD_SET_TX_ON (0x0400) /* Starts TX run */
+#define SEEQCMD_SET_DMA_OFF (0x0800)
+#define SEEQCMD_SET_RX_OFF (0x1000)
+#define SEEQCMD_SET_TX_OFF (0x2000)
+#define SEEQCMD_SET_ALL_OFF (0x3800) /* set all logic off */
+
+#define SEEQCMD_FIFO_READ (0x4000) /* Set FIFO to read mode (read from Buffer) */
+#define SEEQCMD_FIFO_WRITE (0x8000) /* Set FIFO to write mode */
+
+#define SEEQSTAT_DMA_INT_EN (0x0001) /* Status of interrupt enable */
+#define SEEQSTAT_RX_INT_EN (0x0002)
+#define SEEQSTAT_TX_INT_EN (0x0004)
+#define SEEQSTAT_WINDOW_INT_EN (0x0008)
+
+#define SEEQSTAT_DMA_INT (0x0010) /* Interrupt flagged */
+#define SEEQSTAT_RX_INT (0x0020)
+#define SEEQSTAT_TX_INT (0x0040)
+#define SEEQSTAT_WINDOW_INT (0x0080)
+#define SEEQSTAT_ANY_INT (0x00f0)
+
+#define SEEQSTAT_DMA_ON (0x0100) /* DMA logic on */
+#define SEEQSTAT_RX_ON (0x0200) /* Packet RX on */
+#define SEEQSTAT_TX_ON (0x0400) /* TX running */
+
+#define SEEQSTAT_FIFO_FULL (0x2000)
+#define SEEQSTAT_FIFO_EMPTY (0x4000)
+#define SEEQSTAT_FIFO_DIR (0x8000) /* 1=read, 0=write */
+
+#define SEEQCFG1_BUFFER_MASK (0x000f) /* define what mapps into the BUFFER register */
+#define SEEQCFG1_BUFFER_MAC0 (0x0000) /* MAC station addresses 0-5 */
+#define SEEQCFG1_BUFFER_MAC1 (0x0001)
+#define SEEQCFG1_BUFFER_MAC2 (0x0002)
+#define SEEQCFG1_BUFFER_MAC3 (0x0003)
+#define SEEQCFG1_BUFFER_MAC4 (0x0004)
+#define SEEQCFG1_BUFFER_MAC5 (0x0005)
+#define SEEQCFG1_BUFFER_PROM (0x0006) /* The Address/CFG PROM */
+#define SEEQCFG1_BUFFER_TEA (0x0007) /* Transmit end area */
+#define SEEQCFG1_BUFFER_BUFFER (0x0008) /* Packet buffer memory */
+#define SEEQCFG1_BUFFER_INT_VEC (0x0009) /* Interrupt Vector */
+
+#define SEEQCFG1_DMA_INTVL_MASK (0x0030)
+#define SEEQCFG1_DMA_CONT (0x0000)
+#define SEEQCFG1_DMA_800ns (0x0010)
+#define SEEQCFG1_DMA_1600ns (0x0020)
+#define SEEQCFG1_DMA_3200ns (0x0030)
+
+#define SEEQCFG1_DMA_LEN_MASK (0x00c0)
+#define SEEQCFG1_DMA_LEN1 (0x0000)
+#define SEEQCFG1_DMA_LEN2 (0x0040)
+#define SEEQCFG1_DMA_LEN4 (0x0080)
+#define SEEQCFG1_DMA_LEN8 (0x00c0)
+
+#define SEEQCFG1_MAC_MASK (0x3f00) /* Dis/enable bits for MAC addresses */
+#define SEEQCFG1_MAC0_EN (0x0100)
+#define SEEQCFG1_MAC1_EN (0x0200)
+#define SEEQCFG1_MAC2_EN (0x0400)
+#define SEEQCFG1_MAC3_EN (0x0800)
+#define SEEQCFG1_MAC4_EN (0x1000)
+#define SEEQCFG1_MAC5_EN (0x2000)
+
+#define SEEQCFG1_MATCH_MASK (0xc000) /* Packet matching logic cfg bits */
+#define SEEQCFG1_MATCH_SPECIFIC (0x0000) /* only matching MAC addresses */
+#define SEEQCFG1_MATCH_BROAD (0x4000) /* matching and broadcast addresses */
+#define SEEQCFG1_MATCH_MULTI (0x8000) /* matching, broadcast and multicast */
+#define SEEQCFG1_MATCH_ALL (0xc000) /* Promiscuous mode */
+
+#define SEEQCFG1_DEFAULT (SEEQCFG1_BUFFER_BUFFER | SEEQCFG1_MAC0_EN | SEEQCFG1_MATCH_BROAD)
+
+#define SEEQCFG2_BYTE_SWAP (0x0001) /* 0=Intel byte-order */
+#define SEEQCFG2_AUTO_REA (0x0002) /* if set, Receive End Area will be updated when reading from Buffer */
+
+#define SEEQCFG2_CRC_ERR_EN (0x0008) /* enables receiving of packets with CRC errors */
+#define SEEQCFG2_DRIBBLE_EN (0x0010) /* enables receiving of non-aligned packets */
+#define SEEQCFG2_SHORT_EN (0x0020) /* enables receiving of short packets */
+
+#define SEEQCFG2_SLOTSEL (0x0040) /* 0= standard IEEE802.3, 1= smaller,faster, non-standard */
+#define SEEQCFG2_NO_PREAM (0x0080) /* 1= user supplies Xmit preamble bytes */
+#define SEEQCFG2_ADDR_LEN (0x0100) /* 1= 2byte addresses */
+#define SEEQCFG2_REC_CRC (0x0200) /* 0= received packets will have CRC stripped from them */
+#define SEEQCFG2_XMIT_NO_CRC (0x0400) /* dont xmit CRC with each packet (user supplies it) */
+#define SEEQCFG2_LOOPBACK (0x0800)
+#define SEEQCFG2_CTRLO (0x1000)
+#define SEEQCFG2_RESET (0x8000) /* software Hard-reset bit */
+
+struct seeq_pkt_hdr {
+ unsigned short next; /* address of next packet header */
+ unsigned char babble_int:1, /* enable int on >1514 byte packet */
+ coll_int:1, /* enable int on collision */
+ coll_16_int:1, /* enable int on >15 collision */
+ xmit_int:1, /* enable int on success (or xmit with <15 collision) */
+ unused:1,
+ data_follows:1, /* if not set, process this as a header and pointer only */
+ chain_cont:1, /* if set, more headers in chain only cmd bit valid in recv header */
+ xmit_recv:1; /* if set, a xmit packet, else a receive packet.*/
+ unsigned char status;
+};
+
+#define SEEQPKTH_BAB_INT_EN (0x01) /* xmit only */
+#define SEEQPKTH_COL_INT_EN (0x02) /* xmit only */
+#define SEEQPKTH_COL16_INT_EN (0x04) /* xmit only */
+#define SEEQPKTH_XMIT_INT_EN (0x08) /* xmit only */
+#define SEEQPKTH_DATA_FOLLOWS (0x20) /* supposedly in xmit only */
+#define SEEQPKTH_CHAIN (0x40) /* more headers follow */
+#define SEEQPKTH_XMIT (0x80)
+
+#define SEEQPKTS_BABBLE (0x0100) /* xmit only */
+#define SEEQPKTS_OVERSIZE (0x0100) /* recv only */
+#define SEEQPKTS_COLLISION (0x0200) /* xmit only */
+#define SEEQPKTS_CRC_ERR (0x0200) /* recv only */
+#define SEEQPKTS_COLL16 (0x0400) /* xmit only */
+#define SEEQPKTS_DRIB (0x0400) /* recv only */
+#define SEEQPKTS_SHORT (0x0800) /* recv only */
+#define SEEQPKTS_DONE (0x8000)
+#define SEEQPKTS_ANY_ERROR (0x0f00)
diff --git a/i386/i386at/gpl/linux/net/sk_g16.c b/i386/i386at/gpl/linux/net/sk_g16.c
new file mode 100644
index 00000000..83989485
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/sk_g16.c
@@ -0,0 +1,2111 @@
+/*-
+ * Copyright (C) 1994 by PJD Weichmann & SWS Bern, Switzerland
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.c
+ *
+ * Version : $Revision: 1.1.1.1 $
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/26
+ * Last Updated : $Date: 1997/02/25 21:27:39 $
+ *
+ * Description : Schneider & Koch G16 Ethernet Device Driver for
+ * Linux Kernel >= 1.1.22
+ * Update History :
+ *
+-*/
+
+static const char *rcsid = "$Id: sk_g16.c,v 1.1.1.1 1997/02/25 21:27:39 thomas Exp $";
+
+/*
+ * The Schneider & Koch (SK) G16 Network device driver is based
+ * on the 'ni6510' driver from Michael Hipp which can be found at
+ * ftp://sunsite.unc.edu/pub/Linux/system/Network/drivers/nidrivers.tar.gz
+ *
+ * Sources: 1) ni6510.c by M. Hipp
+ * 2) depca.c by D.C. Davies
+ * 3) skeleton.c by D. Becker
+ * 4) Am7990 Local Area Network Controller for Ethernet (LANCE),
+ * AMD, Pub. #05698, June 1989
+ *
+ * Many Thanks for helping me to get things working to:
+ *
+ * A. Cox (A.Cox@swansea.ac.uk)
+ * M. Hipp (mhipp@student.uni-tuebingen.de)
+ * R. Bolz (Schneider & Koch, Germany)
+ *
+ * See README.sk_g16 for details about limitations and bugs for the
+ * current version.
+ *
+ * To Do:
+ * - Support of SK_G8 and other SK Network Cards.
+ * - Autoset memory mapped RAM. Check for free memory and then
+ * configure RAM correctly.
+ * - SK_close should really set card in to initial state.
+ * - Test if IRQ 3 is not switched off. Use autoirq() functionality.
+ * (as in /drivers/net/skeleton.c)
+ * - Implement Multicast addressing. At minimum something like
+ * in depca.c.
+ * - Redo the statistics part.
+ * - Try to find out if the board is in 8 Bit or 16 Bit slot.
+ * If in 8 Bit mode don't use IRQ 11.
+ * - (Try to make it slightly faster.)
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/ptrace.h>
+#include <linux/fcntl.h>
+#include <linux/ioport.h>
+#include <linux/interrupt.h>
+#include <linux/malloc.h>
+#include <linux/ioport.h>
+#include <linux/string.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <linux/errno.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+#include "sk_g16.h"
+
+/*
+ * Schneider & Koch Card Definitions
+ * =================================
+ */
+
+#define SK_NAME "SK_G16"
+
+/*
+ * SK_G16 Configuration
+ * --------------------
+ */
+
+/*
+ * Abbreviations
+ * -------------
+ *
+ * RAM - used for the 16KB shared memory
+ * Boot_ROM, ROM - are used for referencing the BootEPROM
+ *
+ * SK_BOOT_ROM and SK_ADDR are symbolic constants used to configure
+ * the behaviour of the driver and the SK_G16.
+ *
+ * ! See sk_g16.install on how to install and configure the driver !
+ *
+ * SK_BOOT_ROM defines if the Boot_ROM should be switched off or not.
+ *
+ * SK_ADDR defines the address where the RAM will be mapped into the real
+ * host memory.
+ * valid addresses are from 0xa0000 to 0xfc000 in 16Kbyte steps.
+ */
+
+#define SK_BOOT_ROM 1 /* 1=BootROM on 0=off */
+
+#define SK_ADDR 0xcc000
+
+/*
+ * In POS3 are bits A14-A19 of the address bus. These bits can be set
+ * to choose the RAM address. Thats why we only can choose the RAM address
+ * in 16KB steps.
+ */
+
+#define POS_ADDR (rom_addr>>14) /* Do not change this line */
+
+/*
+ * SK_G16 I/O PORT's + IRQ's + Boot_ROM locations
+ * ----------------------------------------------
+ */
+
+/*
+ * As nearly every card has also SK_G16 a specified I/O Port region and
+ * only a few possible IRQ's.
+ * In the Installation Guide from Schneider & Koch is listed a possible
+ * Interrupt IRQ2. IRQ2 is always IRQ9 in boards with two cascaded interrupt
+ * controllers. So we use in SK_IRQS IRQ9.
+ */
+
+/* Don't touch any of the following #defines. */
+
+#define SK_IO_PORTS { 0x100, 0x180, 0x208, 0x220, 0x288, 0x320, 0x328, 0x390, 0 }
+
+#define SK_IRQS { 3, 5, 9, 11, 0 }
+
+#define SK_BOOT_ROM_LOCATIONS { 0xc0000, 0xc4000, 0xc8000, 0xcc000, 0xd0000, 0xd4000, 0xd8000, 0xdc000, 0 }
+
+#define SK_BOOT_ROM_ID { 0x55, 0xaa, 0x10, 0x50, 0x06, 0x33 }
+
+/*
+ * SK_G16 POS REGISTERS
+ * --------------------
+ */
+
+/*
+ * SK_G16 has a Programmable Option Select (POS) Register.
+ * The POS is composed of 8 separate registers (POS0-7) which
+ * are I/O mapped on an address set by the W1 switch.
+ *
+ */
+
+#define SK_POS_SIZE 8 /* 8 I/O Ports are used by SK_G16 */
+
+#define SK_POS0 ioaddr /* Card-ID Low (R) */
+#define SK_POS1 ioaddr+1 /* Card-ID High (R) */
+#define SK_POS2 ioaddr+2 /* Card-Enable, Boot-ROM Disable (RW) */
+#define SK_POS3 ioaddr+3 /* Base address of RAM */
+#define SK_POS4 ioaddr+4 /* IRQ */
+
+/* POS5 - POS7 are unused */
+
+/*
+ * SK_G16 MAC PREFIX
+ * -----------------
+ */
+
+/*
+ * Scheider & Koch manufacturer code (00:00:a5).
+ * This must be checked, that we are sure it is a SK card.
+ */
+
+#define SK_MAC0 0x00
+#define SK_MAC1 0x00
+#define SK_MAC2 0x5a
+
+/*
+ * SK_G16 ID
+ * ---------
+ */
+
+/*
+ * If POS0,POS1 contain the following ID, then we know
+ * at which I/O Port Address we are.
+ */
+
+#define SK_IDLOW 0xfd
+#define SK_IDHIGH 0x6a
+
+
+/*
+ * LANCE POS Bit definitions
+ * -------------------------
+ */
+
+#define SK_ROM_RAM_ON (POS2_CARD)
+#define SK_ROM_RAM_OFF (POS2_EPROM)
+#define SK_ROM_ON (inb(SK_POS2) & POS2_CARD)
+#define SK_ROM_OFF (inb(SK_POS2) | POS2_EPROM)
+#define SK_RAM_ON (inb(SK_POS2) | POS2_CARD)
+#define SK_RAM_OFF (inb(SK_POS2) & POS2_EPROM)
+
+#define POS2_CARD 0x0001 /* 1 = SK_G16 on 0 = off */
+#define POS2_EPROM 0x0002 /* 1 = Boot EPROM off 0 = on */
+
+/*
+ * SK_G16 Memory mapped Registers
+ * ------------------------------
+ *
+ */
+
+#define SK_IOREG (board->ioreg) /* LANCE data registers. */
+#define SK_PORT (board->port) /* Control, Status register */
+#define SK_IOCOM (board->iocom) /* I/O Command */
+
+/*
+ * SK_G16 Status/Control Register bits
+ * -----------------------------------
+ *
+ * (C) Controlreg (S) Statusreg
+ */
+
+/*
+ * Register transfer: 0 = no transfer
+ * 1 = transferring data between LANCE and I/O reg
+ */
+#define SK_IORUN 0x20
+
+/*
+ * LANCE interrupt: 0 = LANCE interrupt occurred
+ * 1 = no LANCE interrupt occurred
+ */
+#define SK_IRQ 0x10
+
+#define SK_RESET 0x08 /* Reset SK_CARD: 0 = RESET 1 = normal */
+#define SK_RW 0x02 /* 0 = write to 1 = read from */
+#define SK_ADR 0x01 /* 0 = REG DataPort 1 = RAP Reg addr port */
+
+
+#define SK_RREG SK_RW /* Transferdirection to read from lance */
+#define SK_WREG 0 /* Transferdirection to write to lance */
+#define SK_RAP SK_ADR /* Destination Register RAP */
+#define SK_RDATA 0 /* Destination Register REG DataPort */
+
+/*
+ * SK_G16 I/O Command
+ * ------------------
+ */
+
+/*
+ * Any bitcombination sets the internal I/O bit (transfer will start)
+ * when written to I/O Command
+ */
+
+#define SK_DOIO 0x80 /* Do Transfer */
+
+/*
+ * LANCE RAP (Register Address Port).
+ * ---------------------------------
+ */
+
+/*
+ * The LANCE internal registers are selected through the RAP.
+ * The Registers are:
+ *
+ * CSR0 - Status and Control flags
+ * CSR1 - Low order bits of initialize block (bits 15:00)
+ * CSR2 - High order bits of initialize block (bits 07:00, 15:08 are reserved)
+ * CSR3 - Allows redefinition of the Bus Master Interface.
+ * This register must be set to 0x0002, which means BSWAP = 0,
+ * ACON = 1, BCON = 0;
+ *
+ */
+
+#define CSR0 0x00
+#define CSR1 0x01
+#define CSR2 0x02
+#define CSR3 0x03
+
+/*
+ * General Definitions
+ * ===================
+ */
+
+/*
+ * Set the number of Tx and Rx buffers, using Log_2(# buffers).
+ * We have 16KB RAM which can be accessed by the LANCE. In the
+ * memory are not only the buffers but also the ring descriptors and
+ * the initialize block.
+ * Don't change anything unless you really know what you do.
+ */
+
+#define LC_LOG_TX_BUFFERS 1 /* (2 == 2^^1) 2 Transmit buffers */
+#define LC_LOG_RX_BUFFERS 3 /* (8 == 2^^3) 8 Receive buffers */
+
+/* Descriptor ring sizes */
+
+#define TMDNUM (1 << (LC_LOG_TX_BUFFERS)) /* 2 Transmit descriptor rings */
+#define RMDNUM (1 << (LC_LOG_RX_BUFFERS)) /* 8 Receive Buffers */
+
+/* Define Mask for setting RMD, TMD length in the LANCE init_block */
+
+#define TMDNUMMASK (LC_LOG_TX_BUFFERS << 29)
+#define RMDNUMMASK (LC_LOG_RX_BUFFERS << 29)
+
+/*
+ * Data Buffer size is set to maximum packet length.
+ */
+
+#define PKT_BUF_SZ 1518
+
+/*
+ * The number of low I/O ports used by the ethercard.
+ */
+
+#define ETHERCARD_TOTAL_SIZE SK_POS_SIZE
+
+/*
+ * Portreserve is there to mark the Card I/O Port region as used.
+ * Check_region is to check if the region at ioaddr with the size "size"
+ * is free or not.
+ * Snarf_region allocates the I/O Port region.
+ */
+
+#ifndef HAVE_PORTRESERVE
+
+#define check_region(ioaddr, size) 0
+#define request_region(ioaddr, size,name) do ; while (0)
+
+#endif
+
+/*
+ * SK_DEBUG
+ *
+ * Here you can choose what level of debugging wanted.
+ *
+ * If SK_DEBUG and SK_DEBUG2 are undefined, then only the
+ * necessary messages will be printed.
+ *
+ * If SK_DEBUG is defined, there will be many debugging prints
+ * which can help to find some mistakes in configuration or even
+ * in the driver code.
+ *
+ * If SK_DEBUG2 is defined, many many messages will be printed
+ * which normally you don't need. I used this to check the interrupt
+ * routine.
+ *
+ * (If you define only SK_DEBUG2 then only the messages for
+ * checking interrupts will be printed!)
+ *
+ * Normal way of live is:
+ *
+ * For the whole thing get going let both symbolic constants
+ * undefined. If you face any problems and you know what's going
+ * on (you know something about the card and you can interpret some
+ * hex LANCE register output) then define SK_DEBUG
+ *
+ */
+
+#undef SK_DEBUG /* debugging */
+#undef SK_DEBUG2 /* debugging with more verbose report */
+
+#ifdef SK_DEBUG
+#define PRINTK(x) printk x
+#else
+#define PRINTK(x) /**/
+#endif
+
+#ifdef SK_DEBUG2
+#define PRINTK2(x) printk x
+#else
+#define PRINTK2(x) /**/
+#endif
+
+/*
+ * SK_G16 RAM
+ *
+ * The components are memory mapped and can be set in a region from
+ * 0x00000 through 0xfc000 in 16KB steps.
+ *
+ * The Network components are: dual ported RAM, Prom, I/O Reg, Status-,
+ * Controlregister and I/O Command.
+ *
+ * dual ported RAM: This is the only memory region which the LANCE chip
+ * has access to. From the Lance it is addressed from 0x0000 to
+ * 0x3fbf. The host accesses it normally.
+ *
+ * PROM: The PROM obtains the ETHERNET-MAC-Address. It is realised as a
+ * 8-Bit PROM, this means only the 16 even addresses are used of the
+ * 32 Byte Address region. Access to a odd address results in invalid
+ * data.
+ *
+ * LANCE I/O Reg: The I/O Reg is build of 4 single Registers, Low-Byte Write,
+ * Hi-Byte Write, Low-Byte Read, Hi-Byte Read.
+ * Transfer from or to the LANCE is always in 16Bit so Low and High
+ * registers are always relevant.
+ *
+ * The Data from the Readregister is not the data in the Writeregister!!
+ *
+ * Port: Status- and Controlregister.
+ * Two different registers which share the same address, Status is
+ * read-only, Control is write-only.
+ *
+ * I/O Command:
+ * Any bitcombination written in here starts the transmission between
+ * Host and LANCE.
+ */
+
+typedef struct
+{
+ unsigned char ram[0x3fc0]; /* 16KB dual ported ram */
+ unsigned char rom[0x0020]; /* 32Byte PROM containing 6Byte MAC */
+ unsigned char res1[0x0010]; /* reserved */
+ unsigned volatile short ioreg;/* LANCE I/O Register */
+ unsigned volatile char port; /* Statusregister and Controlregister */
+ unsigned char iocom; /* I/O Command Register */
+} SK_RAM;
+
+/* struct */
+
+/*
+ * This is the structure for the dual ported ram. We
+ * have exactly 16 320 Bytes. In here there must be:
+ *
+ * - Initialize Block (starting at a word boundary)
+ * - Receive and Transmit Descriptor Rings (quadword boundary)
+ * - Data Buffers (arbitrary boundary)
+ *
+ * This is because LANCE has on SK_G16 only access to the dual ported
+ * RAM and nowhere else.
+ */
+
+struct SK_ram
+{
+ struct init_block ib;
+ struct tmd tmde[TMDNUM];
+ struct rmd rmde[RMDNUM];
+ char tmdbuf[TMDNUM][PKT_BUF_SZ];
+ char rmdbuf[RMDNUM][PKT_BUF_SZ];
+};
+
+/*
+ * Structure where all necessary information is for ring buffer
+ * management and statistics.
+ */
+
+struct priv
+{
+ struct SK_ram *ram; /* dual ported ram structure */
+ struct rmd *rmdhead; /* start of receive ring descriptors */
+ struct tmd *tmdhead; /* start of transmit ring descriptors */
+ int rmdnum; /* actual used ring descriptor */
+ int tmdnum; /* actual transmit descriptor for transmitting data */
+ int tmdlast; /* last sent descriptor used for error handling, etc */
+ void *rmdbufs[RMDNUM]; /* pointer to the receive buffers */
+ void *tmdbufs[TMDNUM]; /* pointer to the transmit buffers */
+ struct enet_statistics stats; /* Device driver statistics */
+};
+
+/* global variable declaration */
+
+/* IRQ map used to reserve a IRQ (see SK_open()) */
+
+/* extern void *irq2dev_map[16]; */ /* Declared in <linux/ioport.h> */
+
+/* static variables */
+
+static SK_RAM *board; /* pointer to our memory mapped board components */
+
+/* Macros */
+
+
+/* Function Prototypes */
+
+/*
+ * Device Driver functions
+ * -----------------------
+ * See for short explanation of each function its definitions header.
+ */
+
+int SK_init(struct device *dev);
+static int SK_probe(struct device *dev, short ioaddr);
+
+static int SK_open(struct device *dev);
+static int SK_send_packet(struct sk_buff *skb, struct device *dev);
+static void SK_interrupt(int irq, struct pt_regs * regs);
+static void SK_rxintr(struct device *dev);
+static void SK_txintr(struct device *dev);
+static int SK_close(struct device *dev);
+
+static struct enet_statistics *SK_get_stats(struct device *dev);
+
+unsigned int SK_rom_addr(void);
+
+static void set_multicast_list(struct device *dev);
+
+/*
+ * LANCE Functions
+ * ---------------
+ */
+
+static int SK_lance_init(struct device *dev, unsigned short mode);
+void SK_reset_board(void);
+void SK_set_RAP(int reg_number);
+int SK_read_reg(int reg_number);
+int SK_rread_reg(void);
+void SK_write_reg(int reg_number, int value);
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+void SK_print_pos(struct device *dev, char *text);
+void SK_print_dev(struct device *dev, char *text);
+void SK_print_ram(struct device *dev);
+
+
+/*-
+ * Function : SK_init
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Check for a SK_G16 network adaptor and initialize it.
+ * This function gets called by dev_init which initializes
+ * all Network devices.
+ *
+ * Parameters : I : struct device *dev - structure preconfigured
+ * from Space.c
+ * Return Value : 0 = Driver Found and initialized
+ * Errors : ENODEV - no device found
+ * ENXIO - not probed
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+/*
+ * Check for a network adaptor of this type, and return '0' if one exists.
+ * If dev->base_addr == 0, probe all likely locations.
+ * If dev->base_addr == 1, always return failure.
+ * If dev->base_addr == 2, allocate space for the device and return success
+ * (detachable devices only).
+ */
+
+int SK_init(struct device *dev)
+{
+ int ioaddr = 0; /* I/O port address used for POS regs */
+ int *port, ports[] = SK_IO_PORTS; /* SK_G16 supported ports */
+
+ /* get preconfigured base_addr from dev which is done in Space.c */
+ int base_addr = dev->base_addr;
+
+ PRINTK(("%s: %s", SK_NAME, rcsid));
+ rcsid = NULL; /* We do not want to use this further */
+
+ if (base_addr > 0x0ff) /* Check a single specified address */
+ {
+ /* Check if on specified address is a SK_G16 */
+
+ if ( (inb(SK_POS0) == SK_IDLOW) ||
+ (inb(SK_POS1) == SK_IDHIGH) )
+ {
+ return SK_probe(dev, base_addr);
+ }
+
+ return ENODEV; /* Sorry, but on specified address NO SK_G16 */
+ }
+ else if (base_addr > 0) /* Don't probe at all */
+ {
+ return ENXIO;
+ }
+
+ /* Autoprobe base_addr */
+
+ for (port = &ports[0]; *port; port++)
+ {
+ ioaddr = *port; /* we need ioaddr for accessing POS regs */
+
+ /* Check if I/O Port region is used by another board */
+
+ if (check_region(ioaddr, ETHERCARD_TOTAL_SIZE))
+ {
+ continue; /* Try next Port address */
+ }
+
+ /* Check if at ioaddr is a SK_G16 */
+
+ if ( !(inb(SK_POS0) == SK_IDLOW) ||
+ !(inb(SK_POS1) == SK_IDHIGH) )
+ {
+ continue; /* Try next Port address */
+ }
+
+ dev->base_addr = ioaddr; /* Set I/O Port Address */
+
+ if (SK_probe(dev, ioaddr) == 0)
+ {
+ return 0; /* Card found and initialized */
+ }
+ }
+
+ dev->base_addr = base_addr; /* Write back original base_addr */
+
+ return ENODEV; /* Failed to find or init driver */
+
+} /* End of SK_init */
+
+
+/*-
+ * Function : SK_probe
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function is called by SK_init and
+ * does the main part of initialization.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : short ioaddr - I/O Port address where POS is.
+ * Return Value : 0 = Initialization done
+ * Errors : ENODEV - No SK_G16 found
+ * -1 - Configuration problem
+ * Globals : irq2dev_map - Which device uses which IRQ
+ * : board - pointer to SK_RAM
+ * Update History :
+ * YY/MM/DD uid Description
+ * 94/06/30 pwe SK_ADDR now checked and at the correct place
+-*/
+
+int SK_probe(struct device *dev, short ioaddr)
+{
+ int i,j; /* Counters */
+ int sk_addr_flag = 0; /* SK ADDR correct? 1 - no, 0 - yes */
+ unsigned int rom_addr; /* used to store RAM address used for POS_ADDR */
+
+ struct priv *p; /* SK_G16 private structure */
+
+ if (SK_ADDR & 0x3fff || SK_ADDR < 0xa0000)
+ {
+
+ sk_addr_flag = 1;
+
+ /*
+ * Now here we could use a routine which searches for a free
+ * place in the ram and set SK_ADDR if found. TODO.
+ */
+ }
+
+ if (SK_BOOT_ROM) /* Shall we keep Boot_ROM on ? */
+ {
+ PRINTK(("## %s: SK_BOOT_ROM is set.\n", SK_NAME));
+
+ rom_addr = SK_rom_addr();
+
+ if (rom_addr == 0) /* No Boot_ROM found */
+ {
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR; /* assign predefined address */
+
+ PRINTK(("## %s: NO Bootrom found \n", SK_NAME));
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+ else if (rom_addr == SK_ADDR)
+ {
+ printk("%s: RAM + ROM are set to the same address %#08x\n"
+ " Check configuration. Now switching off Boot_ROM\n",
+ SK_NAME, rom_addr);
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off*/
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+ else
+ {
+ PRINTK(("## %s: Found ROM at %#08x\n", SK_NAME, rom_addr));
+ PRINTK(("## %s: Keeping Boot_ROM on\n", SK_NAME));
+
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ outb(SK_ROM_RAM_ON, SK_POS2); /* RAM on, BOOT_ROM on */
+ }
+ }
+ else /* Don't keep Boot_ROM */
+ {
+ PRINTK(("## %s: SK_BOOT_ROM is not set.\n", SK_NAME));
+
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_rom_addr(); /* Try to find a Boot_ROM */
+
+ /* IF we find a Boot_ROM disable it */
+
+ outb(SK_ROM_RAM_OFF, SK_POS2); /* Boot_ROM + RAM off */
+
+ /* We found a Boot_ROM and it's gone. Set RAM address on
+ * Boot_ROM address.
+ */
+
+ if (rom_addr)
+ {
+ printk("%s: We found Boot_ROM at %#08x. Now setting RAM on"
+ "that address\n", SK_NAME, rom_addr);
+
+ outb(POS_ADDR, SK_POS3); /* Set RAM on Boot_ROM address */
+ }
+ else /* We did not find a Boot_ROM, use predefined SK_ADDR for ram */
+ {
+ if (sk_addr_flag) /* No or Invalid SK_ADDR is defined */
+ {
+ printk("%s: SK_ADDR %#08x is not valid. Check configuration.\n",
+ dev->name, SK_ADDR);
+ return -1;
+ }
+
+ rom_addr = SK_ADDR;
+
+ outb(POS_ADDR, SK_POS3); /* Set RAM address */
+ }
+ outb(SK_RAM_ON, SK_POS2); /* enable RAM */
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "POS registers after ROM, RAM config");
+#endif
+
+ board = (SK_RAM *) rom_addr;
+
+ /* Read in station address */
+ for (i = 0, j = 0; i < ETH_ALEN; i++, j+=2)
+ {
+ dev->dev_addr[i] = board->rom[j];
+ }
+
+ /* Check for manufacturer code */
+ if (!(dev->dev_addr[0] == SK_MAC0 &&
+ dev->dev_addr[1] == SK_MAC1 &&
+ dev->dev_addr[2] == SK_MAC2) )
+ {
+ PRINTK(("## %s: We did not find SK_G16 at RAM location.\n",
+ SK_NAME));
+ return ENODEV; /* NO SK_G16 found */
+ }
+
+ printk("%s: %s found at %#3x, HW addr: %#04x:%02x:%02x:%02x:%02x:%02x\n",
+ dev->name,
+ "Schneider & Koch Netcard",
+ (unsigned int) dev->base_addr,
+ dev->dev_addr[0],
+ dev->dev_addr[1],
+ dev->dev_addr[2],
+ dev->dev_addr[3],
+ dev->dev_addr[4],
+ dev->dev_addr[5]);
+
+ /* Grab the I/O Port region */
+ request_region(ioaddr, ETHERCARD_TOTAL_SIZE,"sk_g16");
+
+ /* Initialize device structure */
+
+ /* Allocate memory for private structure */
+ p = dev->priv = (void *) kmalloc(sizeof(struct priv), GFP_KERNEL);
+ if (p == NULL)
+ return -ENOMEM;
+ memset((char *) dev->priv, 0, sizeof(struct priv)); /* clear memory */
+
+ /* Assign our Device Driver functions */
+
+ dev->open = &SK_open;
+ dev->stop = &SK_close;
+ dev->hard_start_xmit = &SK_send_packet;
+ dev->get_stats = &SK_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+
+ /* Set the generic fields of the device structure */
+
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST;
+
+ /* Initialize private structure */
+
+ p->ram = (struct SK_ram *) rom_addr; /* Set dual ported RAM addr */
+ p->tmdhead = &(p->ram)->tmde[0]; /* Set TMD head */
+ p->rmdhead = &(p->ram)->rmde[0]; /* Set RMD head */
+
+ /* Initialize buffer pointers */
+
+ for (i = 0; i < TMDNUM; i++)
+ {
+ p->tmdbufs[i] = &(p->ram)->tmdbuf[i];
+ }
+
+ for (i = 0; i < RMDNUM; i++)
+ {
+ p->rmdbufs[i] = &(p->ram)->rmdbuf[i];
+ }
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "End of SK_probe");
+ SK_print_ram(dev);
+#endif
+
+ return 0; /* Initialization done */
+
+} /* End of SK_probe() */
+
+
+/*-
+ * Function : SK_open
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function is called sometimes after booting
+ * when ifconfig program is run.
+ *
+ * This function requests an IRQ, sets the correct
+ * IRQ in the card. Then calls SK_lance_init() to
+ * init and start the LANCE chip. Then if everything is
+ * ok returns with 0 (OK), which means SK_G16 is now
+ * opened and operational.
+ *
+ * (Called by dev_open() /net/inet/dev.c)
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : 0 - Device opened
+ * Errors : -EAGAIN - Open failed
+ * Globals : irq2dev_map - which device uses which irq
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_open(struct device *dev)
+{
+ int i = 0;
+ int irqval = 0;
+ int ioaddr = dev->base_addr;
+
+ int irqtab[] = SK_IRQS;
+
+ struct priv *p = (struct priv *)dev->priv;
+
+ PRINTK(("## %s: At beginning of SK_open(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ if (dev->irq == 0) /* Autoirq */
+ {
+ i = 0;
+
+ /*
+ * Check if one IRQ out of SK_IRQS is free and install
+ * interrupt handler.
+ * Most done by request_irq().
+ * irqval: 0 - interrupt handler installed for IRQ irqtab[i]
+ * -EBUSY - interrupt busy
+ * -EINVAL - irq > 15 or handler = NULL
+ */
+
+ do
+ {
+ irqval = request_irq(irqtab[i], &SK_interrupt, 0, "sk_g16");
+ i++;
+ } while (irqval && irqtab[i]);
+
+ if (irqval) /* We tried every possible IRQ but no success */
+ {
+ printk("%s: unable to get an IRQ\n", dev->name);
+ return -EAGAIN;
+ }
+
+ dev->irq = irqtab[--i];
+
+ outb(i<<2, SK_POS4); /* Set Card on probed IRQ */
+
+ }
+ else if (dev->irq == 2) /* IRQ2 is always IRQ9 */
+ {
+ if (request_irq(9, &SK_interrupt, 0, "sk_g16"))
+ {
+ printk("%s: unable to get IRQ 9\n", dev->name);
+ return -EAGAIN;
+ }
+ dev->irq = 9;
+
+ /*
+ * Now we set card on IRQ2.
+ * This can be confusing, but remember that IRQ2 on the network
+ * card is in reality IRQ9
+ */
+ outb(0x08, SK_POS4); /* set card to IRQ2 */
+
+ }
+ else /* Check IRQ as defined in Space.c */
+ {
+ int i = 0;
+
+ /* check if IRQ free and valid. Then install Interrupt handler */
+
+ if (request_irq(dev->irq, &SK_interrupt, 0, "sk_g16"))
+ {
+ printk("%s: unable to get selected IRQ\n", dev->name);
+ return -EAGAIN;
+ }
+
+ switch(dev->irq)
+ {
+ case 3: i = 0;
+ break;
+ case 5: i = 1;
+ break;
+ case 2: i = 2;
+ break;
+ case 11:i = 3;
+ break;
+ default:
+ printk("%s: Preselected IRQ %d is invalid for %s boards",
+ dev->name,
+ dev->irq,
+ SK_NAME);
+ return -EAGAIN;
+ }
+
+ outb(i<<2, SK_POS4); /* Set IRQ on card */
+ }
+
+ irq2dev_map[dev->irq] = dev; /* Set IRQ as used by us */
+
+ printk("%s: Schneider & Koch G16 at %#3x, IRQ %d, shared mem at %#08x\n",
+ dev->name, (unsigned int)dev->base_addr,
+ (int) dev->irq, (unsigned int) p->ram);
+
+ if (!(i = SK_lance_init(dev, 0))) /* LANCE init OK? */
+ {
+
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+#ifdef SK_DEBUG
+
+ /*
+ * This debug block tries to stop LANCE,
+ * reinit LANCE with transmitter and receiver disabled,
+ * then stop again and reinit with NORMAL_MODE
+ */
+
+ printk("## %s: After lance init. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printk("## %s: LANCE stopped. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(dev, MODE_DTX | MODE_DRX);
+ printk("## %s: Reinit with DTX + DRX off. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_write_reg(CSR0, CSR0_STOP);
+ printk("## %s: LANCE stopped. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_lance_init(dev, MODE_NORMAL);
+ printk("## %s: LANCE back to normal mode. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0));
+ SK_print_pos(dev, "POS regs before returning OK");
+
+#endif /* SK_DEBUG */
+
+ return 0; /* SK_open() is successful */
+ }
+ else /* LANCE init failed */
+ {
+
+ PRINTK(("## %s: LANCE init failed: CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ dev->start = 0; /* Device not ready */
+ return -EAGAIN;
+ }
+
+} /* End of SK_open() */
+
+
+/*-
+ * Function : SK_lance_init
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Reset LANCE chip, fill RMD, TMD structures with
+ * start values and Start LANCE.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : int mode - put LANCE into "mode" see data-sheet for
+ * more info.
+ * Return Value : 0 - Init done
+ * Errors : -1 - Init failed
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_lance_init(struct device *dev, unsigned short mode)
+{
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+ struct rmd *rmdp;
+
+ PRINTK(("## %s: At beginning of LANCE init. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Reset LANCE */
+ SK_reset_board();
+
+ /* Initialize TMD's with start values */
+ p->tmdnum = 0; /* First descriptor for transmitting */
+ p->tmdlast = 0; /* First descriptor for reading stats */
+
+ for (i = 0; i < TMDNUM; i++) /* Init all TMD's */
+ {
+ tmdp = p->tmdhead + i;
+
+ tmdp->u.buffer = (unsigned long) p->tmdbufs[i]; /* assign buffer */
+
+ /* Mark TMD as start and end of packet */
+ tmdp->u.s.status = TX_STP | TX_ENP;
+ }
+
+
+ /* Initialize RMD's with start values */
+
+ p->rmdnum = 0; /* First RMD which will be used */
+
+ for (i = 0; i < RMDNUM; i++) /* Init all RMD's */
+ {
+ rmdp = p->rmdhead + i;
+
+
+ rmdp->u.buffer = (unsigned long) p->rmdbufs[i]; /* assign buffer */
+
+ /*
+ * LANCE must be owner at beginning so that he can fill in
+ * receiving packets, set status and release RMD
+ */
+
+ rmdp->u.s.status = RX_OWN;
+
+ rmdp->blen = -PKT_BUF_SZ; /* Buffer Size in a two's complement */
+
+ rmdp->mlen = 0; /* init message length */
+
+ }
+
+ /* Fill LANCE Initialize Block */
+
+ (p->ram)->ib.mode = mode; /* Set operation mode */
+
+ for (i = 0; i < ETH_ALEN; i++) /* Set physical address */
+ {
+ (p->ram)->ib.paddr[i] = dev->dev_addr[i];
+ }
+
+ for (i = 0; i < 8; i++) /* Set multicast, logical address */
+ {
+ (p->ram)->ib.laddr[i] = 0; /* We do not use logical addressing */
+ }
+
+ /* Set ring descriptor pointers and set number of descriptors */
+
+ (p->ram)->ib.rdrp = (int) p->rmdhead | RMDNUMMASK;
+ (p->ram)->ib.tdrp = (int) p->tmdhead | TMDNUMMASK;
+
+ /* Prepare LANCE Control and Status Registers */
+
+ cli();
+
+ SK_write_reg(CSR3, CSR3_ACON); /* Ale Control !!!THIS MUST BE SET!!!! */
+
+ /*
+ * LANCE addresses the RAM from 0x0000 to 0x3fbf and has no access to
+ * PC Memory locations.
+ *
+ * In structure SK_ram is defined that the first thing in ram
+ * is the initialization block. So his address is for LANCE always
+ * 0x0000
+ *
+ * CSR1 contains low order bits 15:0 of initialization block address
+ * CSR2 is built of:
+ * 7:0 High order bits 23:16 of initialization block address
+ * 15:8 reserved, must be 0
+ */
+
+ /* Set initialization block address (must be on word boundary) */
+ SK_write_reg(CSR1, 0); /* Set low order bits 15:0 */
+ SK_write_reg(CSR2, 0); /* Set high order bits 23:16 */
+
+
+ PRINTK(("## %s: After setting CSR1-3. CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ /* Initialize LANCE */
+
+ /*
+ * INIT = Initialize, when set, causes the LANCE to begin the
+ * initialization procedure and access the Init Block.
+ */
+
+ SK_write_reg(CSR0, CSR0_INIT);
+
+ sti();
+
+ /* Wait until LANCE finished initialization */
+
+ SK_set_RAP(CSR0); /* Register Address Pointer to CSR0 */
+
+ for (i = 0; (i < 100) && !(SK_rread_reg() & CSR0_IDON); i++)
+ ; /* Wait until init done or go ahead if problems (i>=100) */
+
+ if (i >= 100) /* Something is wrong ! */
+ {
+ printk("%s: can't init am7990, status: %04x "
+ "init_block: %#08x\n",
+ dev->name, (int) SK_read_reg(CSR0),
+ (unsigned int) &(p->ram)->ib);
+
+#ifdef SK_DEBUG
+ SK_print_pos(dev, "LANCE INIT failed");
+ SK_print_dev(dev,"Device Structure:");
+#endif
+
+ return -1; /* LANCE init failed */
+ }
+
+ PRINTK(("## %s: init done after %d ticks\n", SK_NAME, i));
+
+ /* Clear Initialize done, enable Interrupts, start LANCE */
+
+ SK_write_reg(CSR0, CSR0_IDON | CSR0_INEA | CSR0_STRT);
+
+ PRINTK(("## %s: LANCE started. CSR0: %#06x\n", SK_NAME,
+ SK_read_reg(CSR0)));
+
+ return 0; /* LANCE is up and running */
+
+} /* End of SK_lance_init() */
+
+
+
+/*-
+ * Function : SK_send_packet
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : Writes an socket buffer into a transmit descriptor
+ * and starts transmission.
+ *
+ * Parameters : I : struct sk_buff *skb - packet to transfer
+ * I : struct device *dev - SK_G16 device structure
+ * Return Value : 0 - OK
+ * 1 - Could not transmit (dev_queue_xmit will queue it)
+ * and try to sent it later
+ * Globals : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static int SK_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ struct priv *p = (struct priv *) dev->priv;
+ struct tmd *tmdp;
+
+ if (dev->tbusy)
+ {
+ /* if Transmitter more than 150ms busy -> time_out */
+
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 15)
+ {
+ return 1; /* We have to try transmit later */
+ }
+
+ printk("%s: xmitter timed out, try to restart!\n", dev->name);
+
+ SK_lance_init(dev, MODE_NORMAL); /* Reinit LANCE */
+
+ dev->tbusy = 0; /* Clear Transmitter flag */
+
+ dev->trans_start = jiffies; /* Mark Start of transmission */
+
+ }
+
+ /*
+ * If some upper Layer thinks we missed a transmit done interrupt
+ * we are passed NULL.
+ * (dev_queue_xmit net/inet/dev.c
+ */
+
+ if (skb == NULL)
+ {
+ /*
+ * Dequeue packets from transmit queue and send them.
+ */
+ dev_tint(dev);
+
+ return 0;
+ }
+
+ PRINTK2(("## %s: SK_send_packet() called, CSR0 %#04x.\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ * This means check if we are already in.
+ */
+
+ if (set_bit(0, (void *) &dev->tbusy) != 0) /* dev->tbusy already set ? */
+ {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ }
+ else
+ {
+ /* Evaluate Packet length */
+ short len = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+
+ tmdp = p->tmdhead + p->tmdnum; /* Which descriptor for transmitting */
+
+ /* Fill in Transmit Message Descriptor */
+
+ /* Copy data into dual ported ram */
+
+ memcpy((char *) (tmdp->u.buffer & 0x00ffffff), (char *)skb->data,
+ skb->len);
+
+ tmdp->blen = -len; /* set length to transmit */
+
+ /*
+ * Packet start and end is always set because we use the maximum
+ * packet length as buffer length.
+ * Relinquish ownership to LANCE
+ */
+
+ tmdp->u.s.status = TX_OWN | TX_STP | TX_ENP;
+
+ /* Start Demand Transmission */
+ SK_write_reg(CSR0, CSR0_TDMD | CSR0_INEA);
+
+ dev->trans_start = jiffies; /* Mark start of transmission */
+
+ /* Set pointer to next transmit buffer */
+ p->tmdnum++;
+ p->tmdnum &= TMDNUM-1;
+
+ /* Do we own the next transmit buffer ? */
+ if (! ((p->tmdhead + p->tmdnum)->u.s.status & TX_OWN) )
+ {
+ /*
+ * We own next buffer and are ready to transmit, so
+ * clear busy flag
+ */
+ dev->tbusy = 0;
+ }
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+} /* End of SK_send_packet */
+
+
+/*-
+ * Function : SK_interrupt
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : SK_G16 interrupt handler which checks for LANCE
+ * Errors, handles transmit and receive interrupts
+ *
+ * Parameters : I : int irq, struct pt_regs * regs -
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_interrupt(int irq, struct pt_regs * regs)
+{
+ int csr0;
+ struct device *dev = (struct device *) irq2dev_map[irq];
+ struct priv *p = (struct priv *) dev->priv;
+
+
+ PRINTK2(("## %s: SK_interrupt(). status: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ if (dev == NULL)
+ {
+ printk("SK_interrupt(): IRQ %d for unknown device.\n", irq);
+ }
+
+
+ if (dev->interrupt)
+ {
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+ }
+
+ csr0 = SK_read_reg(CSR0); /* store register for checking */
+
+ dev->interrupt = 1; /* We are handling an interrupt */
+
+ /*
+ * Acknowledge all of the current interrupt sources, disable
+ * Interrupts (INEA = 0)
+ */
+
+ SK_write_reg(CSR0, csr0 & CSR0_CLRALL);
+
+ if (csr0 & CSR0_ERR) /* LANCE Error */
+ {
+ printk("%s: error: %04x\n", dev->name, csr0);
+
+ if (csr0 & CSR0_MISS) /* No place to store packet ? */
+ {
+ p->stats.rx_dropped++;
+ }
+ }
+
+ if (csr0 & CSR0_RINT) /* Receive Interrupt (packet arrived) */
+ {
+ SK_rxintr(dev);
+ }
+
+ if (csr0 & CSR0_TINT) /* Transmit interrupt (packet sent) */
+ {
+ SK_txintr(dev);
+ }
+
+ SK_write_reg(CSR0, CSR0_INEA); /* Enable Interrupts */
+
+ dev->interrupt = 0; /* We are out */
+} /* End of SK_interrupt() */
+
+
+/*-
+ * Function : SK_txintr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : After sending a packet we check status, update
+ * statistics and relinquish ownership of transmit
+ * descriptor ring.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_txintr(struct device *dev)
+{
+ int tmdstat;
+ struct tmd *tmdp;
+ struct priv *p = (struct priv *) dev->priv;
+
+
+ PRINTK2(("## %s: SK_txintr() status: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ tmdp = p->tmdhead + p->tmdlast; /* Which buffer we sent at last ? */
+
+ /* Set next buffer */
+ p->tmdlast++;
+ p->tmdlast &= TMDNUM-1;
+
+ tmdstat = tmdp->u.s.status & 0xff00; /* filter out status bits 15:08 */
+
+ /*
+ * We check status of transmitted packet.
+ * see LANCE data-sheet for error explanation
+ */
+ if (tmdstat & TX_ERR) /* Error occurred */
+ {
+ printk("%s: TX error: %04x %04x\n", dev->name, (int) tmdstat,
+ (int) tmdp->status2);
+
+ if (tmdp->status2 & TX_TDR) /* TDR problems? */
+ {
+ printk("%s: tdr-problems \n", dev->name);
+ }
+
+ if (tmdp->status2 & TX_RTRY) /* Failed in 16 attempts to transmit ? */
+ p->stats.tx_aborted_errors++;
+ if (tmdp->status2 & TX_LCOL) /* Late collision ? */
+ p->stats.tx_window_errors++;
+ if (tmdp->status2 & TX_LCAR) /* Loss of Carrier ? */
+ p->stats.tx_carrier_errors++;
+ if (tmdp->status2 & TX_UFLO) /* Underflow error ? */
+ {
+ p->stats.tx_fifo_errors++;
+
+ /*
+ * If UFLO error occurs it will turn transmitter of.
+ * So we must reinit LANCE
+ */
+
+ SK_lance_init(dev, MODE_NORMAL);
+ }
+
+ p->stats.tx_errors++;
+
+ tmdp->status2 = 0; /* Clear error flags */
+ }
+ else if (tmdstat & TX_MORE) /* Collisions occurred ? */
+ {
+ /*
+ * Here I have a problem.
+ * I only know that there must be one or up to 15 collisions.
+ * Thats why TX_MORE is set, because after 16 attempts TX_RTRY
+ * will be set which means couldn't send packet aborted transfer.
+ *
+ * First I did not have this in but then I thought at minimum
+ * we see that something was not ok.
+ * If anyone knows something better than this to handle this
+ * please report it. (see Email addresses in the README file)
+ */
+
+ p->stats.collisions++;
+ }
+ else /* Packet sent without any problems */
+ {
+ p->stats.tx_packets++;
+ }
+
+ /*
+ * We mark transmitter not busy anymore, because now we have a free
+ * transmit descriptor which can be filled by SK_send_packet and
+ * afterwards sent by the LANCE
+ */
+
+ dev->tbusy = 0;
+
+ /*
+ * mark_bh(NET_BH);
+ * This will cause net_bh() to run after this interrupt handler.
+ *
+ * The function which do handle slow IRQ parts is do_bottom_half()
+ * which runs at normal kernel priority, that means all interrupt are
+ * enabled. (see kernel/irq.c)
+ *
+ * net_bh does something like this:
+ * - check if already in net_bh
+ * - try to transmit something from the send queue
+ * - if something is in the receive queue send it up to higher
+ * levels if it is a known protocol
+ * - try to transmit something from the send queue
+ */
+
+ mark_bh(NET_BH);
+
+} /* End of SK_txintr() */
+
+
+/*-
+ * Function : SK_rxintr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/27
+ *
+ * Description : Buffer sent, check for errors, relinquish ownership
+ * of the receive message descriptor.
+ *
+ * Parameters : I : SK_G16 device structure
+ * Return Value : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static void SK_rxintr(struct device *dev)
+{
+
+ struct rmd *rmdp;
+ int rmdstat;
+ struct priv *p = (struct priv *) dev->priv;
+
+ PRINTK2(("## %s: SK_rxintr(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ rmdp = p->rmdhead + p->rmdnum;
+
+ /* As long as we own the next entry, check status and send
+ * it up to higher layer
+ */
+
+ while (!( (rmdstat = rmdp->u.s.status) & RX_OWN))
+ {
+ /*
+ * Start and end of packet must be set, because we use
+ * the ethernet maximum packet length (1518) as buffer size.
+ *
+ * Because our buffers are at maximum OFLO and BUFF errors are
+ * not to be concerned (see Data sheet)
+ */
+
+ if ((rmdstat & (RX_STP | RX_ENP)) != (RX_STP | RX_ENP))
+ {
+ /* Start of a frame > 1518 Bytes ? */
+
+ if (rmdstat & RX_STP)
+ {
+ p->stats.rx_errors++; /* bad packet received */
+ p->stats.rx_length_errors++; /* packet to long */
+
+ printk("%s: packet too long\n", dev->name);
+ }
+
+ /*
+ * All other packets will be ignored until a new frame with
+ * start (RX_STP) set follows.
+ *
+ * What we do is just give descriptor free for new incoming
+ * packets.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else if (rmdstat & RX_ERR) /* Receive Error ? */
+ {
+ printk("%s: RX error: %04x\n", dev->name, (int) rmdstat);
+
+ p->stats.rx_errors++;
+
+ if (rmdstat & RX_FRAM) p->stats.rx_frame_errors++;
+ if (rmdstat & RX_CRC) p->stats.rx_crc_errors++;
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+
+ }
+ else /* We have a packet which can be queued for the upper layers */
+ {
+
+ int len = (rmdp->mlen & 0x0fff); /* extract message length from receive buffer */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(len+2); /* allocate socket buffer */
+
+ if (skb == NULL) /* Could not get mem ? */
+ {
+
+ /*
+ * Couldn't allocate sk_buffer so we give descriptor back
+ * to Lance, update statistics and go ahead.
+ */
+
+ rmdp->u.s.status = RX_OWN; /* Relinquish ownership to LANCE */
+ printk("%s: Couldn't allocate sk_buff, deferring packet.\n",
+ dev->name);
+ p->stats.rx_dropped++;
+
+ break; /* Jump out */
+ }
+
+ /* Prepare sk_buff to queue for upper layers */
+
+ skb->dev = dev;
+ skb_reserve(skb,2); /* Align IP header on 16 byte boundary */
+
+ /*
+ * Copy data out of our receive descriptor into sk_buff.
+ *
+ * (rmdp->u.buffer & 0x00ffffff) -> get address of buffer and
+ * ignore status fields)
+ */
+
+ memcpy(skb_put(skb,len), (unsigned char *) (rmdp->u.buffer & 0x00ffffff),
+ len);
+
+
+ /*
+ * Notify the upper protocol layers that there is another packet
+ * to handle
+ *
+ * netif_rx() always succeeds. see /net/inet/dev.c for more.
+ */
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb); /* queue packet and mark it for processing */
+
+ /*
+ * Packet is queued and marked for processing so we
+ * free our descriptor and update statistics
+ */
+
+ rmdp->u.s.status = RX_OWN;
+ p->stats.rx_packets++;
+
+
+ p->rmdnum++;
+ p->rmdnum %= RMDNUM;
+
+ rmdp = p->rmdhead + p->rmdnum;
+ }
+ }
+} /* End of SK_rxintr() */
+
+
+/*-
+ * Function : SK_close
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : close gets called from dev_close() and should
+ * deinstall the card (free_irq, mem etc).
+ *
+ * Parameters : I : struct device *dev - our device structure
+ * Return Value : 0 - closed device driver
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+/* I have tried to set BOOT_ROM on and RAM off but then, after a 'ifconfig
+ * down' the system stops. So I don't shut set card to init state.
+ */
+
+static int SK_close(struct device *dev)
+{
+
+ PRINTK(("## %s: SK_close(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ dev->tbusy = 1; /* Transmitter busy */
+ dev->start = 0; /* Card down */
+
+ printk("%s: Shutting %s down CSR0 %#06x\n", dev->name, SK_NAME,
+ (int) SK_read_reg(CSR0));
+
+ SK_write_reg(CSR0, CSR0_STOP); /* STOP the LANCE */
+
+ free_irq(dev->irq); /* Free IRQ */
+ irq2dev_map[dev->irq] = 0; /* Mark IRQ as unused */
+
+ return 0; /* always succeed */
+
+} /* End of SK_close() */
+
+
+/*-
+ * Function : SK_get_stats
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : Return current status structure to upper layers.
+ * It is called by sprintf_stats (dev.c).
+ *
+ * Parameters : I : struct device *dev - our device structure
+ * Return Value : struct enet_statistics * - our current statistics
+ * Errors : None
+ * Side Effects : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+static struct enet_statistics *SK_get_stats(struct device *dev)
+{
+
+ struct priv *p = (struct priv *) dev->priv;
+
+ PRINTK(("## %s: SK_get_stats(). CSR0: %#06x\n",
+ SK_NAME, SK_read_reg(CSR0)));
+
+ return &p->stats; /* Return Device status */
+
+} /* End of SK_get_stats() */
+
+
+/*-
+ * Function : set_multicast_list
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/26
+ *
+ * Description : This function gets called when a program performs
+ * a SIOCSIFFLAGS call. Ifconfig does this if you call
+ * 'ifconfig [-]allmulti' which enables or disables the
+ * Promiscuous mode.
+ * Promiscuous mode is when the Network card accepts all
+ * packets, not only the packets which match our MAC
+ * Address. It is useful for writing a network monitor,
+ * but it is also a security problem. You have to remember
+ * that all information on the net is not encrypted.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device Structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+ * 95/10/18 ACox Noew multicast calling scheme
+-*/
+
+
+/* Set or clear the multicast filter for SK_G16.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+
+ if (dev->flags&IFF_PROMISC)
+ {
+ /* Reinitialize LANCE with MODE_PROM set */
+ SK_lance_init(dev, MODE_PROM);
+ }
+ else if (dev->mc_count==0 && !(dev->flags&IFF_ALLMULTI))
+ {
+ /* Reinitialize LANCE without MODE_PROM */
+ SK_lance_init(dev, MODE_NORMAL);
+ }
+ else
+ {
+ /* Multicast with logical address filter on */
+ /* Reinitialize LANCE without MODE_PROM */
+ SK_lance_init(dev, MODE_NORMAL);
+
+ /* Not implemented yet. */
+ }
+} /* End of set_multicast_list() */
+
+
+
+/*-
+ * Function : SK_rom_addr
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/06/01
+ *
+ * Description : Try to find a Boot_ROM at all possible locations
+ *
+ * Parameters : None
+ * Return Value : Address where Boot_ROM is
+ * Errors : 0 - Did not find Boot_ROM
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+unsigned int SK_rom_addr(void)
+{
+ int i,j;
+ int rom_found = 0;
+ unsigned int rom_location[] = SK_BOOT_ROM_LOCATIONS;
+ unsigned char rom_id[] = SK_BOOT_ROM_ID;
+ unsigned char *test_byte;
+
+ /* Autodetect Boot_ROM */
+ PRINTK(("## %s: Autodetection of Boot_ROM\n", SK_NAME));
+
+ for (i = 0; (rom_location[i] != 0) && (rom_found == 0); i++)
+ {
+
+ PRINTK(("## Trying ROM location %#08x", rom_location[i]));
+
+ rom_found = 1;
+ for (j = 0; j < 6; j++)
+ {
+ test_byte = (unsigned char *) (rom_location[i]+j);
+ PRINTK((" %02x ", *test_byte));
+
+ if(!(*test_byte == rom_id[j]))
+ {
+ rom_found = 0;
+ }
+ }
+ PRINTK(("\n"));
+ }
+
+ if (rom_found == 1)
+ {
+ PRINTK(("## %s: Boot_ROM found at %#08x\n",
+ SK_NAME, rom_location[(i-1)]));
+
+ return (rom_location[--i]);
+ }
+ else
+ {
+ PRINTK(("%s: No Boot_ROM found\n", SK_NAME));
+ return 0;
+ }
+} /* End of SK_rom_addr() */
+
+
+
+/* LANCE access functions
+ *
+ * ! CSR1-3 can only be accessed when in CSR0 the STOP bit is set !
+ */
+
+
+/*-
+ * Function : SK_reset_board
+ *
+ * Author : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : This function resets SK_G16 and all components, but
+ * POS registers are not changed
+ *
+ * Parameters : None
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ *
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_reset_board(void)
+{
+ int i;
+
+ SK_PORT = 0x00; /* Reset active */
+ for (i = 0; i < 10 ; i++) /* Delay min 5ms */
+ ;
+ SK_PORT = SK_RESET; /* Set back to normal operation */
+
+} /* End of SK_reset_board() */
+
+
+/*-
+ * Function : SK_set_RAP
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : Set LANCE Register Address Port to register
+ * for later data transfer.
+ *
+ * Parameters : I : reg_number - which CSR to read/write from/to
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_set_RAP(int reg_number)
+{
+ SK_IOREG = reg_number;
+ SK_PORT = SK_RESET | SK_RAP | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_set_RAP() */
+
+
+/*-
+ * Function : SK_read_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : Set RAP and read data from a LANCE CSR register
+ *
+ * Parameters : I : reg_number - which CSR to read from
+ * Return Value : Register contents
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+int SK_read_reg(int reg_number)
+{
+ SK_set_RAP(reg_number);
+
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_read_reg() */
+
+
+/*-
+ * Function : SK_rread_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/28
+ *
+ * Description : Read data from preseted register.
+ * This function requires that you know which
+ * Register is actually set. Be aware that CSR1-3
+ * can only be accessed when in CSR0 STOP is set.
+ *
+ * Return Value : Register contents
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+int SK_rread_reg(void)
+{
+ SK_PORT = SK_RESET | SK_RDATA | SK_RREG;
+
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+ return (SK_IOREG);
+
+} /* End of SK_rread_reg() */
+
+
+/*-
+ * Function : SK_write_reg
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function sets the RAP then fills in the
+ * LANCE I/O Reg and starts Transfer to LANCE.
+ * It waits until transfer has ended which is max. 7 ms
+ * and then it returns.
+ *
+ * Parameters : I : reg_number - which CSR to write to
+ * I : value - what value to fill into register
+ * Return Value : None
+ * Errors : None
+ * Globals : SK_RAM *board - SK_RAM structure pointer
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_write_reg(int reg_number, int value)
+{
+ SK_set_RAP(reg_number);
+
+ SK_IOREG = value;
+ SK_PORT = SK_RESET | SK_RDATA | SK_WREG;
+ SK_IOCOM = SK_DOIO;
+
+ while (SK_PORT & SK_IORUN)
+ ;
+} /* End of SK_write_reg */
+
+
+
+/*
+ * Debugging functions
+ * -------------------
+ */
+
+/*-
+ * Function : SK_print_pos
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function prints out the 4 POS (Programmable
+ * Option Select) Registers. Used mainly to debug operation.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : char * - Text which will be printed as title
+ * Return Value : None
+ * Errors : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_pos(struct device *dev, char *text)
+{
+ int ioaddr = dev->base_addr;
+
+ unsigned char pos0 = inb(SK_POS0),
+ pos1 = inb(SK_POS1),
+ pos2 = inb(SK_POS2),
+ pos3 = inb(SK_POS3),
+ pos4 = inb(SK_POS4);
+
+
+ printk("## %s: %s.\n"
+ "## pos0=%#4x pos1=%#4x pos2=%#04x pos3=%#08x pos4=%#04x\n",
+ SK_NAME, text, pos0, pos1, pos2, (pos3<<14), pos4);
+
+} /* End of SK_print_pos() */
+
+
+
+/*-
+ * Function : SK_print_dev
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/05/25
+ *
+ * Description : This function simply prints out the important fields
+ * of the device structure.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * I : char *text - Title for printing
+ * Return Value : None
+ * Errors : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_dev(struct device *dev, char *text)
+{
+ if (dev == NULL)
+ {
+ printk("## %s: Device Structure. %s\n", SK_NAME, text);
+ printk("## DEVICE == NULL\n");
+ }
+ else
+ {
+ printk("## %s: Device Structure. %s\n", SK_NAME, text);
+ printk("## Device Name: %s Base Address: %#06lx IRQ: %d\n",
+ dev->name, dev->base_addr, dev->irq);
+
+ printk("## FLAGS: start: %d tbusy: %ld int: %d\n",
+ dev->start, dev->tbusy, dev->interrupt);
+
+ printk("## next device: %#08x init function: %#08x\n",
+ (int) dev->next, (int) dev->init);
+ }
+
+} /* End of SK_print_dev() */
+
+
+
+/*-
+ * Function : SK_print_ram
+ * Author : Patrick J.D. Weichmann
+ * Date Created : 94/06/02
+ *
+ * Description : This function is used to check how are things set up
+ * in the 16KB RAM. Also the pointers to the receive and
+ * transmit descriptor rings and rx and tx buffers locations.
+ * It contains a minor bug in printing, but has no effect to the values
+ * only newlines are not correct.
+ *
+ * Parameters : I : struct device *dev - SK_G16 device structure
+ * Return Value : None
+ * Errors : None
+ * Globals : None
+ * Update History :
+ * YY/MM/DD uid Description
+-*/
+
+void SK_print_ram(struct device *dev)
+{
+
+ int i;
+ struct priv *p = (struct priv *) dev->priv;
+
+ printk("## %s: RAM Details.\n"
+ "## RAM at %#08x tmdhead: %#08x rmdhead: %#08x initblock: %#08x\n",
+ SK_NAME,
+ (unsigned int) p->ram,
+ (unsigned int) p->tmdhead,
+ (unsigned int) p->rmdhead,
+ (unsigned int) &(p->ram)->ib);
+
+ printk("## ");
+
+ for(i = 0; i < TMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printk("\n## ");
+ }
+ printk("tmdbufs%d: %#08x ", (i+1), (int) p->tmdbufs[i]);
+ }
+ printk("## ");
+
+ for(i = 0; i < RMDNUM; i++)
+ {
+ if (!(i % 3)) /* Every third line do a newline */
+ {
+ printk("\n## ");
+ }
+ printk("rmdbufs%d: %#08x ", (i+1), (int) p->rmdbufs[i]);
+ }
+ printk("\n");
+
+} /* End of SK_print_ram() */
+
diff --git a/i386/i386at/gpl/linux/net/sk_g16.h b/i386/i386at/gpl/linux/net/sk_g16.h
new file mode 100644
index 00000000..3a92f1f9
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/sk_g16.h
@@ -0,0 +1,171 @@
+/*-
+ *
+ * This software may be used and distributed according to the terms
+ * of the GNU Public License, incorporated herein by reference.
+ *
+ * Module : sk_g16.h
+ * Version : $Revision: 1.1.1.1 $
+ *
+ * Author : M.Hipp (mhipp@student.uni-tuebingen.de)
+ * changes by : Patrick J.D. Weichmann
+ *
+ * Date Created : 94/05/25
+ *
+ * Description : In here are all necessary definitions of
+ * the am7990 (LANCE) chip used for writing a
+ * network device driver which uses this chip
+ *
+ * $Log: sk_g16.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:56 thomas
+ * Imported from UK22
+ *
+ * Revision 1.3 1996/03/25 20:24:35 goel
+ * Linux driver merge.
+ *
+-*/
+
+#ifndef SK_G16_H
+
+#define SK_G16_H
+
+
+/*
+ * Control and Status Register 0 (CSR0) bit definitions
+ *
+ * (R=Readable) (W=Writeable) (S=Set on write) (C-Clear on write)
+ *
+ */
+
+#define CSR0_ERR 0x8000 /* Error summary (R) */
+#define CSR0_BABL 0x4000 /* Babble transmitter timeout error (RC) */
+#define CSR0_CERR 0x2000 /* Collision Error (RC) */
+#define CSR0_MISS 0x1000 /* Missed packet (RC) */
+#define CSR0_MERR 0x0800 /* Memory Error (RC) */
+#define CSR0_RINT 0x0400 /* Receiver Interrupt (RC) */
+#define CSR0_TINT 0x0200 /* Transmit Interrupt (RC) */
+#define CSR0_IDON 0x0100 /* Initialization Done (RC) */
+#define CSR0_INTR 0x0080 /* Interrupt Flag (R) */
+#define CSR0_INEA 0x0040 /* Interrupt Enable (RW) */
+#define CSR0_RXON 0x0020 /* Receiver on (R) */
+#define CSR0_TXON 0x0010 /* Transmitter on (R) */
+#define CSR0_TDMD 0x0008 /* Transmit Demand (RS) */
+#define CSR0_STOP 0x0004 /* Stop (RS) */
+#define CSR0_STRT 0x0002 /* Start (RS) */
+#define CSR0_INIT 0x0001 /* Initialize (RS) */
+
+#define CSR0_CLRALL 0x7f00 /* mask for all clearable bits */
+
+/*
+ * Control and Status Register 3 (CSR3) bit definitions
+ *
+ */
+
+#define CSR3_BSWAP 0x0004 /* Byte Swap (RW) */
+#define CSR3_ACON 0x0002 /* ALE Control (RW) */
+#define CSR3_BCON 0x0001 /* Byte Control (RW) */
+
+/*
+ * Initialization Block Mode operation Bit Definitions.
+ */
+
+#define MODE_PROM 0x8000 /* Promiscuous Mode */
+#define MODE_INTL 0x0040 /* Internal Loopback */
+#define MODE_DRTY 0x0020 /* Disable Retry */
+#define MODE_COLL 0x0010 /* Force Collision */
+#define MODE_DTCR 0x0008 /* Disable Transmit CRC) */
+#define MODE_LOOP 0x0004 /* Loopback */
+#define MODE_DTX 0x0002 /* Disable the Transmitter */
+#define MODE_DRX 0x0001 /* Disable the Receiver */
+
+#define MODE_NORMAL 0x0000 /* Normal operation mode */
+
+/*
+ * Receive message descriptor status bit definitions.
+ */
+
+#define RX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define RX_ERR 0x40 /* Error Summary */
+#define RX_FRAM 0x20 /* Framing Error */
+#define RX_OFLO 0x10 /* Overflow Error */
+#define RX_CRC 0x08 /* CRC Error */
+#define RX_BUFF 0x04 /* Buffer Error */
+#define RX_STP 0x02 /* Start of Packet */
+#define RX_ENP 0x01 /* End of Packet */
+
+
+/*
+ * Transmit message descriptor status bit definitions.
+ */
+
+#define TX_OWN 0x80 /* Owner bit 0 = host, 1 = lance */
+#define TX_ERR 0x40 /* Error Summary */
+#define TX_MORE 0x10 /* More the 1 retry needed to Xmit */
+#define TX_ONE 0x08 /* One retry needed to Xmit */
+#define TX_DEF 0x04 /* Deferred */
+#define TX_STP 0x02 /* Start of Packet */
+#define TX_ENP 0x01 /* End of Packet */
+
+/*
+ * Transmit status (2) (valid if TX_ERR == 1)
+ */
+
+#define TX_BUFF 0x8000 /* Buffering error (no ENP) */
+#define TX_UFLO 0x4000 /* Underflow (late memory) */
+#define TX_LCOL 0x1000 /* Late collision */
+#define TX_LCAR 0x0400 /* Loss of Carrier */
+#define TX_RTRY 0x0200 /* Failed after 16 retransmissions */
+#define TX_TDR 0x003f /* Time-domain-reflectometer-value */
+
+
+/*
+ * Structures used for Communication with the LANCE
+ */
+
+/* LANCE Initialize Block */
+
+struct init_block
+{
+ unsigned short mode; /* Mode Register */
+ unsigned char paddr[6]; /* Physical Address (MAC) */
+ unsigned char laddr[8]; /* Logical Filter Address (not used) */
+ unsigned int rdrp; /* Receive Descriptor Ring pointer */
+ unsigned int tdrp; /* Transmit Descriptor Ring pointer */
+};
+
+
+/* Receive Message Descriptor Entry */
+
+struct rmd
+{
+ union
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ volatile short blen; /* Buffer Length (two's complement) */
+ unsigned short mlen; /* Message Byte Count */
+};
+
+
+/* Transmit Message Descriptor Entry */
+
+struct tmd
+{
+ union
+ {
+ unsigned long buffer; /* Address of buffer */
+ struct
+ {
+ unsigned char unused[3];
+ unsigned volatile char status; /* Status Bits */
+ } s;
+ } u;
+ unsigned short blen; /* Buffer Length (two's complement) */
+ unsigned volatile short status2; /* Error Status Bits */
+};
+
+#endif /* End of SK_G16_H */
diff --git a/i386/i386at/gpl/linux/net/smc-ultra.c b/i386/i386at/gpl/linux/net/smc-ultra.c
new file mode 100644
index 00000000..f13cd0a7
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/smc-ultra.c
@@ -0,0 +1,419 @@
+/* smc-ultra.c: A SMC Ultra ethernet driver for linux. */
+/*
+ Written 1993,1994,1995 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a driver for the SMC Ultra and SMC EtherEZ ethercards.
+
+ This driver uses the cards in the 8390-compatible, shared memory mode.
+ Most of the run-time complexity is handled by the generic code in
+ 8390.c. The code in this file is responsible for
+
+ ultra_probe() Detecting and initializing the card.
+ ultra_probe1()
+
+ ultra_open() The card-specific details of starting, stopping
+ ultra_reset_8390() and resetting the 8390 NIC core.
+ ultra_close()
+
+ ultra_block_input() Routines for reading and writing blocks of
+ ultra_block_output() packet buffer memory.
+
+ This driver enables the shared memory only when doing the actual data
+ transfers to avoid a bug in early version of the card that corrupted
+ data transferred by a AHA1542.
+
+ This driver does not support the programmed-I/O data transfer mode of
+ the EtherEZ. That support (if available) is smc-ez.c. Nor does it
+ use the non-8390-compatible "Altego" mode. (No support currently planned.)
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users.
+*/
+
+static const char *version =
+ "smc-ultra.c:v1.12 1/18/95 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int ultra_portlist[] =
+{0x200, 0x220, 0x240, 0x280, 0x300, 0x340, 0x380, 0};
+
+int ultra_probe(struct device *dev);
+int ultra_probe1(struct device *dev, int ioaddr);
+
+static int ultra_open(struct device *dev);
+static void ultra_reset_8390(struct device *dev);
+static void ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void ultra_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void ultra_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int ultra_close_card(struct device *dev);
+
+
+#define START_PG 0x00 /* First page of TX buffer */
+
+#define ULTRA_CMDREG 0 /* Offset to ASIC command register. */
+#define ULTRA_RESET 0x80 /* Board reset, in ULTRA_CMDREG. */
+#define ULTRA_MEMENB 0x40 /* Enable the shared memory. */
+#define ULTRA_NIC_OFFSET 16 /* NIC register offset from the base_addr. */
+#define ULTRA_IO_EXTENT 32
+
+/* Probe for the Ultra. This looks like a 8013 with the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following.
+*/
+#ifdef HAVE_DEVLIST
+struct netdev_entry ultra_drv =
+{"ultra", ultra_probe1, NETCARD_IO_EXTENT, netcard_portlist};
+#else
+
+int ultra_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return ultra_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; ultra_portlist[i]; i++) {
+ int ioaddr = ultra_portlist[i];
+ if (check_region(ioaddr, ULTRA_IO_EXTENT))
+ continue;
+ if (ultra_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+int ultra_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ int checksum = 0;
+ const char *model_name;
+ unsigned char eeprom_irq = 0;
+ static unsigned version_printed = 0;
+ /* Values from various config regs. */
+ unsigned char num_pages, irqreg, addr;
+ unsigned char idreg = inb(ioaddr + 7);
+ unsigned char reg4 = inb(ioaddr + 4) & 0x7f;
+
+ /* Check the ID nibble. */
+ if ((idreg & 0xF0) != 0x20 /* SMC Ultra */
+ && (idreg & 0xF0) != 0x40) /* SMC EtherEZ */
+ return ENODEV;
+
+ /* Select the station address register set. */
+ outb(reg4, ioaddr + 4);
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if ((checksum & 0xff) != 0xFF)
+ return ENODEV;
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("smc-ultra.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ model_name = (idreg & 0xF0) == 0x20 ? "SMC Ultra" : "SMC EtherEZ";
+
+ printk("%s: %s at %#3x,", dev->name, model_name, ioaddr);
+
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+ /* Switch from the station address to the alternate register set and
+ read the useful registers there. */
+ outb(0x80 | reg4, ioaddr + 4);
+
+ /* Enabled FINE16 mode to avoid BIOS ROM width mismatches @ reboot. */
+ outb(0x80 | inb(ioaddr + 0x0c), ioaddr + 0x0c);
+ irqreg = inb(ioaddr + 0xd);
+ addr = inb(ioaddr + 0xb);
+
+ /* Switch back to the station address register set so that the MS-DOS driver
+ can find the card after a warm boot. */
+ outb(reg4, ioaddr + 4);
+
+ if (dev->irq < 2) {
+ unsigned char irqmap[] = {0, 9, 3, 5, 7, 10, 11, 15};
+ int irq;
+
+ /* The IRQ bits are split. */
+ irq = irqmap[((irqreg & 0x40) >> 4) + ((irqreg & 0x0c) >> 2)];
+
+ if (irq == 0) {
+ printk(", failed to detect IRQ line.\n");
+ return -EAGAIN;
+ }
+ dev->irq = irq;
+ eeprom_irq = 1;
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (", no memory for dev->priv.\n");
+ return -ENOMEM;
+ }
+
+ /* OK, we are certain this is going to work. Setup the device. */
+ request_region(ioaddr, ULTRA_IO_EXTENT, model_name);
+
+ /* The 8390 isn't at the base address, so fake the offset */
+ dev->base_addr = ioaddr+ULTRA_NIC_OFFSET;
+
+ {
+ int addr_tbl[4] = {0x0C0000, 0x0E0000, 0xFC0000, 0xFE0000};
+ short num_pages_tbl[4] = {0x20, 0x40, 0x80, 0xff};
+
+ dev->mem_start = ((addr & 0x0f) << 13) + addr_tbl[(addr >> 6) & 3] ;
+ num_pages = num_pages_tbl[(addr >> 4) & 3];
+ }
+
+ ei_status.name = model_name;
+ ei_status.word16 = 1;
+ ei_status.tx_start_page = START_PG;
+ ei_status.rx_start_page = START_PG + TX_PAGES;
+ ei_status.stop_page = num_pages;
+
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+ dev->mem_end = dev->rmem_end
+ = dev->mem_start + (ei_status.stop_page - START_PG)*256;
+
+ printk(",%s IRQ %d memory %#lx-%#lx.\n", eeprom_irq ? "" : "assigned ",
+ dev->irq, dev->mem_start, dev->mem_end-1);
+
+ ei_status.reset_8390 = &ultra_reset_8390;
+ ei_status.block_input = &ultra_block_input;
+ ei_status.block_output = &ultra_block_output;
+ ei_status.get_8390_hdr = &ultra_get_8390_hdr;
+ dev->open = &ultra_open;
+ dev->stop = &ultra_close_card;
+ NS8390_init(dev, 0);
+
+ return 0;
+}
+
+static int
+ultra_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC addr */
+
+ if (request_irq(dev->irq, ei_interrupt, 0, ei_status.name))
+ return -EAGAIN;
+
+ outb(ULTRA_MEMENB, ioaddr); /* Enable memory, 16 bit mode. */
+ outb(0x80, ioaddr + 5);
+ outb(0x01, ioaddr + 6); /* Enable interrupts and memory. */
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void
+ultra_reset_8390(struct device *dev)
+{
+ int cmd_port = dev->base_addr - ULTRA_NIC_OFFSET; /* ASIC base addr */
+
+ outb(ULTRA_RESET, cmd_port);
+ if (ei_debug > 1) printk("resetting Ultra, t=%ld...", jiffies);
+ ei_status.txing = 0;
+
+ outb(ULTRA_MEMENB, cmd_port);
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+ultra_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ unsigned long hdr_start = dev->mem_start + ((ring_page - START_PG)<<8);
+
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem on */
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* shmem off */
+}
+
+/* Block input and output are easy on shared memory ethercards, the only
+ complication is when the ring buffer wraps. */
+
+static void
+ultra_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ unsigned long xfer_start = dev->mem_start + ring_offset - (START_PG<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, xfer_start, count, 0);
+ }
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+static void
+ultra_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ unsigned long shmem = dev->mem_start + ((start_page - START_PG)<<8);
+
+ /* Enable shared memory. */
+ outb(ULTRA_MEMENB, dev->base_addr - ULTRA_NIC_OFFSET);
+
+ memcpy_toio(shmem, buf, count);
+
+ outb(0x00, dev->base_addr - ULTRA_NIC_OFFSET); /* Disable memory. */
+}
+
+static int
+ultra_close_card(struct device *dev)
+{
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET; /* CMDREG */
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+
+ outb(0x00, ioaddr + 6); /* Disable interrupts. */
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ NS8390_init(dev, 0);
+
+ /* We should someday disable shared memory and change to 8-bit mode
+ "just in case"... */
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_ULTRA_CARDS 4 /* Max number of Ultra cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_ULTRA_CARDS] = { 0, };
+static struct device dev_ultra[MAX_ULTRA_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_ULTRA_CARDS] = { 0, };
+static int irq[MAX_ULTRA_CARDS] = { 0, };
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ struct device *dev = &dev_ultra[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->init = ultra_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "smc-ultra.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "smc-ultra.c: No SMC Ultra card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_ULTRA_CARDS; this_dev++) {
+ struct device *dev = &dev_ultra[this_dev];
+ if (dev->priv != NULL) {
+ /* NB: ultra_close_card() does free_irq + irq2dev */
+ int ioaddr = dev->base_addr - ULTRA_NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ release_region(ioaddr, ULTRA_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -Wall -O6 -I/usr/src/linux/net/inet -c smc-ultra.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/tulip.c b/i386/i386at/gpl/linux/net/tulip.c
new file mode 100644
index 00000000..3386a9b8
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/tulip.c
@@ -0,0 +1,782 @@
+/* tulip.c: A DEC 21040 ethernet driver for linux. */
+/*
+ NOTICE: this version works with kernels 1.1.82 and later only!
+ Written 1994,1995 by Donald Becker.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ This driver is for the SMC EtherPower PCI ethernet adapter.
+ It should work with most other DEC 21*40-based ethercards.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+*/
+
+static const char *version = "tulip.c:v0.05 1/20/95 becker@cesdis.gsfc.nasa.gov\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/interrupt.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+
+/* The total size is unusually large: The 21040 aligns each of its 16
+ longword-wide registers on a quadword boundary. */
+#define TULIP_TOTAL_SIZE 0x80
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry tulip_drv =
+{"Tulip", tulip_pci_probe, TULIP_TOTAL_SIZE, NULL};
+#endif
+
+#define TULIP_DEBUG 1
+#ifdef TULIP_DEBUG
+int tulip_debug = TULIP_DEBUG;
+#else
+int tulip_debug = 1;
+#endif
+
+/*
+ Theory of Operation
+
+I. Board Compatibility
+
+This device driver is designed for the DECchip 21040 "Tulip", Digital's
+single-chip ethernet controller for PCI, as used on the SMC EtherPower
+ethernet adapter.
+
+II. Board-specific settings
+
+PCI bus devices are configured by the system at boot time, so no jumpers
+need to be set on the board. The system BIOS should be set to assign the
+PCI INTA signal to an otherwise unused system IRQ line. While it's
+physically possible to shared PCI interrupt lines, the kernel doesn't
+support it.
+
+III. Driver operation
+
+IIIa. Ring buffers
+The Tulip can use either ring buffers or lists of Tx and Rx descriptors.
+The current driver uses a statically allocated Rx ring of descriptors and
+buffers, and a list of the Tx buffers.
+
+IIIC. Synchronization
+The driver runs as two independent, single-threaded flows of control. One
+is the send-packet routine, which enforces single-threaded use by the
+dev->tbusy flag. The other thread is the interrupt handler, which is single
+threaded by the hardware and other software.
+
+The send packet thread has partial control over the Tx ring and 'dev->tbusy'
+flag. It sets the tbusy flag whenever it's queuing a Tx packet. If the next
+queue slot is empty, it clears the tbusy flag when finished otherwise it sets
+the 'tp->tx_full' flag.
+
+The interrupt handler has exclusive control over the Rx ring and records stats
+from the Tx ring. (The Tx-done interrupt can't be selectively turned off, so
+we can't avoid the interrupt overhead by having the Tx routine reap the Tx
+stats.) After reaping the stats, it marks the queue entry as empty by setting
+the 'base' to zero. Iff the 'tp->tx_full' flag is set, it clears both the
+tx_full and tbusy flags.
+
+IV. Notes
+
+Thanks to Duke Kamstra of SMC for providing an EtherPower board.
+
+The DEC databook doesn't document which Rx filter settings accept broadcast
+packets. Nor does it document how to configure the part to configure the
+serial subsystem for normal (vs. loopback) operation or how to have it
+autoswitch between internal 10baseT, SIA and AUI transceivers.
+
+The databook claims that CSR13, CSR14, and CSR15 should each be the last
+register of the set CSR12-15 written. Hmmm, now how is that possible?
+*/
+
+#define DEC_VENDOR_ID 0x1011 /* Hex 'D' :-> */
+#define DEC_21040_ID 0x0002 /* Change for 21140. */
+
+/* Keep the ring sizes a power of two for efficiency. */
+#define TX_RING_SIZE 4
+#define RX_RING_SIZE 4
+#define PKT_BUF_SZ 1536 /* Size of each temporary Rx buffer.*/
+
+/* Offsets to the Command and Status Registers, "CSRs". All accesses
+ must be longword instructions and quadword aligned. */
+enum tulip_offsets {
+ CSR0=0, CSR1=0x08, CSR2=0x10, CSR3=0x18, CSR4=0x20, CSR5=0x28,
+ CSR6=0x30, CSR7=0x38, CSR8=0x40, CSR9=0x48, CSR10=0x50, CSR11=0x58,
+ CSR12=0x60, CSR13=0x68, CSR14=0x70, CSR15=0x78 };
+
+/* The Tulip Rx and Tx buffer descriptors. */
+struct tulip_rx_desc {
+ int status;
+ int length;
+ char *buffer1, *buffer2; /* We use only buffer 1. */
+};
+
+struct tulip_tx_desc {
+ int status;
+ int length;
+ char *buffer1, *buffer2; /* We use only buffer 1. */
+};
+
+struct tulip_private {
+ char devname[8]; /* Used only for kernel debugging. */
+ struct tulip_rx_desc rx_ring[RX_RING_SIZE];
+ struct tulip_tx_desc tx_ring[TX_RING_SIZE];
+ /* The saved address of a sent-in-place packet/buffer, for skfree(). */
+ struct sk_buff* tx_skbuff[TX_RING_SIZE];
+ long rx_buffs; /* Address of temporary Rx buffers. */
+ struct enet_statistics stats;
+ int setup_frame[48]; /* Pseudo-Tx frame to init address table. */
+ unsigned int cur_rx, cur_tx; /* The next free ring entry */
+ unsigned int dirty_rx, dirty_tx; /* The ring entries to be free()ed. */
+ unsigned int tx_full:1;
+ int pad0, pad1; /* Used for 8-byte alignment */
+};
+
+static void tulip_probe1(int ioaddr, int irq);
+static int tulip_open(struct device *dev);
+static void tulip_init_ring(struct device *dev);
+static int tulip_start_xmit(struct sk_buff *skb, struct device *dev);
+static int tulip_rx(struct device *dev);
+static void tulip_interrupt(int irq, struct pt_regs *regs);
+static int tulip_close(struct device *dev);
+static struct enet_statistics *tulip_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static int set_mac_address(struct device *dev, void *addr);
+
+
+
+#ifndef MODULE
+/* This 21040 probe is unlike most other board probes. We can use memory
+ efficiently by allocating a large contiguous region and dividing it
+ ourselves. This is done by having the initialization occur before
+ the 'kmalloc()' memory management system is started. */
+
+int dec21040_init(void)
+{
+
+ if (pcibios_present()) {
+ int pci_index;
+ for (pci_index = 0; pci_index < 8; pci_index++) {
+ unsigned char pci_bus, pci_device_fn, pci_irq_line;
+ unsigned long pci_ioaddr;
+
+ if (pcibios_find_device (DEC_VENDOR_ID, DEC_21040_ID, pci_index,
+ &pci_bus, &pci_device_fn) != 0)
+ break;
+ pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &pci_irq_line);
+ pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &pci_ioaddr);
+ /* Remove I/O space marker in bit 0. */
+ pci_ioaddr &= ~3;
+ if (tulip_debug > 2)
+ printk("Found DEC PCI Tulip at I/O %#lx, IRQ %d.\n",
+ pci_ioaddr, pci_irq_line);
+ tulip_probe1(pci_ioaddr, pci_irq_line);
+ }
+ }
+
+ return 0;
+}
+#endif
+#ifdef MODULE
+static int tulip_probe(struct device *dev)
+{
+ printk("tulip: This driver does not yet install properly from module!\n");
+ return -1;
+}
+#endif
+
+static void tulip_probe1(int ioaddr, int irq)
+{
+ static int did_version = 0; /* Already printed version info. */
+ struct device *dev;
+ struct tulip_private *tp;
+ int i;
+
+ if (tulip_debug > 0 && did_version++ == 0)
+ printk(version);
+
+ dev = init_etherdev(0, 0);
+
+ printk("%s: DEC 21040 Tulip at %#3x,", dev->name, ioaddr);
+
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+ /* Clear the missed-packet counter. */
+ inl(ioaddr + CSR8) & 0xffff;
+
+ /* The station address ROM is read byte serially. The register must
+ be polled, waiting for the value to be read bit serially from the
+ EEPROM.
+ */
+ outl(0, ioaddr + CSR9); /* Reset the pointer with a dummy write. */
+ for (i = 0; i < 6; i++) {
+ int value, boguscnt = 100000;
+ do
+ value = inl(ioaddr + CSR9);
+ while (value < 0 && --boguscnt > 0);
+ printk(" %2.2x", dev->dev_addr[i] = value);
+ }
+ printk(", IRQ %d\n", irq);
+
+ /* We do a request_region() only to register /proc/ioports info. */
+ request_region(ioaddr, TULIP_TOTAL_SIZE, "DEC Tulip Ethernet");
+
+ dev->base_addr = ioaddr;
+ dev->irq = irq;
+
+ /* Make certain the data structures are quadword aligned. */
+ tp = kmalloc(sizeof(*tp), GFP_KERNEL | GFP_DMA);
+ dev->priv = tp;
+ tp->rx_buffs = kmalloc(PKT_BUF_SZ*RX_RING_SIZE, GFP_KERNEL | GFP_DMA);
+
+ /* The Tulip-specific entries in the device structure. */
+ dev->open = &tulip_open;
+ dev->hard_start_xmit = &tulip_start_xmit;
+ dev->stop = &tulip_close;
+ dev->get_stats = &tulip_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+ dev->set_mac_address = &set_mac_address;
+
+ return;
+}
+
+
+static int
+tulip_open(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+
+ /* Reset the chip, holding bit 0 set at least 10 PCI cycles. */
+ outl(0xfff80001, ioaddr + CSR0);
+ SLOW_DOWN_IO;
+ /* Deassert reset. Set 8 longword cache alignment, 8 longword burst.
+ Cache alignment bits 15:14 Burst length 13:8
+ 0000 No alignment 0x00000000 unlimited 0800 8 longwords
+ 4000 8 longwords 0100 1 longword 1000 16 longwords
+ 8000 16 longwords 0200 2 longwords 2000 32 longwords
+ C000 32 longwords 0400 4 longwords
+ Wait the specified 50 PCI cycles after a reset by initializing
+ Tx and Rx queues and the address filter list. */
+ outl(0xfff84800, ioaddr + CSR0);
+
+ if (irq2dev_map[dev->irq] != NULL
+ || (irq2dev_map[dev->irq] = dev) == NULL
+ || dev->irq == 0
+ || request_irq(dev->irq, &tulip_interrupt, 0, "DEC 21040 Tulip")) {
+ return -EAGAIN;
+ }
+
+ if (tulip_debug > 1)
+ printk("%s: tulip_open() irq %d.\n", dev->name, dev->irq);
+
+ tulip_init_ring(dev);
+
+ /* Fill the whole address filter table with our physical address. */
+ {
+ unsigned short *eaddrs = (unsigned short *)dev->dev_addr;
+ int *setup_frm = tp->setup_frame, i;
+
+ /* You must add the broadcast address when doing perfect filtering! */
+ *setup_frm++ = 0xffff;
+ *setup_frm++ = 0xffff;
+ *setup_frm++ = 0xffff;
+ /* Fill the rest of the accept table with our physical address. */
+ for (i = 1; i < 16; i++) {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ }
+ /* Put the setup frame on the Tx list. */
+ tp->tx_ring[0].length = 0x08000000 | 192;
+ tp->tx_ring[0].buffer1 = (char *)tp->setup_frame;
+ tp->tx_ring[0].buffer2 = 0;
+ tp->tx_ring[0].status = 0x80000000;
+
+ tp->cur_tx++, tp->dirty_tx++;
+ }
+
+ outl((int)tp->rx_ring, ioaddr + CSR3);
+ outl((int)tp->tx_ring, ioaddr + CSR4);
+
+ /* Turn on the xcvr interface. */
+ outl(0x00000000, ioaddr + CSR13);
+ outl(0x00000004, ioaddr + CSR13);
+
+ /* Start the chip's Tx and Rx processes. */
+ outl(0xfffe2002, ioaddr + CSR6);
+
+ /* Trigger an immediate transmit demand to process the setup frame. */
+ outl(0, ioaddr + CSR1);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ dev->start = 1;
+
+ /* Enable interrupts by setting the interrupt mask. */
+ outl(0xFFFFFFFF, ioaddr + CSR7);
+
+ if (tulip_debug > 2) {
+ printk("%s: Done tulip_open(), CSR0 %8.8x, CSR13 %8.8x.\n",
+ dev->name, inl(ioaddr + CSR0), inl(ioaddr + CSR13));
+ }
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/* Initialize the Rx and Tx rings, along with various 'dev' bits. */
+static void
+tulip_init_ring(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int i;
+
+ tp->tx_full = 0;
+ tp->cur_rx = tp->cur_tx = 0;
+ tp->dirty_rx = tp->dirty_tx = 0;
+
+ for (i = 0; i < RX_RING_SIZE; i++) {
+ tp->rx_ring[i].status = 0x80000000; /* Owned by Tulip chip */
+ tp->rx_ring[i].length = PKT_BUF_SZ;
+ tp->rx_ring[i].buffer1 = (char *)(tp->rx_buffs + i*PKT_BUF_SZ);
+ tp->rx_ring[i].buffer2 = (char *)&tp->rx_ring[i+1];
+ }
+ /* Mark the last entry as wrapping the ring. */
+ tp->rx_ring[i-1].length = PKT_BUF_SZ | 0x02000000;
+ tp->rx_ring[i-1].buffer2 = (char *)&tp->rx_ring[0];
+
+ /* The Tx buffer descriptor is filled in as needed, but we
+ do need to clear the ownership bit. */
+ for (i = 0; i < TX_RING_SIZE; i++) {
+ tp->tx_ring[i].status = 0x00000000;
+ }
+}
+
+static int
+tulip_start_xmit(struct sk_buff *skb, struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int entry;
+
+ /* Transmitter timeout, serious problems. */
+ if (dev->tbusy) {
+ int tickssofar = jiffies - dev->trans_start;
+ int i;
+ if (tickssofar < 20)
+ return 1;
+ printk("%s: transmit timed out, status %8.8x, SIA %8.8x %8.8x %8.8x %8.8x, resetting...\n",
+ dev->name, inl(ioaddr + CSR5), inl(ioaddr + CSR12),
+ inl(ioaddr + CSR13), inl(ioaddr + CSR14), inl(ioaddr + CSR15));
+ printk(" Rx ring %8.8x: ", (int)tp->rx_ring);
+ for (i = 0; i < RX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)tp->rx_ring[i].status);
+ printk("\n Tx ring %8.8x: ", (int)tp->tx_ring);
+ for (i = 0; i < TX_RING_SIZE; i++)
+ printk(" %8.8x", (unsigned int)tp->tx_ring[i].status);
+ printk("\n");
+
+ tp->stats.tx_errors++;
+ /* We should reinitialize the hardware here. */
+ dev->tbusy=0;
+ dev->trans_start = jiffies;
+ return 0;
+ }
+
+ if (skb == NULL || skb->len <= 0) {
+ printk("%s: Obsolete driver layer request made: skbuff==NULL.\n",
+ dev->name);
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well.
+ If this ever occurs the queue layer is doing something evil! */
+ if (set_bit(0, (void*)&dev->tbusy) != 0) {
+ printk("%s: Transmitter access conflict.\n", dev->name);
+ return 1;
+ }
+
+ /* Caution: the write order is important here, set the base address
+ with the "ownership" bits last. */
+
+ /* Calculate the next Tx descriptor entry. */
+ entry = tp->cur_tx % TX_RING_SIZE;
+
+ tp->tx_full = 1;
+ tp->tx_skbuff[entry] = skb;
+ tp->tx_ring[entry].length = skb->len |
+ (entry == TX_RING_SIZE-1 ? 0xe2000000 : 0xe0000000);
+ tp->tx_ring[entry].buffer1 = skb->data;
+ tp->tx_ring[entry].buffer2 = 0;
+ tp->tx_ring[entry].status = 0x80000000; /* Pass ownership to the chip. */
+
+ tp->cur_tx++;
+
+ /* Trigger an immediate transmit demand. */
+ outl(0, ioaddr + CSR1);
+
+ dev->trans_start = jiffies;
+
+ return 0;
+}
+
+/* The interrupt handler does all of the Rx thread work and cleans up
+ after the Tx thread. */
+static void tulip_interrupt(int irq, struct pt_regs *regs)
+{
+ struct device *dev = (struct device *)(irq2dev_map[irq]);
+ struct tulip_private *lp;
+ int csr5, ioaddr, boguscnt=10;
+
+ if (dev == NULL) {
+ printk ("tulip_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (struct tulip_private *)dev->priv;
+ if (dev->interrupt)
+ printk("%s: Re-entering the interrupt handler.\n", dev->name);
+
+ dev->interrupt = 1;
+
+ do {
+ csr5 = inl(ioaddr + CSR5);
+ /* Acknowledge all of the current interrupt sources ASAP. */
+ outl(csr5 & 0x0001ffff, ioaddr + CSR5);
+
+ if (tulip_debug > 4)
+ printk("%s: interrupt csr5=%#8.8x new csr5=%#8.8x.\n",
+ dev->name, csr5, inl(dev->base_addr + CSR5));
+
+ if ((csr5 & 0x00018000) == 0)
+ break;
+
+ if (csr5 & 0x0040) /* Rx interrupt */
+ tulip_rx(dev);
+
+ if (csr5 & 0x0001) { /* Tx-done interrupt */
+ int dirty_tx = lp->dirty_tx;
+
+ while (dirty_tx < lp->cur_tx) {
+ int entry = dirty_tx % TX_RING_SIZE;
+ int status = lp->tx_ring[entry].status;
+
+ if (status < 0)
+ break; /* It still hasn't been Txed */
+
+ if (status & 0x8000) {
+ /* There was an major error, log it. */
+ lp->stats.tx_errors++;
+ if (status & 0x4104) lp->stats.tx_aborted_errors++;
+ if (status & 0x0C00) lp->stats.tx_carrier_errors++;
+ if (status & 0x0200) lp->stats.tx_window_errors++;
+ if (status & 0x0002) lp->stats.tx_fifo_errors++;
+ if (status & 0x0080) lp->stats.tx_heartbeat_errors++;
+#ifdef ETHER_STATS
+ if (status & 0x0100) lp->stats.collisions16++;
+#endif
+ } else {
+#ifdef ETHER_STATS
+ if (status & 0x0001) lp->stats.tx_deferred++;
+#endif
+ lp->stats.collisions += (status >> 3) & 15;
+ lp->stats.tx_packets++;
+ }
+
+ /* Free the original skb. */
+ dev_kfree_skb(lp->tx_skbuff[entry], FREE_WRITE);
+ dirty_tx++;
+ }
+
+#ifndef final_version
+ if (lp->cur_tx - dirty_tx >= TX_RING_SIZE) {
+ printk("out-of-sync dirty pointer, %d vs. %d, full=%d.\n",
+ dirty_tx, lp->cur_tx, lp->tx_full);
+ dirty_tx += TX_RING_SIZE;
+ }
+#endif
+
+ if (lp->tx_full && dev->tbusy
+ && dirty_tx > lp->cur_tx - TX_RING_SIZE + 2) {
+ /* The ring is no longer full, clear tbusy. */
+ lp->tx_full = 0;
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ lp->dirty_tx = dirty_tx;
+ }
+
+ /* Log errors. */
+ if (csr5 & 0x8000) { /* Abnormal error summary bit. */
+ if (csr5 & 0x0008) lp->stats.tx_errors++; /* Tx babble. */
+ if (csr5 & 0x0100) { /* Missed a Rx frame. */
+ lp->stats.rx_errors++;
+ lp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+ }
+ if (csr5 & 0x0800) {
+ printk("%s: Something Wicked happened! %8.8x.\n",
+ dev->name, csr5);
+ /* Hmmmmm, it's not clear what to do here. */
+ }
+ }
+ if (--boguscnt < 0) {
+ printk("%s: Too much work at interrupt, csr5=0x%8.8x.\n",
+ dev->name, csr5);
+ /* Clear all interrupt sources. */
+ outl(0x0001ffff, ioaddr + CSR5);
+ break;
+ }
+ } while (1);
+
+ if (tulip_debug > 3)
+ printk("%s: exiting interrupt, csr5=%#4.4x.\n",
+ dev->name, inl(ioaddr + CSR5));
+
+ /* Special code for testing *only*. */
+ {
+ static int stopit = 10;
+ if (dev->start == 0 && --stopit < 0) {
+ printk("%s: Emergency stop, looping startup interrupt.\n",
+ dev->name);
+ free_irq(irq);
+ }
+ }
+
+ dev->interrupt = 0;
+ return;
+}
+
+static int
+tulip_rx(struct device *dev)
+{
+ struct tulip_private *lp = (struct tulip_private *)dev->priv;
+ int entry = lp->cur_rx % RX_RING_SIZE;
+ int i;
+
+ if (tulip_debug > 4)
+ printk(" In tulip_rx().\n");
+ /* If we own the next entry, it's a new packet. Send it up. */
+ while (lp->rx_ring[entry].status >= 0) {
+ int status = lp->rx_ring[entry].status;
+
+ if (tulip_debug > 4)
+ printk(" tulip_rx() status was %8.8x.\n", status);
+ if ((status & 0x0300) != 0x0300) {
+ printk("%s: Ethernet frame spanned multiple buffers, status %8.8x!\n",
+ dev->name, status);
+ } else if (status & 0x8000) {
+ /* There was a fatal error. */
+ lp->stats.rx_errors++; /* end of a packet.*/
+ if (status & 0x0890) lp->stats.rx_length_errors++;
+ if (status & 0x0004) lp->stats.rx_frame_errors++;
+ if (status & 0x0002) lp->stats.rx_crc_errors++;
+ if (status & 0x0001) lp->stats.rx_fifo_errors++;
+ } else {
+ /* Malloc up new buffer, compatible with net-2e. */
+ short pkt_len = lp->rx_ring[entry].status >> 16;
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len+2);
+ if (skb == NULL) {
+ printk("%s: Memory squeeze, deferring packet.\n", dev->name);
+ /* Check that at least two ring entries are free.
+ If not, free one and mark stats->rx_dropped++. */
+ for (i=0; i < RX_RING_SIZE; i++)
+ if (lp->rx_ring[(entry+i) % RX_RING_SIZE].status < 0)
+ break;
+
+ if (i > RX_RING_SIZE -2) {
+ lp->stats.rx_dropped++;
+ lp->rx_ring[entry].status = 0x80000000;
+ lp->cur_rx++;
+ }
+ break;
+ }
+ skb->dev = dev;
+ skb_reserve(skb,2); /* 16 byte align the data fields */
+ memcpy(skb_put(skb,pkt_len), lp->rx_ring[entry].buffer1, pkt_len);
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+
+ lp->rx_ring[entry].status = 0x80000000;
+ entry = (++lp->cur_rx) % RX_RING_SIZE;
+ }
+
+ return 0;
+}
+
+static int
+tulip_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+
+ dev->start = 0;
+ dev->tbusy = 1;
+
+ if (tulip_debug > 1)
+ printk("%s: Shutting down ethercard, status was %2.2x.\n",
+ dev->name, inl(ioaddr + CSR5));
+
+ /* Disable interrupts by clearing the interrupt mask. */
+ outl(0x00000000, ioaddr + CSR7);
+ /* Stop the chip's Tx and Rx processes. */
+ outl(inl(ioaddr + CSR6) & ~0x2002, ioaddr + CSR6);
+
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = 0;
+
+ MOD_DEC_USE_COUNT;
+ return 0;
+}
+
+static struct enet_statistics *
+tulip_get_stats(struct device *dev)
+{
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ short ioaddr = dev->base_addr;
+
+ tp->stats.rx_missed_errors += inl(ioaddr + CSR8) & 0xffff;
+
+ return &tp->stats;
+}
+
+/*
+ * Set or clear the multicast filter for this adaptor.
+ */
+
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+ int csr6 = inl(ioaddr + CSR6) & ~0x00D5;
+
+ if (dev->flags&IFF_PROMISC)
+ { /* Set promiscuous. */
+ outl(csr6 | 0x00C0, ioaddr + CSR6);
+ /* Log any net taps. */
+ printk("%s: Promiscuous mode enabled.\n", dev->name);
+ }
+ else if (dev->mc_count > 15 || (dev->flags&IFF_ALLMULTI))
+ {
+ /* Too many to filter perfectly -- accept all multicasts. */
+ outl(csr6 | 0x0080, ioaddr + CSR6);
+ }
+ else
+ {
+ struct tulip_private *tp = (struct tulip_private *)dev->priv;
+ struct dev_mc_list *dmi=dev->mc_list;
+ int *setup_frm = tp->setup_frame;
+ unsigned short *eaddrs;
+ int i;
+
+ /* We have <= 15 addresses that we can use the wonderful
+ 16 address perfect filtering of the Tulip. Note that only
+ the low shortword of setup_frame[] is valid. */
+ outl(csr6 | 0x0000, ioaddr + CSR6);
+ i=0;
+ while(dmi)
+ {
+ eaddrs=(unsigned short *)dmi->dmi_addr;
+ dmi=dmi->next;
+ i++;
+ *setup_frm++ = *eaddrs++;
+ *setup_frm++ = *eaddrs++;
+ *setup_frm++ = *eaddrs++;
+ }
+ /* Fill the rest of the table with our physical address. */
+ eaddrs = (unsigned short *)dev->dev_addr;
+ do {
+ *setup_frm++ = eaddrs[0];
+ *setup_frm++ = eaddrs[1];
+ *setup_frm++ = eaddrs[2];
+ } while (++i < 16);
+
+ /* Now add this frame to the Tx list. */
+ }
+}
+
+static int
+set_mac_address(struct device *dev, void *addr)
+{
+ int i;
+ struct sockaddr *sa=(struct sockaddr *)addr;
+ if (dev->start)
+ return -EBUSY;
+ printk("%s: Setting MAC address to ", dev->name);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = sa->sa_data[i]);
+ printk(".\n");
+ return 0;
+}
+
+#ifdef MODULE
+static char devicename[9] = { 0, };
+static struct device dev_tulip = {
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, tulip_probe
+};
+
+static int io = 0;
+static int irq = 0;
+
+int init_module(void)
+{
+ printk("tulip: Sorry, modularization is not completed\n");
+ return -EIO;
+#if 0
+ if (io == 0)
+ printk("tulip: You should not use auto-probing with insmod!\n");
+ dev_tulip.base_addr = io;
+ dev_tulip.irq = irq;
+ if (register_netdev(&dev_tulip) != 0) {
+ printk("tulip: register_netdev() returned non-zero.\n");
+ return -EIO;
+ }
+ return 0;
+#endif
+}
+
+void
+cleanup_module(void)
+{
+ unregister_netdev(&dev_tulip);
+}
+#endif /* MODULE */
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c tulip.c"
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/wavelan.c b/i386/i386at/gpl/linux/net/wavelan.c
new file mode 100644
index 00000000..4d09badd
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/wavelan.c
@@ -0,0 +1,2526 @@
+/*
+ * AT&T GIS (nee NCR) WaveLAN card:
+ * An Ethernet-like radio transceiver
+ * controlled by an Intel 82586 coprocessor.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/types.h>
+#include <linux/fcntl.h>
+#include <linux/interrupt.h>
+#include <linux/stat.h>
+#include <linux/ptrace.h>
+#include <linux/ioport.h>
+#include <linux/in.h>
+#include <linux/string.h>
+#include <linux/delay.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/errno.h>
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/malloc.h>
+#include <linux/timer.h>
+#include <linux/proc_fs.h>
+#define STRUCT_CHECK 1
+#ifdef MACH
+#include <net/i82586.h>
+#else
+#include "i82586.h"
+#endif
+#include "wavelan.h"
+
+#ifndef WAVELAN_DEBUG
+#define WAVELAN_DEBUG 0
+#endif /* WAVELAN_DEBUG */
+
+#define WATCHDOG_JIFFIES 512 /* TODO: express in HZ. */
+#define ENABLE_FULL_PROMISCUOUS 0x10000
+
+#define nels(a) (sizeof(a) / sizeof(a[0]))
+
+typedef struct device device;
+typedef struct enet_statistics en_stats;
+typedef struct net_local net_local;
+typedef struct timer_list timer_list;
+
+struct net_local
+{
+ en_stats stats;
+ unsigned int tx_n_in_use;
+ unsigned char nwid[2];
+ unsigned short hacr;
+ unsigned short rx_head;
+ unsigned short rx_last;
+ unsigned short tx_first_free;
+ unsigned short tx_first_in_use;
+ unsigned int nresets;
+ unsigned int correct_nwid;
+ unsigned int wrong_nwid;
+ unsigned int promiscuous;
+ unsigned int full_promiscuous;
+ timer_list watchdog;
+ device *dev;
+ net_local *prev;
+ net_local *next;
+};
+
+extern int wavelan_probe(device *); /* See Space.c */
+
+static const char *version = "wavelan.c:v7 95/4/8\n";
+
+/*
+ * Entry point forward declarations.
+ */
+static int wavelan_probe1(device *, unsigned short);
+static int wavelan_open(device *);
+static int wavelan_send_packet(struct sk_buff *, device *);
+static void wavelan_interrupt(int, struct pt_regs *);
+static int wavelan_close(device *);
+static en_stats *wavelan_get_stats(device *);
+static void wavelan_set_multicast_list(device *);
+static int wavelan_get_info(char*, char**, off_t, int, int);
+
+/*
+ * Other forward declarations.
+ */
+static void wavelan_cu_show_one(device *, net_local *, int, unsigned short);
+static void wavelan_cu_start(device *);
+static void wavelan_ru_start(device *);
+static void wavelan_watchdog(unsigned long);
+#if 0
+static void wavelan_psa_show(psa_t *);
+static void wavelan_mmc_show(unsigned short);
+#endif /* 0 */
+static void wavelan_scb_show(unsigned short);
+static void wavelan_ru_show(device *);
+static void wavelan_cu_show(device *);
+static void wavelan_dev_show(device *);
+static void wavelan_local_show(device *);
+
+static unsigned int wavelan_debug = WAVELAN_DEBUG;
+static net_local *first_wavelan = (net_local *)0;
+
+static
+unsigned long
+wavelan_splhi(void)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ return flags;
+}
+
+static
+void
+wavelan_splx(unsigned long flags)
+{
+ restore_flags(flags);
+}
+
+static
+unsigned short
+hasr_read(unsigned short ioaddr)
+{
+ return inw(HASR(ioaddr));
+}
+
+static
+void
+hacr_write(unsigned short ioaddr, int hacr)
+{
+ outw(hacr, HACR(ioaddr));
+}
+
+static
+void
+hacr_write_slow(unsigned short ioaddr, int hacr)
+{
+ hacr_write(ioaddr, hacr);
+ /* delay might only be needed sometimes */
+ udelay(1000);
+}
+
+/*
+ * Set the channel attention bit.
+ */
+static
+void
+set_chan_attn(unsigned short ioaddr, unsigned short current_hacr)
+{
+ hacr_write(ioaddr, current_hacr | HACR_CA);
+}
+
+/*
+ * Reset, and then set host adaptor into default mode.
+ */
+static
+void
+wavelan_reset(unsigned short ioaddr)
+{
+ hacr_write_slow(ioaddr, HACR_RESET);
+ hacr_write(ioaddr, HACR_DEFAULT);
+}
+
+static
+void
+wavelan_16_off(unsigned short ioaddr, unsigned short hacr)
+{
+ hacr &= ~HACR_16BITS;
+
+ hacr_write(ioaddr, hacr);
+}
+
+static
+void
+wavelan_16_on(unsigned short ioaddr, unsigned short hacr)
+{
+ hacr |= HACR_16BITS;
+
+ hacr_write(ioaddr, hacr);
+}
+
+static
+void
+wavelan_ints_off(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ lp->hacr &= ~HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+
+ wavelan_splx(x);
+}
+
+static
+void
+wavelan_ints_on(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ lp->hacr |= HACR_INTRON;
+ hacr_write(ioaddr, lp->hacr);
+
+ wavelan_splx(x);
+}
+
+/*
+ * Read bytes from the PSA.
+ */
+static
+void
+psa_read(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+{
+ wavelan_16_off(ioaddr, hacr);
+
+ while (n-- > 0)
+ {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ *b++ = inb(PIOP2(ioaddr));
+ }
+
+ wavelan_16_on(ioaddr, hacr);
+}
+
+#if defined(IRQ_SET_WORKS)
+/*
+ * Write bytes to the PSA.
+ */
+static
+void
+psa_write(unsigned short ioaddr, unsigned short hacr, int o, unsigned char *b, int n)
+{
+ wavelan_16_off(ioaddr, hacr);
+
+ while (n-- > 0)
+ {
+ outw(o, PIOR2(ioaddr));
+ o++;
+ outb(*b, PIOP2(ioaddr));
+ b++;
+ }
+
+ wavelan_16_on(ioaddr, hacr);
+}
+#endif /* defined(IRQ_SET_WORKS) */
+
+/*
+ * Read bytes from the on-board RAM.
+ */
+static
+void
+obram_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+
+ outw(o, PIOR1(ioaddr));
+
+ insw(PIOP1(ioaddr), (unsigned short *)b, n);
+}
+
+/*
+ * Write bytes to the on-board RAM.
+ */
+static
+void
+obram_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ n = (n + 1) / (sizeof(unsigned short) / sizeof(unsigned char));
+
+ outw(o, PIOR1(ioaddr));
+
+ outsw(PIOP1(ioaddr), (unsigned short *)b, n);
+}
+
+/*
+ * Read bytes from the MMC.
+ */
+static
+void
+mmc_read(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ while (n-- > 0)
+ {
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ outw(o << 1, MMCR(ioaddr));
+ o++;
+
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ *b++ = (unsigned char)(inw(MMCR(ioaddr)) >> 8);
+ }
+}
+
+/*
+ * Write bytes to the MMC.
+ */
+static
+void
+mmc_write(unsigned short ioaddr, unsigned short o, unsigned char *b, int n)
+{
+ while (n-- > 0)
+ {
+ while (inw(HASR(ioaddr)) & HASR_MMC_BUSY)
+ ;
+
+ outw((unsigned short)(((unsigned short)*b << 8) | (o << 1) | 1), MMCR(ioaddr));
+ b++;
+ o++;
+ }
+}
+
+static int irqvals[] =
+{
+ 0, 0, 0, 0x01,
+ 0x02, 0x04, 0, 0x08,
+ 0, 0, 0x10, 0x20,
+ 0x40, 0, 0, 0x80,
+};
+
+#if defined(IRQ_SET_WORKS)
+static
+int
+wavelan_unmap_irq(int irq, unsigned char *irqval)
+{
+ if (irq < 0 || irq >= nels(irqvals) || irqvals[irq] == 0)
+ return -1;
+
+ *irqval = (unsigned char)irqvals[irq];
+
+ return 0;
+}
+#endif /* defined(IRQ_SET_WORKS) */
+
+/*
+ * Map values from the irq parameter register to irq numbers.
+ */
+static
+int
+wavelan_map_irq(unsigned char irqval)
+{
+ int irq;
+
+ for (irq = 0; irq < nels(irqvals); irq++)
+ {
+ if (irqvals[irq] == (int)irqval)
+ return irq;
+ }
+
+ return -1;
+}
+
+/*
+ * Initialize the Modem Management Controller.
+ */
+static
+void
+wavelan_mmc_init(device *dev, psa_t *psa)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ mmw_t m;
+ int configured;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+ memset(&m, 0x00, sizeof(m));
+
+ /*
+ * configured = psa->psa_conf_status & 1;
+ *
+ * For now we use the persistent PSA
+ * information as little as possible, thereby
+ * allowing us to return to the same known state
+ * during a hardware reset.
+ */
+ configured = 0;
+
+ /*
+ * Set default modem control parameters.
+ * See NCR document 407-0024326 Rev. A.
+ */
+ m.mmw_jabber_enable = 0x01;
+ m.mmw_anten_sel = MMW_ANTEN_SEL_ALG_EN;
+ m.mmw_ifs = 0x20;
+ m.mmw_mod_delay = 0x04;
+ m.mmw_jam_time = 0x38;
+
+ m.mmw_encr_enable = 0;
+ m.mmw_des_io_invert = 0;
+ m.mmw_freeze = 0;
+ m.mmw_decay_prm = 0;
+ m.mmw_decay_updat_prm = 0;
+
+ if (configured)
+ {
+ /*
+ * Use configuration defaults from parameter storage area.
+ */
+ if (psa->psa_undefined & 1)
+ m.mmw_loopt_sel = 0x00;
+ else
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+
+ m.mmw_thr_pre_set = psa->psa_thr_pre_set & 0x3F;
+ m.mmw_quality_thr = psa->psa_quality_thr & 0x0F;
+ }
+ else
+ {
+ if (lp->promiscuous && lp->full_promiscuous)
+ m.mmw_loopt_sel = MMW_LOOPT_SEL_UNDEFINED;
+ else
+ m.mmw_loopt_sel = 0x00;
+
+ /*
+ * 0x04 for AT,
+ * 0x01 for MCA.
+ */
+ if (psa->psa_comp_number & 1)
+ m.mmw_thr_pre_set = 0x01;
+ else
+ m.mmw_thr_pre_set = 0x04;
+
+ m.mmw_quality_thr = 0x03;
+ }
+
+ m.mmw_netw_id_l = lp->nwid[1];
+ m.mmw_netw_id_h = lp->nwid[0];
+
+ mmc_write(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+}
+
+static
+void
+wavelan_ack(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cs;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ scb_cs &= SCB_ST_INT;
+
+ if (scb_cs == 0)
+ return;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_ack(): board not accepting command.\n", dev->name);
+}
+
+/*
+ * Set channel attention bit and busy wait until command has
+ * completed, then acknowledge the command completion.
+ */
+static
+int
+wavelan_synchronous_cmd(device *dev, const char *str)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cmd;
+ ach_t cb;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ scb_cmd = SCB_CMD_CUC & SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 64; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_C)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0 || !(cb.ac_status & AC_SFLD_OK))
+ {
+ printk("%s: %s failed; status = 0x%x\n", dev->name, str, cb.ac_status);
+ wavelan_scb_show(ioaddr);
+ return -1;
+ }
+
+ wavelan_ack(dev);
+
+ return 0;
+}
+
+static
+int
+wavelan_hardware_reset(device *dev)
+{
+ unsigned short ioaddr;
+ psa_t psa;
+ net_local *lp;
+ scp_t scp;
+ iscp_t iscp;
+ scb_t scb;
+ ach_t cb;
+ int i;
+ ac_cfg_t cfg;
+ ac_ias_t ias;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_hardware_reset(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ lp->nresets++;
+
+ wavelan_reset(ioaddr);
+ lp->hacr = HACR_DEFAULT;
+
+ /*
+ * Clear the onboard RAM.
+ */
+ {
+ unsigned char zeroes[512];
+
+ memset(&zeroes[0], 0x00, sizeof(zeroes));
+
+ for (i = 0; i < I82586_MEMZ; i += sizeof(zeroes))
+ obram_write(ioaddr, i, &zeroes[0], sizeof(zeroes));
+ }
+
+ psa_read(ioaddr, lp->hacr, 0, (unsigned char *)&psa, sizeof(psa));
+
+ wavelan_mmc_init(dev, &psa);
+
+ /*
+ * Construct the command unit structures:
+ * scp, iscp, scb, cb.
+ */
+ memset(&scp, 0x00, sizeof(scp));
+ scp.scp_sysbus = SCP_SY_16BBUS;
+ scp.scp_iscpl = OFFSET_ISCP;
+ obram_write(ioaddr, OFFSET_SCP, (unsigned char *)&scp, sizeof(scp));
+
+ memset(&iscp, 0x00, sizeof(iscp));
+ iscp.iscp_busy = 1;
+ iscp.iscp_offset = OFFSET_SCB;
+ obram_write(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+ memset(&scb, 0x00, sizeof(scb));
+ scb.scb_command = SCB_CMD_RESET;
+ scb.scb_cbl_offset = OFFSET_CU;
+ scb.scb_rfa_offset = OFFSET_RU;
+ obram_write(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_ISCP, (unsigned char *)&iscp, sizeof(iscp));
+
+ if (iscp.iscp_busy == (unsigned short)0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ {
+ printk("%s: wavelan_hardware_reset(): iscp_busy timeout.\n", dev->name);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ for (i = 15; i > 0; i--)
+ {
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ if (scb.scb_status == (SCB_ST_CX | SCB_ST_CNA))
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ {
+ printk("%s: wavelan_hardware_reset(): status: expected 0x%02x, got 0x%02x.\n", dev->name, SCB_ST_CX | SCB_ST_CNA, scb.scb_status);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ wavelan_ack(dev);
+
+ memset(&cb, 0x00, sizeof(cb));
+ cb.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_diagnose);
+ cb.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+
+ if (wavelan_synchronous_cmd(dev, "diag()") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ obram_read(ioaddr, OFFSET_CU, (unsigned char *)&cb, sizeof(cb));
+ if (cb.ac_status & AC_SFLD_FAIL)
+ {
+ printk("%s: wavelan_hardware_reset(): i82586 Self Test failed.\n", dev->name);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+ return -1;
+ }
+
+ memset(&cfg, 0x00, sizeof(cfg));
+
+#if 0
+ /*
+ * The default board configuration.
+ */
+ cfg.fifolim_bytecnt = 0x080c;
+ cfg.addrlen_mode = 0x2600;
+ cfg.linprio_interframe = 0x7820; /* IFS=120, ACS=2 */
+ cfg.slot_time = 0xf00c; /* slottime=12 */
+ cfg.hardware = 0x0008; /* tx even w/o CD */
+ cfg.min_frame_len = 0x0040;
+#endif /* 0 */
+
+ /*
+ * For Linux we invert AC_CFG_ALOC(..) so as to conform
+ * to the way that net packets reach us from above.
+ * (See also ac_tx_t.)
+ */
+ cfg.cfg_byte_cnt = AC_CFG_BYTE_CNT(sizeof(ac_cfg_t) - sizeof(ach_t));
+ cfg.cfg_fifolim = AC_CFG_FIFOLIM(8);
+ cfg.cfg_byte8 = AC_CFG_SAV_BF(0) |
+ AC_CFG_SRDY(0);
+ cfg.cfg_byte9 = AC_CFG_ELPBCK(0) |
+ AC_CFG_ILPBCK(0) |
+ AC_CFG_PRELEN(AC_CFG_PLEN_2) |
+ AC_CFG_ALOC(1) |
+ AC_CFG_ADDRLEN(WAVELAN_ADDR_SIZE);
+ cfg.cfg_byte10 = AC_CFG_BOFMET(0) |
+ AC_CFG_ACR(0) |
+ AC_CFG_LINPRIO(0);
+ cfg.cfg_ifs = 32;
+ cfg.cfg_slotl = 0;
+ cfg.cfg_byte13 = AC_CFG_RETRYNUM(15) |
+ AC_CFG_SLTTMHI(2);
+ cfg.cfg_byte14 = AC_CFG_FLGPAD(0) |
+ AC_CFG_BTSTF(0) |
+ AC_CFG_CRC16(0) |
+ AC_CFG_NCRC(0) |
+ AC_CFG_TNCRS(1) |
+ AC_CFG_MANCH(0) |
+ AC_CFG_BCDIS(0) |
+ AC_CFG_PRM(lp->promiscuous);
+ cfg.cfg_byte15 = AC_CFG_ICDS(0) |
+ AC_CFG_CDTF(0) |
+ AC_CFG_ICSS(0) |
+ AC_CFG_CSTF(0);
+/*
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(64);
+*/
+ cfg.cfg_min_frm_len = AC_CFG_MNFRM(8);
+
+ cfg.cfg_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_configure);
+ cfg.cfg_h.ac_link = OFFSET_CU;
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&cfg, sizeof(cfg));
+
+ if (wavelan_synchronous_cmd(dev, "reset()-configure") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+
+ return -1;
+ }
+
+ memset(&ias, 0x00, sizeof(ias));
+ ias.ias_h.ac_command = AC_CFLD_EL | (AC_CFLD_CMD & acmd_ia_setup);
+ ias.ias_h.ac_link = OFFSET_CU;
+ memcpy(&ias.ias_addr[0], (unsigned char *)&dev->dev_addr[0], sizeof(ias.ias_addr));
+ obram_write(ioaddr, OFFSET_CU, (unsigned char *)&ias, sizeof(ias));
+
+ if (wavelan_synchronous_cmd(dev, "reset()-address") == -1)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): -1\n", dev->name);
+
+ return -1;
+ }
+
+ wavelan_ints_on(dev);
+
+ if (wavelan_debug > 4)
+ wavelan_scb_show(ioaddr);
+
+ wavelan_ru_start(dev);
+ wavelan_cu_start(dev);
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_hardware_reset(): 0\n", dev->name);
+
+ return 0;
+}
+
+#if STRUCT_CHECK == 1
+
+static
+const char *
+wavelan_struct_check(void)
+{
+#define SC(t,s,n) if (sizeof(t) != s) return n
+ SC(psa_t, PSA_SIZE, "psa_t");
+ SC(mmw_t, MMW_SIZE, "mmw_t");
+ SC(mmr_t, MMR_SIZE, "mmr_t");
+ SC(ha_t, HA_SIZE, "ha_t");
+#undef SC
+
+ return (char *)0;
+}
+
+#endif /* STRUCT_CHECK == 1 */
+
+/*
+ * Check for a network adaptor of this type.
+ * Return '0' iff one exists.
+ * (There seem to be different interpretations of
+ * the initial value of dev->base_addr.
+ * We follow the example in drivers/net/ne.c.)
+ */
+int
+wavelan_probe(device *dev)
+{
+ int i;
+ int r;
+ short base_addr;
+ static unsigned short iobase[] =
+ {
+#if 0
+ Leave out 0x3C0 for now -- seems to clash
+ with some video controllers.
+ Leave out the others too -- we will always
+ use 0x390 and leave 0x300 for the Ethernet device.
+ 0x300, 0x390, 0x3E0, 0x3C0,
+#endif /* 0 */
+ 0x390,
+ };
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_probe(dev=0x%x (base_addr=0x%x))\n", dev->name, (unsigned int)dev, (unsigned int)dev->base_addr);
+
+#if STRUCT_CHECK == 1
+ if (wavelan_struct_check() != (char *)0)
+ {
+ printk("%s: structure/compiler botch: \"%s\"\n", dev->name, wavelan_struct_check());
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+
+ return ENODEV;
+ }
+#endif /* STRUCT_CHECK == 1 */
+
+ base_addr = dev->base_addr;
+
+ if (base_addr < 0)
+ {
+ /*
+ * Don't probe at all.
+ */
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENXIO\n", dev->name);
+ return ENXIO;
+ }
+
+ if (base_addr > 0x100)
+ {
+ /*
+ * Check a single specified location.
+ */
+ r = wavelan_probe1(dev, base_addr);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): %d\n", dev->name, r);
+ return r;
+ }
+
+ for (i = 0; i < nels(iobase); i++)
+ {
+ if (check_region(iobase[i], sizeof(ha_t)))
+ continue;
+
+ if (wavelan_probe1(dev, iobase[i]) == 0)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): 0\n", dev->name);
+ proc_net_register(&(struct proc_dir_entry) {
+ PROC_NET_WAVELAN, 7, "wavelan",
+ S_IFREG | S_IRUGO, 1, 0, 0,
+ 0, &proc_net_inode_operations,
+ wavelan_get_info
+ });
+
+ return 0;
+ }
+ }
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe(): ENODEV\n", dev->name);
+
+ return ENODEV;
+}
+
+static
+int
+wavelan_probe1(device *dev, unsigned short ioaddr)
+{
+ psa_t psa;
+ int irq;
+ int i;
+ net_local *lp;
+ int enable_full_promiscuous;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_probe1(dev=0x%x, ioaddr=0x%x)\n", dev->name, (unsigned int)dev, ioaddr);
+
+ wavelan_reset(ioaddr);
+
+ psa_read(ioaddr, HACR_DEFAULT, 0, (unsigned char *)&psa, sizeof(psa));
+
+ /*
+ * Check the first three octets of the MAC address
+ * for the manufacturer's code.
+ */
+ if
+ (
+ psa.psa_univ_mac_addr[0] != SA_ADDR0
+ ||
+ psa.psa_univ_mac_addr[1] != SA_ADDR1
+ ||
+ psa.psa_univ_mac_addr[2] != SA_ADDR2
+ )
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): ENODEV\n", dev->name);
+ return ENODEV;
+ }
+
+ printk("%s: WaveLAN at %#x,", dev->name, ioaddr);
+
+ if (dev->irq != 0)
+ {
+ printk("[WARNING: explicit IRQ value %d ignored: using PSA value instead]", dev->irq);
+#if defined(IRQ_SET_WORKS)
+Leave this out until I can get it to work -- BJ.
+ if (wavelan_unmap_irq(dev->irq, &psa.psa_int_req_no) == -1)
+ {
+ printk(" could not wavelan_unmap_irq(%d, ..) -- ignored.\n", dev->irq);
+ dev->irq = 0;
+ }
+ else
+ {
+ psa_write(ioaddr, HACR_DEFAULT, (char *)&psa.psa_int_req_no - (char *)&psa, (unsigned char *)&psa.psa_int_req_no, sizeof(psa.psa_int_req_no));
+ wavelan_reset(ioaddr);
+ }
+#endif /* defined(IRQ_SET_WORKS) */
+ }
+
+ if ((irq = wavelan_map_irq(psa.psa_int_req_no)) == -1)
+ {
+ printk(" could not wavelan_map_irq(%d).\n", psa.psa_int_req_no);
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): EAGAIN\n", dev->name);
+ return EAGAIN;
+ }
+
+ dev->irq = irq;
+
+ request_region(ioaddr, sizeof(ha_t), "wavelan");
+ dev->base_addr = ioaddr;
+
+ /*
+ * The third numeric argument to LILO's
+ * `ether=' control line arrives here as `dev->mem_start'.
+ *
+ * If bit 16 of dev->mem_start is non-zero we enable
+ * full promiscuity.
+ *
+ * If either of the least significant two bytes of
+ * dev->mem_start are non-zero we use them instead
+ * of the PSA NWID.
+ */
+ enable_full_promiscuous = (dev->mem_start & ENABLE_FULL_PROMISCUOUS) == ENABLE_FULL_PROMISCUOUS;
+ dev->mem_start &= ~ENABLE_FULL_PROMISCUOUS;
+
+ if (dev->mem_start != 0)
+ {
+ psa.psa_nwid[0] = (dev->mem_start >> 8) & 0xFF;
+ psa.psa_nwid[1] = (dev->mem_start >> 0) & 0xFF;
+ }
+
+ dev->mem_start = 0x0000;
+ dev->mem_end = 0x0000;
+ dev->if_port = 0;
+
+ memcpy(&dev->dev_addr[0], &psa.psa_univ_mac_addr[0], WAVELAN_ADDR_SIZE);
+
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? " " : ":", dev->dev_addr[i]);
+
+ printk(", IRQ %d", dev->irq);
+ if (enable_full_promiscuous)
+ printk(", promisc");
+ printk(", nwid 0x%02x%02x", psa.psa_nwid[0], psa.psa_nwid[1]);
+
+ printk(", PC");
+ switch (psa.psa_comp_number)
+ {
+ case PSA_COMP_PC_AT_915:
+ case PSA_COMP_PC_AT_2400:
+ printk("-AT");
+ break;
+
+ case PSA_COMP_PC_MC_915:
+ case PSA_COMP_PC_MC_2400:
+ printk("-MC");
+ break;
+
+ case PSA_COMP_PCMCIA_915:
+ printk("MCIA");
+ break;
+
+ default:
+ printk("???");
+ break;
+ }
+
+ printk(", ");
+ switch (psa.psa_subband)
+ {
+ case PSA_SUBBAND_915:
+ printk("915");
+ break;
+
+ case PSA_SUBBAND_2425:
+ printk("2425");
+ break;
+
+ case PSA_SUBBAND_2460:
+ printk("2460");
+ break;
+
+ case PSA_SUBBAND_2484:
+ printk("2484");
+ break;
+
+ case PSA_SUBBAND_2430_5:
+ printk("2430.5");
+ break;
+
+ default:
+ printk("???");
+ break;
+ }
+ printk(" MHz");
+
+ printk("\n");
+
+ if (wavelan_debug > 0)
+ printk(version);
+
+ dev->priv = kmalloc(sizeof(net_local), GFP_KERNEL);
+ if (dev->priv == NULL)
+ return -ENOMEM;
+ memset(dev->priv, 0x00, sizeof(net_local));
+ lp = (net_local *)dev->priv;
+
+ if (first_wavelan == (net_local *)0)
+ {
+ first_wavelan = lp;
+ lp->prev = lp;
+ lp->next = lp;
+ }
+ else
+ {
+ lp->prev = first_wavelan->prev;
+ lp->next = first_wavelan;
+ first_wavelan->prev->next = lp;
+ first_wavelan->prev = lp;
+ }
+ lp->dev = dev;
+
+ lp->hacr = HACR_DEFAULT;
+
+ lp->full_promiscuous = enable_full_promiscuous;
+ lp->nwid[0] = psa.psa_nwid[0];
+ lp->nwid[1] = psa.psa_nwid[1];
+
+ lp->watchdog.function = wavelan_watchdog;
+ lp->watchdog.data = (unsigned long)dev;
+
+ dev->open = wavelan_open;
+ dev->stop = wavelan_close;
+ dev->hard_start_xmit = wavelan_send_packet;
+ dev->get_stats = wavelan_get_stats;
+ dev->set_multicast_list = &wavelan_set_multicast_list;
+
+ /*
+ * Fill in the fields of the device structure
+ * with ethernet-generic values.
+ */
+ ether_setup(dev);
+
+ dev->flags &= ~IFF_MULTICAST; /* Not yet supported */
+
+ dev->mtu = WAVELAN_MTU;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_probe1(): 0\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Construct the fd and rbd structures.
+ * Start the receive unit.
+ */
+static
+void
+wavelan_ru_start(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cs;
+ fd_t fd;
+ rbd_t rbd;
+ unsigned short rx;
+ unsigned short rx_next;
+ int i;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if ((scb_cs & SCB_ST_RUS) == SCB_ST_RUS_RDY)
+ return;
+
+ lp->rx_head = OFFSET_RU;
+
+ for (i = 0, rx = lp->rx_head; i < NRXBLOCKS; i++, rx = rx_next)
+ {
+ rx_next = (i == NRXBLOCKS - 1) ? lp->rx_head : rx + RXBLOCKZ;
+
+ fd.fd_status = 0;
+ fd.fd_command = (i == NRXBLOCKS - 1) ? FD_COMMAND_EL : 0;
+ fd.fd_link_offset = rx_next;
+ fd.fd_rbd_offset = rx + sizeof(fd);
+ obram_write(ioaddr, rx, (unsigned char *)&fd, sizeof(fd));
+
+ rbd.rbd_status = 0;
+ rbd.rbd_next_rbd_offset = I82586NULL;
+ rbd.rbd_bufl = rx + sizeof(fd) + sizeof(rbd);
+ rbd.rbd_bufh = 0;
+ rbd.rbd_el_size = RBD_EL | (RBD_SIZE & MAXDATAZ);
+ obram_write(ioaddr, rx + sizeof(fd), (unsigned char *)&rbd, sizeof(rbd));
+
+ lp->rx_last = rx;
+ }
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_rfa_offset), (unsigned char *)&lp->rx_head, sizeof(lp->rx_head));
+
+ scb_cs = SCB_CMD_RUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_ru_start(): board not accepting command.\n", dev->name);
+}
+
+/*
+ * Initialise the transmit blocks.
+ * Start the command unit executing the NOP
+ * self-loop of the first transmit block.
+ */
+static
+void
+wavelan_cu_start(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ int i;
+ unsigned short txblock;
+ unsigned short first_nop;
+ unsigned short scb_cs;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ lp->tx_first_free = OFFSET_CU;
+ lp->tx_first_in_use = I82586NULL;
+
+ for
+ (
+ i = 0, txblock = OFFSET_CU;
+ i < NTXBLOCKS;
+ i++, txblock += TXBLOCKZ
+ )
+ {
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ tx.tx_h.ac_status = 0;
+ tx.tx_h.ac_command = acmd_transmit | AC_CFLD_I;
+ tx.tx_h.ac_link = nop_addr;
+ tx.tx_tbd_offset = tbd_addr;
+ obram_write(ioaddr, tx_addr, (unsigned char *)&tx, sizeof(tx));
+
+ nop.nop_h.ac_status = 0;
+ nop.nop_h.ac_command = acmd_nop;
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, nop_addr, (unsigned char *)&nop, sizeof(nop));
+
+ tbd.tbd_status = TBD_STATUS_EOF;
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+ }
+
+ first_nop = OFFSET_CU + (NTXBLOCKS - 1) * TXBLOCKZ + sizeof(ac_tx_t);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_cbl_offset), (unsigned char *)&first_nop, sizeof(first_nop));
+
+ scb_cs = SCB_CMD_CUC_GO;
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ for (i = 1000; i > 0; i--)
+ {
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cs, sizeof(scb_cs));
+ if (scb_cs == 0)
+ break;
+
+ udelay(1000);
+ }
+
+ if (i <= 0)
+ printk("%s: wavelan_cu_start(): board not accepting command.\n", dev->name);
+
+ lp->tx_n_in_use = 0;
+ dev->tbusy = 0;
+}
+
+static
+int
+wavelan_open(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned long x;
+ int r;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_open(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ if (dev->irq == 0)
+ {
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -ENXIO\n", dev->name);
+ return -ENXIO;
+ }
+
+ if
+ (
+ irq2dev_map[dev->irq] != (device *)0
+ /* This is always true, but avoid the false IRQ. */
+ ||
+ (irq2dev_map[dev->irq] = dev) == (device *)0
+ ||
+ request_irq(dev->irq, &wavelan_interrupt, 0, "WaveLAN") != 0
+ )
+ {
+ irq2dev_map[dev->irq] = (device *)0;
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -EAGAIN\n", dev->name);
+ return -EAGAIN;
+ }
+
+ x = wavelan_splhi();
+ if ((r = wavelan_hardware_reset(dev)) != -1)
+ {
+ dev->interrupt = 0;
+ dev->start = 1;
+ }
+ wavelan_splx(x);
+
+ if (r == -1)
+ {
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = (device *)0;
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): -EAGAIN(2)\n", dev->name);
+ return -EAGAIN;
+ }
+
+ MOD_INC_USE_COUNT;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_open(): 0\n", dev->name);
+
+ return 0;
+}
+
+static
+void
+hardware_send_packet(device *dev, void *buf, short length)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short txblock;
+ unsigned short txpred;
+ unsigned short tx_addr;
+ unsigned short nop_addr;
+ unsigned short tbd_addr;
+ unsigned short buf_addr;
+ ac_tx_t tx;
+ ac_nop_t nop;
+ tbd_t tbd;
+ unsigned long x;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ x = wavelan_splhi();
+
+ txblock = lp->tx_first_free;
+ txpred = txblock - TXBLOCKZ;
+ if (txpred < OFFSET_CU)
+ txpred += NTXBLOCKS * TXBLOCKZ;
+ lp->tx_first_free += TXBLOCKZ;
+ if (lp->tx_first_free >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_free -= NTXBLOCKS * TXBLOCKZ;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ lp->tx_n_in_use++;
+
+ tx_addr = txblock;
+ nop_addr = tx_addr + sizeof(tx);
+ tbd_addr = nop_addr + sizeof(nop);
+ buf_addr = tbd_addr + sizeof(tbd);
+
+ /*
+ * Transmit command.
+ */
+ tx.tx_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_tx_t, tx_addr, tx_h.ac_status), (unsigned char *)&tx.tx_h.ac_status, sizeof(tx.tx_h.ac_status));
+
+ /*
+ * NOP command.
+ */
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = nop_addr;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
+
+ /*
+ * Transmit buffer descriptor.
+ */
+ tbd.tbd_status = TBD_STATUS_EOF | (TBD_STATUS_ACNT & length);
+ tbd.tbd_next_bd_offset = I82586NULL;
+ tbd.tbd_bufl = buf_addr;
+ tbd.tbd_bufh = 0;
+ obram_write(ioaddr, tbd_addr, (unsigned char *)&tbd, sizeof(tbd));
+
+ /*
+ * Data.
+ */
+ obram_write(ioaddr, buf_addr, buf, length);
+
+ /*
+ * Overwrite the predecessor NOP link
+ * so that it points to this txblock.
+ */
+ nop_addr = txpred + sizeof(tx);
+ nop.nop_h.ac_status = 0;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_status), (unsigned char *)&nop.nop_h.ac_status, sizeof(nop.nop_h.ac_status));
+ nop.nop_h.ac_link = txblock;
+ obram_write(ioaddr, toff(ac_nop_t, nop_addr, nop_h.ac_link), (unsigned char *)&nop.nop_h.ac_link, sizeof(nop.nop_h.ac_link));
+
+ if (lp->tx_first_in_use == I82586NULL)
+ lp->tx_first_in_use = txblock;
+
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ dev->tbusy = 0;
+
+ dev->trans_start = jiffies;
+
+ if (lp->watchdog.next == (timer_list *)0)
+ wavelan_watchdog((unsigned long)dev);
+
+ wavelan_splx(x);
+
+ if (wavelan_debug > 4)
+ {
+ unsigned char *a;
+
+ a = (unsigned char *)buf;
+
+ printk
+ (
+ "%s: tx: dest %02x:%02x:%02x:%02x:%02x:%02x, length %d, tbd.tbd_bufl 0x%x.\n",
+ dev->name,
+ a[0], a[1], a[2], a[3], a[4], a[5],
+ length,
+ buf_addr
+ );
+ }
+}
+
+static
+int
+wavelan_send_packet(struct sk_buff *skb, device *dev)
+{
+ unsigned short ioaddr;
+
+ ioaddr = dev->base_addr;
+
+ if (dev->tbusy)
+ {
+ /*
+ * If we get here, some higher level
+ * has decided we are broken.
+ */
+ int tickssofar;
+
+ tickssofar = jiffies - dev->trans_start;
+
+ /*
+ * But for the moment, we will rely on wavelan_watchdog()
+ * instead as it allows finer control over exactly when we
+ * make the determination of failure.
+ *
+ if (tickssofar < 5)
+ */
+ return 1;
+
+ wavelan_scb_show(ioaddr);
+ wavelan_ru_show(dev);
+ wavelan_cu_show(dev);
+ wavelan_dev_show(dev);
+ wavelan_local_show(dev);
+
+ printk("%s: transmit timed out -- resetting board.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ /*
+ * If some higher layer thinks we've missed
+ * a tx-done interrupt we are passed NULL.
+ * Caution: dev_tint() handles the cli()/sti() itself.
+ */
+ if (skb == (struct sk_buff *)0)
+ {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /*
+ * Block a timer-based transmit from overlapping.
+ */
+ if (set_bit(0, (void *)&dev->tbusy) == 0)
+ {
+ short length;
+ unsigned char *buf;
+
+ length = (ETH_ZLEN < skb->len) ? skb->len : ETH_ZLEN;
+ buf = skb->data;
+
+ hardware_send_packet(dev, buf, length);
+ }
+ else
+ printk("%s: Transmitter access conflict.\n", dev->name);
+
+ dev_kfree_skb(skb, FREE_WRITE);
+
+ return 0;
+}
+
+#if 0
+static
+int
+addrcmp(unsigned char *a0, unsigned char *a1)
+{
+ int i;
+
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ {
+ if (a0[i] != a1[i])
+ return a0[i] - a1[i];
+ }
+
+ return 0;
+}
+#endif /* 0 */
+
+/*
+ * Transfer as many packets as we can
+ * from the device RAM.
+ * Called by the interrupt handler.
+ */
+static
+void
+wavelan_receive(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ int nreaped;
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+ nreaped = 0;
+
+ for (;;)
+ {
+ fd_t fd;
+ rbd_t rbd;
+ ushort pkt_len;
+ int sksize;
+ struct sk_buff *skb;
+
+ obram_read(ioaddr, lp->rx_head, (unsigned char *)&fd, sizeof(fd));
+
+ if ((fd.fd_status & FD_STATUS_C) != FD_STATUS_C)
+ break;
+
+ nreaped++;
+
+ if
+ (
+ (fd.fd_status & (FD_STATUS_B | FD_STATUS_OK))
+ !=
+ (FD_STATUS_B | FD_STATUS_OK)
+ )
+ {
+ /*
+ * Not sure about this one -- it does not seem
+ * to be an error so we will keep quiet about it.
+ if ((fd.fd_status & FD_STATUS_B) != FD_STATUS_B)
+ printk("%s: frame not consumed by RU.\n", dev->name);
+ */
+
+ if ((fd.fd_status & FD_STATUS_OK) != FD_STATUS_OK)
+ printk("%s: frame not received successfully.\n", dev->name);
+ }
+
+ if ((fd.fd_status & (FD_STATUS_S6 | FD_STATUS_S7 | FD_STATUS_S8 | FD_STATUS_S9 | FD_STATUS_S10 | FD_STATUS_S11)) != 0)
+ {
+ lp->stats.rx_errors++;
+
+ if ((fd.fd_status & FD_STATUS_S6) != 0)
+ printk("%s: no EOF flag.\n", dev->name);
+
+ if ((fd.fd_status & FD_STATUS_S7) != 0)
+ {
+ lp->stats.rx_length_errors++;
+ printk("%s: frame too short.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S8) != 0)
+ {
+ lp->stats.rx_over_errors++;
+ printk("%s: rx DMA overrun.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S9) != 0)
+ {
+ lp->stats.rx_fifo_errors++;
+ printk("%s: ran out of resources.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S10) != 0)
+ {
+ lp->stats.rx_frame_errors++;
+ printk("%s: alignment error.\n", dev->name);
+ }
+
+ if ((fd.fd_status & FD_STATUS_S11) != 0)
+ {
+ lp->stats.rx_crc_errors++;
+ printk("%s: CRC error.\n", dev->name);
+ }
+ }
+
+ if (fd.fd_rbd_offset == I82586NULL)
+ printk("%s: frame has no data.\n", dev->name);
+ else
+ {
+ obram_read(ioaddr, fd.fd_rbd_offset, (unsigned char *)&rbd, sizeof(rbd));
+
+ if ((rbd.rbd_status & RBD_STATUS_EOF) != RBD_STATUS_EOF)
+ printk("%s: missing EOF flag.\n", dev->name);
+
+ if ((rbd.rbd_status & RBD_STATUS_F) != RBD_STATUS_F)
+ printk("%s: missing F flag.\n", dev->name);
+
+ pkt_len = rbd.rbd_status & RBD_STATUS_ACNT;
+
+#if 0
+ {
+ unsigned char addr[WAVELAN_ADDR_SIZE];
+ int i;
+ static unsigned char toweraddr[WAVELAN_ADDR_SIZE] =
+ {
+ 0x08, 0x00, 0x0e, 0x20, 0x3e, 0xd3,
+ };
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
+ if
+ (
+ /*
+ addrcmp(&addr[0], &dev->dev_addr[0]) != 0
+ &&
+ */
+ addrcmp(&addr[0], toweraddr) != 0
+ )
+ {
+ printk("%s: foreign MAC source addr=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+ }
+ }
+#endif /* 0 */
+
+ if (wavelan_debug > 5)
+ {
+ unsigned char addr[WAVELAN_ADDR_SIZE];
+ unsigned short ltype;
+ int i;
+
+#if 0
+ printk("%s: fd_dest=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", fd.fd_dest[i]);
+ printk("\n");
+
+ printk("%s: fd_src=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", fd.fd_src[i]);
+ printk("\n");
+ printk("%s: fd_length=%d\n", dev->name, fd.fd_length);
+#endif /* 0 */
+
+ obram_read(ioaddr, rbd.rbd_bufl, &addr[0], sizeof(addr));
+ printk("%s: dest=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr), &addr[0], sizeof(addr));
+ printk("%s: src=", dev->name);
+ for (i = 0; i < WAVELAN_ADDR_SIZE; i++)
+ printk("%s%02x", (i == 0) ? "" : ":", addr[i]);
+ printk("\n");
+
+ obram_read(ioaddr, rbd.rbd_bufl + sizeof(addr) * 2, (unsigned char *)&ltype, sizeof(ltype));
+ printk("%s: ntohs(length/type)=0x%04x\n", dev->name, ntohs(ltype));
+ }
+
+ sksize = pkt_len;
+
+ if ((skb = dev_alloc_skb(sksize)) == (struct sk_buff *)0)
+ {
+ printk("%s: could not alloc_skb(%d, GFP_ATOMIC).\n", dev->name, sksize);
+ lp->stats.rx_dropped++;
+ }
+ else
+ {
+ skb->dev = dev;
+
+ obram_read(ioaddr, rbd.rbd_bufl, skb_put(skb,pkt_len), pkt_len);
+
+ if (wavelan_debug > 5)
+ {
+ int i;
+ int maxi;
+
+ printk("%s: pkt_len=%d, data=\"", dev->name, pkt_len);
+
+ if ((maxi = pkt_len) > 16)
+ maxi = 16;
+
+ for (i = 0; i < maxi; i++)
+ {
+ unsigned char c;
+
+ c = skb->data[i];
+ if (c >= ' ' && c <= '~')
+ printk(" %c", skb->data[i]);
+ else
+ printk("%02x", skb->data[i]);
+ }
+
+ if (maxi < pkt_len)
+ printk("..");
+
+ printk("\"\n\n");
+ }
+
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+
+ lp->stats.rx_packets++;
+ }
+ }
+
+ fd.fd_status = 0;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_status), (unsigned char *)&fd.fd_status, sizeof(fd.fd_status));
+
+ fd.fd_command = FD_COMMAND_EL;
+ obram_write(ioaddr, fdoff(lp->rx_head, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
+
+ fd.fd_command = 0;
+ obram_write(ioaddr, fdoff(lp->rx_last, fd_command), (unsigned char *)&fd.fd_command, sizeof(fd.fd_command));
+
+ lp->rx_last = lp->rx_head;
+ lp->rx_head = fd.fd_link_offset;
+ }
+
+/*
+ if (nreaped > 1)
+ printk("r%d", nreaped);
+*/
+}
+
+/*
+ * Command completion interrupt.
+ * Reclaim as many freed tx buffers as we can.
+ */
+static
+int
+wavelan_complete(device *dev, unsigned short ioaddr, net_local *lp)
+{
+ int nreaped;
+
+ nreaped = 0;
+
+ for (;;)
+ {
+ unsigned short tx_status;
+
+ if (lp->tx_first_in_use == I82586NULL)
+ break;
+
+ obram_read(ioaddr, acoff(lp->tx_first_in_use, ac_status), (unsigned char *)&tx_status, sizeof(tx_status));
+
+ if ((tx_status & AC_SFLD_C) == 0)
+ break;
+
+ nreaped++;
+
+ --lp->tx_n_in_use;
+
+/*
+if (lp->tx_n_in_use > 0)
+ printk("%c", "0123456789abcdefghijk"[lp->tx_n_in_use]);
+*/
+
+ if (lp->tx_n_in_use <= 0)
+ lp->tx_first_in_use = I82586NULL;
+ else
+ {
+ lp->tx_first_in_use += TXBLOCKZ;
+ if (lp->tx_first_in_use >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ lp->tx_first_in_use -= NTXBLOCKS * TXBLOCKZ;
+ }
+
+ if (tx_status & AC_SFLD_OK)
+ {
+ int ncollisions;
+
+ lp->stats.tx_packets++;
+ ncollisions = tx_status & AC_SFLD_MAXCOL;
+ lp->stats.collisions += ncollisions;
+ /*
+ if (ncollisions > 0)
+ printk("%s: tx completed after %d collisions.\n", dev->name, ncollisions);
+ */
+ }
+ else
+ {
+ lp->stats.tx_errors++;
+ if (tx_status & AC_SFLD_S10)
+ {
+ lp->stats.tx_carrier_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: no CS.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S9)
+ {
+ lp->stats.tx_carrier_errors++;
+ printk("%s: tx error: lost CTS.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S8)
+ {
+ lp->stats.tx_fifo_errors++;
+ printk("%s: tx error: slow DMA.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S6)
+ {
+ lp->stats.tx_heartbeat_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: heart beat.\n", dev->name);
+ }
+ if (tx_status & AC_SFLD_S5)
+ {
+ lp->stats.tx_aborted_errors++;
+ if (wavelan_debug > 0)
+ printk("%s: tx error: too many collisions.\n", dev->name);
+ }
+ }
+
+ if (wavelan_debug > 5)
+ printk("%s: tx completed, tx_status 0x%04x.\n", dev->name, tx_status);
+ }
+
+/*
+ if (nreaped > 1)
+ printk("c%d", nreaped);
+*/
+
+ /*
+ * Inform upper layers.
+ */
+ if (lp->tx_n_in_use < NTXBLOCKS - 1)
+ {
+ dev->tbusy = 0;
+ mark_bh(NET_BH);
+ }
+
+ return nreaped;
+}
+
+static
+void
+wavelan_watchdog(unsigned long a)
+{
+ device *dev;
+ net_local *lp;
+ unsigned short ioaddr;
+ unsigned long x;
+ unsigned int nreaped;
+
+ x = wavelan_splhi();
+
+ dev = (device *)a;
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ if (lp->tx_n_in_use <= 0)
+ {
+ wavelan_splx(x);
+ return;
+ }
+
+ lp->watchdog.expires = jiffies+WATCHDOG_JIFFIES;
+ add_timer(&lp->watchdog);
+
+ if (jiffies - dev->trans_start < WATCHDOG_JIFFIES)
+ {
+ wavelan_splx(x);
+ return;
+ }
+
+ nreaped = wavelan_complete(dev, ioaddr, lp);
+
+ printk("%s: warning: wavelan_watchdog(): %d reaped, %d remain.\n", dev->name, nreaped, lp->tx_n_in_use);
+ /*
+ wavelan_scb_show(ioaddr);
+ wavelan_ru_show(dev);
+ wavelan_cu_show(dev);
+ wavelan_dev_show(dev);
+ wavelan_local_show(dev);
+ */
+
+ wavelan_splx(x);
+}
+
+static
+void
+wavelan_interrupt(int irq, struct pt_regs *regs)
+{
+ device *dev;
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short hasr;
+ unsigned short status;
+ unsigned short ack_cmd;
+
+ if ((dev = (device *)(irq2dev_map[irq])) == (device *)0)
+ {
+ printk("wavelan_interrupt(): irq %d for unknown device.\n", irq);
+ return;
+ }
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ dev->interrupt = 1;
+
+ if ((hasr = hasr_read(ioaddr)) & HASR_MMC_INTR)
+ {
+ unsigned char dce_status;
+
+ /*
+ * Interrupt from the modem management controller.
+ * This will clear it -- ignored for now.
+ */
+ mmc_read(ioaddr, mmroff(0, mmr_dce_status), &dce_status, sizeof(dce_status));
+ if (wavelan_debug > 0)
+ printk("%s: warning: wavelan_interrupt(): unexpected mmc interrupt: status 0x%04x.\n", dev->name, dce_status);
+ }
+
+ if ((hasr & HASR_82586_INTR) == 0)
+ {
+ dev->interrupt = 0;
+ if (wavelan_debug > 0)
+ printk("%s: warning: wavelan_interrupt() but (hasr & HASR_82586_INTR) == 0.\n", dev->name);
+ return;
+ }
+
+ obram_read(ioaddr, scboff(OFFSET_SCB, scb_status), (unsigned char *)&status, sizeof(status));
+
+ /*
+ * Acknowledge the interrupt(s).
+ */
+ ack_cmd = status & SCB_ST_INT;
+
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&ack_cmd, sizeof(ack_cmd));
+
+ set_chan_attn(ioaddr, lp->hacr);
+
+ if (wavelan_debug > 5)
+ printk("%s: interrupt, status 0x%04x.\n", dev->name, status);
+
+ if ((status & SCB_ST_CX) == SCB_ST_CX)
+ {
+ /*
+ * Command completed.
+ */
+ if (wavelan_debug > 5)
+ printk("%s: command completed.\n", dev->name);
+ (void)wavelan_complete(dev, ioaddr, lp);
+ }
+
+ if ((status & SCB_ST_FR) == SCB_ST_FR)
+ {
+ /*
+ * Frame received.
+ */
+ if (wavelan_debug > 5)
+ printk("%s: received packet.\n", dev->name);
+ wavelan_receive(dev);
+ }
+
+ if
+ (
+ (status & SCB_ST_CNA) == SCB_ST_CNA
+ ||
+ (((status & SCB_ST_CUS) != SCB_ST_CUS_ACTV) && dev->start)
+ )
+ {
+ printk("%s: warning: CU inactive -- restarting.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ if
+ (
+ (status & SCB_ST_RNR) == SCB_ST_RNR
+ ||
+ (((status & SCB_ST_RUS) != SCB_ST_RUS_RDY) && dev->start)
+ )
+ {
+ printk("%s: warning: RU not ready -- restarting.\n", dev->name);
+
+ (void)wavelan_hardware_reset(dev);
+ }
+
+ dev->interrupt = 0;
+}
+
+static
+int
+wavelan_close(device *dev)
+{
+ unsigned short ioaddr;
+ net_local *lp;
+ unsigned short scb_cmd;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_close(dev=0x%x)\n", dev->name, (unsigned int)dev);
+
+ ioaddr = dev->base_addr;
+ lp = (net_local *)dev->priv;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ /*
+ * Flush the Tx and disable Rx.
+ */
+ scb_cmd = (SCB_CMD_CUC & SCB_CMD_CUC_SUS) | (SCB_CMD_RUC & SCB_CMD_RUC_SUS);
+ obram_write(ioaddr, scboff(OFFSET_SCB, scb_command), (unsigned char *)&scb_cmd, sizeof(scb_cmd));
+ set_chan_attn(ioaddr, lp->hacr);
+
+ wavelan_ints_off(dev);
+
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = (device *)0;
+
+ /*
+ * Release the ioport-region.
+ */
+ release_region(ioaddr, sizeof(ha_t));
+
+ MOD_DEC_USE_COUNT;
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_close(): 0\n", dev->name);
+
+ return 0;
+}
+
+/*
+ * Get the current statistics.
+ * This may be called with the card open or closed.
+ */
+static
+en_stats *
+wavelan_get_stats(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+static
+void
+wavelan_set_multicast_list(device *dev)
+{
+ net_local *lp;
+ unsigned long x;
+
+ if (wavelan_debug > 0)
+ printk("%s: ->wavelan_set_multicast_list(dev=0x%x)", dev->name, dev);
+
+ lp = (net_local *)dev->priv;
+
+ if(dev->flags&IFF_PROMISC)
+ {
+ /*
+ * Promiscuous mode: receive all packets.
+ */
+ lp->promiscuous = 1;
+ x = wavelan_splhi();
+ (void)wavelan_hardware_reset(dev);
+ wavelan_splx(x);
+ }
+#if MULTICAST_IS_ADDED
+ else if((dev->flags&IFF_ALLMULTI)||dev->mc_list)
+ {
+
+
+ }
+#endif
+ else
+ {
+ /*
+ * Normal mode: disable promiscuous mode,
+ * clear multicast list.
+ */
+ lp->promiscuous = 0;
+ x = wavelan_splhi();
+ (void)wavelan_hardware_reset(dev);
+ wavelan_splx(x);
+ }
+
+ if (wavelan_debug > 0)
+ printk("%s: <-wavelan_set_multicast_list()\n", dev->name);
+}
+
+/*
+ * Extra WaveLAN-specific device data.
+ * "cat /proc/net/wavelan" -- see fs/proc/net.c.
+ */
+static
+int
+sprintf_stats(char *buffer, device *dev)
+{
+ net_local *lp;
+ unsigned char v;
+ mmr_t m;
+
+ lp = (net_local *)dev->priv;
+
+ if (lp == (net_local *)0)
+ return sprintf(buffer, "%6s: No statistics available.\n", dev->name);
+
+ v = (unsigned char)1;
+ mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
+
+ mmc_read(dev->base_addr, mmroff(0, mmr_dce_status), &m.mmr_dce_status, sizeof(m.mmr_dce_status));
+ mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_h), &m.mmr_correct_nwid_h, sizeof(m.mmr_correct_nwid_h));
+ mmc_read(dev->base_addr, mmroff(0, mmr_correct_nwid_l), &m.mmr_correct_nwid_l, sizeof(m.mmr_correct_nwid_l));
+ mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_h), &m.mmr_wrong_nwid_h, sizeof(m.mmr_wrong_nwid_h));
+ mmc_read(dev->base_addr, mmroff(0, mmr_wrong_nwid_l), &m.mmr_wrong_nwid_l, sizeof(m.mmr_wrong_nwid_l));
+ mmc_read(dev->base_addr, mmroff(0, mmr_signal_lvl), &m.mmr_signal_lvl, sizeof(m.mmr_signal_lvl));
+ mmc_read(dev->base_addr, mmroff(0, mmr_silence_lvl), &m.mmr_silence_lvl, sizeof(m.mmr_silence_lvl));
+ mmc_read(dev->base_addr, mmroff(0, mmr_sgnl_qual), &m.mmr_sgnl_qual, sizeof(m.mmr_sgnl_qual));
+
+ v = (unsigned char)0;
+ mmc_write(dev->base_addr, mmwoff(0, mmw_freeze), &v, sizeof(v));
+
+ lp->correct_nwid += (m.mmr_correct_nwid_h << 8) | m.mmr_correct_nwid_l;
+ lp->wrong_nwid += (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l;
+
+ return sprintf
+ (
+ buffer,
+ "%6s: %02x %08x %08x %02x %02x %02x %02x %u\n",
+ dev->name,
+ m.mmr_dce_status,
+ lp->correct_nwid,
+ lp->wrong_nwid,
+ m.mmr_signal_lvl,
+ m.mmr_silence_lvl,
+ m.mmr_sgnl_qual,
+ lp->tx_n_in_use,
+ lp->nresets
+ );
+}
+
+static int
+wavelan_get_info(char *buffer, char **start, off_t offset, int length, int dummy)
+{
+ int len;
+ off_t begin;
+ off_t pos;
+ int size;
+ unsigned long x;
+
+ len = 0;
+ begin = 0;
+ pos = 0;
+
+ size = sprintf(buffer, "%s", "Iface | dce +nwid -nwid lvl slnc qual ntxq nrst\n");
+
+ pos += size;
+ len += size;
+
+ x = wavelan_splhi();
+
+ if (first_wavelan != (net_local *)0)
+ {
+ net_local *lp;
+
+ lp = first_wavelan;
+ do
+ {
+ size = sprintf_stats(buffer + len, lp->dev);
+
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset)
+ {
+ len = 0;
+ begin = pos;
+ }
+
+ if (pos > offset + length)
+ break;
+ }
+ while ((lp = lp->next) != first_wavelan);
+ }
+
+ wavelan_splx(x);
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ len = length; /* Ending slop */
+
+ return len;
+}
+
+#if defined(MODULE)
+static char devicename[9] = { 0, };
+static struct device dev_wavelan =
+{
+ devicename, /* device name is inserted by linux/drivers/net/net_init.c */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, wavelan_probe
+};
+
+static int io = 0x390; /* Default from above.. */
+static int irq = 0;
+
+int
+init_module(void)
+{
+ dev_wavelan.base_addr = io;
+ dev_wavelan.irq = irq;
+ if (register_netdev(&dev_wavelan) != 0)
+ return -EIO;
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ proc_net_unregister(PROC_NET_WAVELAN);
+ unregister_netdev(&dev_wavelan);
+ kfree_s(dev_wavelan.priv, sizeof(struct net_local));
+ dev_wavelan.priv = NULL;
+}
+#endif /* defined(MODULE) */
+
+static
+void
+wavelan_cu_show_one(device *dev, net_local *lp, int i, unsigned short p)
+{
+ unsigned short ioaddr;
+ ac_tx_t actx;
+
+ ioaddr = dev->base_addr;
+
+ printk("%d: 0x%x:", i, p);
+
+ obram_read(ioaddr, p, (unsigned char *)&actx, sizeof(actx));
+ printk(" status=0x%x,", actx.tx_h.ac_status);
+ printk(" command=0x%x,", actx.tx_h.ac_command);
+
+/*
+ {
+ tbd_t tbd;
+
+ obram_read(ioaddr, actx.tx_tbd_offset, (unsigned char *)&tbd, sizeof(tbd));
+ printk(" tbd_status=0x%x,", tbd.tbd_status);
+ }
+*/
+
+ printk("|");
+}
+
+#if 0
+static
+void
+wavelan_psa_show(psa_t *p)
+{
+ printk("psa:");
+
+ printk("psa_io_base_addr_1: 0x%02x,", p->psa_io_base_addr_1);
+ printk("psa_io_base_addr_2: 0x%02x,", p->psa_io_base_addr_2);
+ printk("psa_io_base_addr_3: 0x%02x,", p->psa_io_base_addr_3);
+ printk("psa_io_base_addr_4: 0x%02x,", p->psa_io_base_addr_4);
+ printk("psa_rem_boot_addr_1: 0x%02x,", p->psa_rem_boot_addr_1);
+ printk("psa_rem_boot_addr_2: 0x%02x,", p->psa_rem_boot_addr_2);
+ printk("psa_rem_boot_addr_3: 0x%02x,", p->psa_rem_boot_addr_3);
+ printk("psa_holi_params: 0x%02x,", p->psa_holi_params);
+ printk("psa_int_req_no: %d,", p->psa_int_req_no);
+ printk
+ (
+ "psa_univ_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_univ_mac_addr[0],
+ p->psa_univ_mac_addr[1],
+ p->psa_univ_mac_addr[2],
+ p->psa_univ_mac_addr[3],
+ p->psa_univ_mac_addr[4],
+ p->psa_univ_mac_addr[5]
+ );
+ printk
+ (
+ "psa_local_mac_addr[]: %02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_local_mac_addr[0],
+ p->psa_local_mac_addr[1],
+ p->psa_local_mac_addr[2],
+ p->psa_local_mac_addr[3],
+ p->psa_local_mac_addr[4],
+ p->psa_local_mac_addr[5]
+ );
+ printk("psa_univ_local_sel: %d,", p->psa_univ_local_sel);
+ printk("psa_comp_number: %d,", p->psa_comp_number);
+ printk("psa_thr_pre_set: 0x%02x,", p->psa_thr_pre_set);
+ printk("psa_feature_select/decay_prm: 0x%02x,", p->psa_feature_select);
+ printk("psa_subband/decay_update_prm: %d,", p->psa_subband);
+ printk("psa_quality_thr: 0x%02x,", p->psa_quality_thr);
+ printk("psa_mod_delay: 0x%02x,", p->psa_mod_delay);
+ printk("psa_nwid: 0x%02x%02x,", p->psa_nwid[0], p->psa_nwid[1]);
+ printk("psa_undefined: %d,", p->psa_undefined);
+ printk("psa_encryption_select: %d,", p->psa_encryption_select);
+ printk
+ (
+ "psa_encryption_key[]: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x,",
+ p->psa_encryption_key[0],
+ p->psa_encryption_key[1],
+ p->psa_encryption_key[2],
+ p->psa_encryption_key[3],
+ p->psa_encryption_key[4],
+ p->psa_encryption_key[5],
+ p->psa_encryption_key[6],
+ p->psa_encryption_key[7]
+ );
+ printk("psa_databus_width: %d,", p->psa_databus_width);
+ printk("psa_call_code/auto_squelch: 0x%02x,", p->psa_call_code);
+ printk("psa_no_of_retries: %d,", p->psa_no_of_retries);
+ printk("psa_acr: %d,", p->psa_acr);
+ printk("psa_dump_count: %d,", p->psa_dump_count);
+ printk("psa_nwid_prefix: 0x%02x,", p->psa_nwid_prefix);
+ printk("psa_conf_status: %d,", p->psa_conf_status);
+ printk("psa_crc: 0x%02x%02x,", p->psa_crc[0], p->psa_crc[1]);
+ printk("psa_crc_status: 0x%02x,", p->psa_crc_status);
+
+ printk("\n");
+}
+
+static
+void
+wavelan_mmc_show(unsigned short ioaddr)
+{
+ mmr_t m;
+
+ mmc_read(ioaddr, 0, (unsigned char *)&m, sizeof(m));
+
+ printk("mmr:");
+ printk(" des_status: 0x%x", m.mmr_des_status);
+ printk(" des_avail: 0x%x", m.mmr_des_avail);
+ printk(" des_io_invert: 0x%x", m.mmr_des_io_invert);
+ printk
+ (
+ " dce_status: 0x%x[%s%s%s%s]",
+ m.mmr_dce_status & 0x0F,
+ (m.mmr_dce_status & MMR_DCE_STATUS_ENERG_DET) ? "energy detected," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_LOOPT_IND) ? "loop test indicated," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_XMTITR_IND) ? "transmitter on," : "",
+ (m.mmr_dce_status & MMR_DCE_STATUS_JBR_EXPIRED) ? "jabber timer expired," : ""
+ );
+ printk(" correct_nwid: %d", m.mmr_correct_nwid_h << 8 | m.mmr_correct_nwid_l);
+ printk(" wrong_nwid: %d", (m.mmr_wrong_nwid_h << 8) | m.mmr_wrong_nwid_l);
+ printk(" thr_pre_set: 0x%x", m.mmr_thr_pre_set);
+ printk(" signal_lvl: %d", m.mmr_signal_lvl);
+ printk(" silence_lvl: %d", m.mmr_silence_lvl);
+ printk(" sgnl_qual: 0x%x", m.mmr_sgnl_qual);
+ printk(" netw_id_l: %x", m.mmr_netw_id_l);
+
+ printk("\n");
+}
+#endif /* 0 */
+
+static
+void
+wavelan_scb_show(unsigned short ioaddr)
+{
+ scb_t scb;
+
+ obram_read(ioaddr, OFFSET_SCB, (unsigned char *)&scb, sizeof(scb));
+
+ printk("scb:");
+
+ printk(" status:");
+ printk
+ (
+ " stat 0x%x[%s%s%s%s]",
+ (scb.scb_status & (SCB_ST_CX | SCB_ST_FR | SCB_ST_CNA | SCB_ST_RNR)) >> 12,
+ (scb.scb_status & SCB_ST_CX) ? "cmd completion interrupt," : "",
+ (scb.scb_status & SCB_ST_FR) ? "frame received," : "",
+ (scb.scb_status & SCB_ST_CNA) ? "cmd unit not active," : "",
+ (scb.scb_status & SCB_ST_RNR) ? "rcv unit not ready," : ""
+ );
+ printk
+ (
+ " cus 0x%x[%s%s%s]",
+ (scb.scb_status & SCB_ST_CUS) >> 8,
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_CUS) == SCB_ST_CUS_ACTV) ? "active" : ""
+ );
+ printk
+ (
+ " rus 0x%x[%s%s%s%s]",
+ (scb.scb_status & SCB_ST_RUS) >> 4,
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_IDLE) ? "idle" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_SUSP) ? "suspended" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_NRES) ? "no resources" : "",
+ ((scb.scb_status & SCB_ST_RUS) == SCB_ST_RUS_RDY) ? "ready" : ""
+ );
+
+ printk(" command:");
+ printk
+ (
+ " ack 0x%x[%s%s%s%s]",
+ (scb.scb_command & (SCB_CMD_ACK_CX | SCB_CMD_ACK_FR | SCB_CMD_ACK_CNA | SCB_CMD_ACK_RNR)) >> 12,
+ (scb.scb_command & SCB_CMD_ACK_CX) ? "ack cmd completion," : "",
+ (scb.scb_command & SCB_CMD_ACK_FR) ? "ack frame received," : "",
+ (scb.scb_command & SCB_CMD_ACK_CNA) ? "ack CU not active," : "",
+ (scb.scb_command & SCB_CMD_ACK_RNR) ? "ack RU not ready," : ""
+ );
+ printk
+ (
+ " cuc 0x%x[%s%s%s%s%s]",
+ (scb.scb_command & SCB_CMD_CUC) >> 8,
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_GO) ? "start cbl_offset" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_RES) ? "resume execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_SUS) ? "suspend execution" : "",
+ ((scb.scb_command & SCB_CMD_CUC) == SCB_CMD_CUC_ABT) ? "abort execution" : ""
+ );
+ printk
+ (
+ " ruc 0x%x[%s%s%s%s%s]",
+ (scb.scb_command & SCB_CMD_RUC) >> 4,
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_NOP) ? "nop" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_GO) ? "start rfa_offset" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_RES) ? "resume reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_SUS) ? "suspend reception" : "",
+ ((scb.scb_command & SCB_CMD_RUC) == SCB_CMD_RUC_ABT) ? "abort reception" : ""
+ );
+
+ printk(" cbl_offset 0x%x", scb.scb_cbl_offset);
+ printk(" rfa_offset 0x%x", scb.scb_rfa_offset);
+
+ printk(" crcerrs %d", scb.scb_crcerrs);
+ printk(" alnerrs %d", scb.scb_alnerrs);
+ printk(" rscerrs %d", scb.scb_rscerrs);
+ printk(" ovrnerrs %d", scb.scb_ovrnerrs);
+
+ printk("\n");
+}
+
+static
+void
+wavelan_ru_show(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk("ru:");
+ /*
+ * Not implemented yet...
+ */
+ printk("\n");
+}
+
+static
+void
+wavelan_cu_show(device *dev)
+{
+ net_local *lp;
+ unsigned int i;
+ unsigned short p;
+
+ lp = (net_local *)dev->priv;
+
+ printk("cu:");
+ printk("\n");
+
+ for (i = 0, p = lp->tx_first_in_use; i < NTXBLOCKS; i++)
+ {
+ wavelan_cu_show_one(dev, lp, i, p);
+
+ p += TXBLOCKZ;
+ if (p >= OFFSET_CU + NTXBLOCKS * TXBLOCKZ)
+ p -= NTXBLOCKS * TXBLOCKZ;
+ }
+}
+
+static
+void
+wavelan_dev_show(device *dev)
+{
+ printk("dev:");
+ printk(" start=%d,", dev->start);
+ printk(" tbusy=%ld,", dev->tbusy);
+ printk(" interrupt=%d,", dev->interrupt);
+ printk(" trans_start=%ld,", dev->trans_start);
+ printk(" flags=0x%x,", dev->flags);
+ printk("\n");
+}
+
+static
+void
+wavelan_local_show(device *dev)
+{
+ net_local *lp;
+
+ lp = (net_local *)dev->priv;
+
+ printk("local:");
+ printk(" tx_n_in_use=%d,", lp->tx_n_in_use);
+ printk(" hacr=0x%x,", lp->hacr);
+ printk(" rx_head=0x%x,", lp->rx_head);
+ printk(" rx_last=0x%x,", lp->rx_last);
+ printk(" tx_first_free=0x%x,", lp->tx_first_free);
+ printk(" tx_first_in_use=0x%x,", lp->tx_first_in_use);
+ printk("\n");
+}
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * This software was developed as a component of the
+ * Linux operating system.
+ * It is based on other device drivers and information
+ * either written or supplied by:
+ * Ajay Bakre (bakre@paul.rutgers.edu),
+ * Donald Becker (becker@cesdis.gsfc.nasa.gov),
+ * Loeke Brederveld (Loeke.Brederveld@Utrecht.NCR.com),
+ * Anders Klemets (klemets@it.kth.se),
+ * Vladimir V. Kolpakov (w@stier.koenig.ru),
+ * Marc Meertens (Marc.Meertens@Utrecht.NCR.com),
+ * Pauline Middelink (middelin@polyware.iaf.nl),
+ * Robert Morris (rtm@das.harvard.edu),
+ * Girish Welling (welling@paul.rutgers.edu),
+ *
+ * Thanks go also to:
+ * James Ashton (jaa101@syseng.anu.edu.au),
+ * Alan Cox (iialan@iiit.swan.ac.uk),
+ * Allan Creighton (allanc@cs.usyd.edu.au),
+ * Matthew Geier (matthew@cs.usyd.edu.au),
+ * Remo di Giovanni (remo@cs.usyd.edu.au),
+ * Eckhard Grah (grah@wrcs1.urz.uni-wuppertal.de),
+ * Vipul Gupta (vgupta@cs.binghamton.edu),
+ * Mark Hagan (mhagan@wtcpost.daytonoh.NCR.COM),
+ * Tim Nicholson (tim@cs.usyd.edu.au),
+ * Ian Parkin (ian@cs.usyd.edu.au),
+ * John Rosenberg (johnr@cs.usyd.edu.au),
+ * George Rossi (george@phm.gov.au),
+ * Arthur Scott (arthur@cs.usyd.edu.au),
+ * Peter Storey,
+ * for their assistance and advice.
+ *
+ * Please send bug reports, updates, comments to:
+ *
+ * Bruce Janson Email: bruce@cs.usyd.edu.au
+ * Basser Department of Computer Science Phone: +61-2-351-3423
+ * University of Sydney, N.S.W., 2006, AUSTRALIA Fax: +61-2-351-3838
+ */
diff --git a/i386/i386at/gpl/linux/net/wavelan.h b/i386/i386at/gpl/linux/net/wavelan.h
new file mode 100644
index 00000000..3eb221c0
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/wavelan.h
@@ -0,0 +1,252 @@
+#define WAVELAN_ADDR_SIZE 6 /* Size of a MAC address */
+#define SA_ADDR0 0x08 /* First octet of WaveLAN MAC addresses */
+#define SA_ADDR1 0x00 /* Second octet of WaveLAN MAC addresses */
+#define SA_ADDR2 0x0E /* Third octet of WaveLAN MAC addresses */
+#define WAVELAN_MTU 1500 /* Maximum size of WaveLAN packet */
+
+/*
+ * Parameter Storage Area (PSA).
+ */
+typedef struct psa_t psa_t;
+struct psa_t
+{
+ unsigned char psa_io_base_addr_1; /* Base address 1 ??? */
+ unsigned char psa_io_base_addr_2; /* Base address 2 */
+ unsigned char psa_io_base_addr_3; /* Base address 3 */
+ unsigned char psa_io_base_addr_4; /* Base address 4 */
+ unsigned char psa_rem_boot_addr_1; /* Remote Boot Address 1 */
+ unsigned char psa_rem_boot_addr_2; /* Remote Boot Address 2 */
+ unsigned char psa_rem_boot_addr_3; /* Remote Boot Address 3 */
+ unsigned char psa_holi_params; /* HOst Lan Interface (HOLI) Parameters */
+ unsigned char psa_int_req_no; /* Interrupt Request Line */
+ unsigned char psa_unused0[7]; /* unused */
+ unsigned char psa_univ_mac_addr[WAVELAN_ADDR_SIZE]; /* Universal (factory) MAC Address */
+ unsigned char psa_local_mac_addr[WAVELAN_ADDR_SIZE]; /* Local MAC Address */
+ unsigned char psa_univ_local_sel; /* Universal Local Selection */
+#define PSA_UNIVERSAL 0 /* Universal (factory) */
+#define PSA_LOCAL 1 /* Local */
+ unsigned char psa_comp_number; /* Compatibility Number: */
+#define PSA_COMP_PC_AT_915 0 /* PC-AT 915 MHz */
+#define PSA_COMP_PC_MC_915 1 /* PC-MC 915 MHz */
+#define PSA_COMP_PC_AT_2400 2 /* PC-AT 2.4 GHz */
+#define PSA_COMP_PC_MC_2400 3 /* PC-MC 2.4 GHz */
+#define PSA_COMP_PCMCIA_915 4 /* PCMCIA 915 MHz */
+ unsigned char psa_thr_pre_set; /* Modem Threshold Preset */
+ unsigned char psa_feature_select; /* ??? */
+#if 0
+ <alias for above>
+ unsigned char psa_decay_prm; /* Modem Decay */
+#endif /* 0 */
+ unsigned char psa_subband; /* Subband */
+#define PSA_SUBBAND_915 0 /* 915 MHz */
+#define PSA_SUBBAND_2425 1 /* 2425 MHz */
+#define PSA_SUBBAND_2460 2 /* 2460 MHz */
+#define PSA_SUBBAND_2484 3 /* 2484 MHz */
+#define PSA_SUBBAND_2430_5 4 /* 2430.5 MHz */
+#if 0
+ <alias for above>
+ unsigned char psa_decay_updat_prm; /* Modem Decay Update ??? */
+#endif /* 0 */
+ unsigned char psa_quality_thr; /* Modem Quality Threshold */
+ unsigned char psa_mod_delay; /* Modem Delay ??? */
+ unsigned char psa_nwid[2]; /* Network ID */
+ unsigned char psa_undefined; /* undefined */
+ unsigned char psa_encryption_select; /* Encryption On Off */
+ unsigned char psa_encryption_key[8]; /* Encryption Key */
+ unsigned char psa_databus_width; /* 8/16 bit bus width */
+ unsigned char psa_call_code; /* ??? */
+#if 0
+ <alias for above>
+ unsigned char psa_auto_squelch; /* Automatic Squelch level On off ??? */
+#endif /* 0 */
+ unsigned char psa_no_of_retries; /* LAN Cont. No of retries */
+ unsigned char psa_acr; /* LAN Cont. ACR */
+ unsigned char psa_dump_count; /* number of Dump Commands in TFB */
+ unsigned char psa_unused1[4]; /* unused */
+ unsigned char psa_nwid_prefix; /* ??? */
+ unsigned char psa_unused2[3]; /* unused */
+ unsigned char psa_conf_status; /* Card Configuration Status */
+ unsigned char psa_crc[2]; /* CRC over PSA */
+ unsigned char psa_crc_status; /* CRC Valid Flag */
+};
+#if STRUCT_CHECK == 1
+#define PSA_SIZE 64
+#endif /* STRUCT_CHECK == 1 */
+
+/*
+ * Modem Management Controller (MMC) write structure.
+ */
+typedef struct mmw_t mmw_t;
+struct mmw_t
+{
+ unsigned char mmw_encr_key[8]; /* encryption key */
+ unsigned char mmw_encr_enable; /* enable/disable encryption */
+ unsigned char mmw_unused0[1]; /* unused */
+ unsigned char mmw_des_io_invert; /* ??? */
+ unsigned char mmw_unused1[5]; /* unused */
+ unsigned char mmw_loopt_sel; /* looptest selection */
+#define MMW_LOOPT_SEL_UNDEFINED 0x40 /* undefined */
+#define MMW_LOOPT_SEL_INT 0x20 /* activate Attention Request */
+#define MMW_LOOPT_SEL_LS 0x10 /* looptest without collision avoidance */
+#define MMW_LOOPT_SEL_LT3A 0x08 /* looptest 3a */
+#define MMW_LOOPT_SEL_LT3B 0x04 /* looptest 3b */
+#define MMW_LOOPT_SEL_LT3C 0x02 /* looptest 3c */
+#define MMW_LOOPT_SEL_LT3D 0x01 /* looptest 3d */
+ unsigned char mmw_jabber_enable; /* jabber timer enable */
+ unsigned char mmw_freeze; /* freeze / unfreeze signal level */
+ unsigned char mmw_anten_sel; /* antenna selection */
+#define MMW_ANTEN_SEL_SEL 0x01 /* direct antenna selection */
+#define MMW_ANTEN_SEL_ALG_EN 0x02 /* antenna selection algorithm enable */
+ unsigned char mmw_ifs; /* inter frame spacing */
+ unsigned char mmw_mod_delay; /* modem delay */
+ unsigned char mmw_jam_time; /* jamming time */
+ unsigned char mmw_unused2[1]; /* unused */
+ unsigned char mmw_thr_pre_set; /* level threshold preset */
+ unsigned char mmw_decay_prm; /* decay parameters */
+ unsigned char mmw_decay_updat_prm; /* decay update parameters */
+ unsigned char mmw_quality_thr; /* quality (z-quotient) threshold */
+ unsigned char mmw_netw_id_l; /* NWID low order byte */
+ unsigned char mmw_netw_id_h; /* NWID high order byte */
+};
+#if STRUCT_CHECK == 1
+#define MMW_SIZE 30
+#endif /* STRUCT_CHECK == 1 */
+
+#define mmwoff(p,f) (unsigned short)((void *)(&((mmw_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Modem Management Controller (MMC) read structure.
+ */
+typedef struct mmr_t mmr_t;
+struct mmr_t
+{
+ unsigned char mmr_unused0[8]; /* unused */
+ unsigned char mmr_des_status; /* encryption status */
+ unsigned char mmr_des_avail; /* encryption available (0x55 read) */
+ unsigned char mmr_des_io_invert; /* des I/O invert register */
+ unsigned char mmr_unused1[5]; /* unused */
+ unsigned char mmr_dce_status; /* DCE status */
+#define MMR_DCE_STATUS_ENERG_DET 0x01 /* energy detected */
+#define MMR_DCE_STATUS_LOOPT_IND 0x02 /* loop test indicated */
+#define MMR_DCE_STATUS_XMTITR_IND 0x04 /* transmitter on */
+#define MMR_DCE_STATUS_JBR_EXPIRED 0x08 /* jabber timer expired */
+ unsigned char mmr_unused2[3]; /* unused */
+ unsigned char mmr_correct_nwid_l; /* no. of correct NWID's rxd (low) */
+ unsigned char mmr_correct_nwid_h; /* no. of correct NWID's rxd (high) */
+ unsigned char mmr_wrong_nwid_l; /* count of wrong NWID's received (low) */
+ unsigned char mmr_wrong_nwid_h; /* count of wrong NWID's received (high) */
+ unsigned char mmr_thr_pre_set; /* level threshold preset */
+ unsigned char mmr_signal_lvl; /* signal level */
+ unsigned char mmr_silence_lvl; /* silence level */
+ unsigned char mmr_sgnl_qual; /* signal quality */
+#define MMR_SGNL_QUAL_0 0x01 /* signal quality 0 */
+#define MMR_SGNL_QUAL_1 0x02 /* signal quality 1 */
+#define MMR_SGNL_QUAL_2 0x04 /* signal quality 2 */
+#define MMR_SGNL_QUAL_3 0x08 /* signal quality 3 */
+#define MMR_SGNL_QUAL_S_A 0x80 /* currently selected antenna */
+ unsigned char mmr_netw_id_l; /* NWID low order byte ??? */
+ unsigned char mmr_unused3[1]; /* unused */
+};
+#if STRUCT_CHECK == 1
+#define MMR_SIZE 30
+#endif /* STRUCT_CHECK == 1 */
+
+#define MMR_LEVEL_MASK 0x3F
+
+#define mmroff(p,f) (unsigned short)((void *)(&((mmr_t *)((void *)0 + (p)))->f) - (void *)0)
+
+/*
+ * Host Adaptor structure.
+ * (base is board port address).
+ */
+typedef union hacs_u hacs_u;
+union hacs_u
+{
+ unsigned short hu_command; /* Command register */
+#define HACR_RESET 0x0001 /* Reset board */
+#define HACR_CA 0x0002 /* Set Channel Attention for 82586 */
+#define HACR_16BITS 0x0004 /* 16 bits operation (0 => 8bits) */
+#define HACR_OUT0 0x0008 /* General purpose output pin 0 */
+ /* not used - must be 1 */
+#define HACR_OUT1 0x0010 /* General purpose output pin 1 */
+ /* not used - must be 1 */
+#define HACR_82586_INT_ENABLE 0x0020 /* Enable 82586 interrupts */
+#define HACR_MMC_INT_ENABLE 0x0040 /* Enable MMC interrupts */
+#define HACR_INTR_CLR_ENABLE 0x0080 /* Enable interrupt status read/clear */
+ unsigned short hu_status; /* Status Register */
+#define HASR_82586_INTR 0x0001 /* Interrupt request from 82586 */
+#define HASR_MMC_INTR 0x0002 /* Interrupt request from MMC */
+#define HASR_MMC_BUSY 0x0004 /* MMC busy indication */
+#define HASR_PSA_BUSY 0x0008 /* LAN parameter storage area busy */
+};
+
+typedef struct ha_t ha_t;
+struct ha_t
+{
+ hacs_u ha_cs; /* Command and status registers */
+#define ha_command ha_cs.hu_command
+#define ha_status ha_cs.hu_status
+ unsigned short ha_mmcr; /* Modem Management Ctrl Register */
+ unsigned short ha_pior0; /* Program I/O Address Register Port 0 */
+ unsigned short ha_piop0; /* Program I/O Port 0 */
+ unsigned short ha_pior1; /* Program I/O Address Register Port 1 */
+ unsigned short ha_piop1; /* Program I/O Port 1 */
+ unsigned short ha_pior2; /* Program I/O Address Register Port 2 */
+ unsigned short ha_piop2; /* Program I/O Port 2 */
+};
+#if STRUCT_CHECK == 1
+#define HA_SIZE 16
+#endif /* STRUCT_CHECK == 1 */
+
+#define hoff(p,f) (unsigned short)((void *)(&((ha_t *)((void *)0 + (p)))->f) - (void *)0)
+#define HACR(p) hoff(p, ha_command)
+#define HASR(p) hoff(p, ha_status)
+#define MMCR(p) hoff(p, ha_mmcr)
+#define PIOR0(p) hoff(p, ha_pior0)
+#define PIOP0(p) hoff(p, ha_piop0)
+#define PIOR1(p) hoff(p, ha_pior1)
+#define PIOP1(p) hoff(p, ha_piop1)
+#define PIOR2(p) hoff(p, ha_pior2)
+#define PIOP2(p) hoff(p, ha_piop2)
+
+/*
+ * Program I/O Mode Register values.
+ */
+#define STATIC_PIO 0 /* Mode 1: static mode */
+ /* RAM access ??? */
+#define AUTOINCR_PIO 1 /* Mode 2: auto increment mode */
+ /* RAM access ??? */
+#define AUTODECR_PIO 2 /* Mode 3: auto decrement mode */
+ /* RAM access ??? */
+#define PARAM_ACCESS_PIO 3 /* Mode 4: LAN parameter access mode */
+ /* Parameter access. */
+#define PIO_MASK 3 /* register mask */
+#define PIOM(cmd,piono) ((u_short)cmd << 10 << (piono * 2))
+
+#define HACR_DEFAULT (HACR_OUT0 | HACR_OUT1 | HACR_16BITS | PIOM(STATIC_PIO, 0) | PIOM(AUTOINCR_PIO, 1) | PIOM(PARAM_ACCESS_PIO, 2))
+#define HACR_INTRON (HACR_82586_INT_ENABLE | HACR_MMC_INT_ENABLE | HACR_INTR_CLR_ENABLE)
+
+#define MAXDATAZ (WAVELAN_ADDR_SIZE + WAVELAN_ADDR_SIZE + 2 + WAVELAN_MTU)
+
+/*
+ * Onboard 64k RAM layout.
+ * (Offsets from 0x0000.)
+ */
+#define OFFSET_RU 0x0000
+#define OFFSET_CU 0x8000
+#define OFFSET_SCB (OFFSET_ISCP - sizeof(scb_t))
+#define OFFSET_ISCP (OFFSET_SCP - sizeof(iscp_t))
+#define OFFSET_SCP I82586_SCP_ADDR
+
+#define RXBLOCKZ (sizeof(fd_t) + sizeof(rbd_t) + MAXDATAZ)
+#define TXBLOCKZ (sizeof(ac_tx_t) + sizeof(ac_nop_t) + sizeof(tbd_t) + MAXDATAZ)
+
+#define NRXBLOCKS ((OFFSET_CU - OFFSET_RU) / RXBLOCKZ)
+#define NTXBLOCKS ((OFFSET_SCB - OFFSET_CU) / TXBLOCKZ)
+
+/*
+ * This software may only be used and distributed
+ * according to the terms of the GNU Public License.
+ *
+ * For more details, see wavelan.c.
+ */
diff --git a/i386/i386at/gpl/linux/net/wd.c b/i386/i386at/gpl/linux/net/wd.c
new file mode 100644
index 00000000..5eaa6585
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/wd.c
@@ -0,0 +1,513 @@
+/* wd.c: A WD80x3 ethernet driver for linux. */
+/*
+ Written 1993-94 by Donald Becker.
+
+ Copyright 1993 United States Government as represented by the
+ Director, National Security Agency.
+
+ This software may be used and distributed according to the terms
+ of the GNU Public License, incorporated herein by reference.
+
+ The author may be reached as becker@CESDIS.gsfc.nasa.gov, or C/O
+ Center of Excellence in Space Data and Information Sciences
+ Code 930.5, Goddard Space Flight Center, Greenbelt MD 20771
+
+ This is a driver for WD8003 and WD8013 "compatible" ethercards.
+
+ Thanks to Russ Nelson (nelson@crnwyr.com) for loaning me a WD8013.
+
+ Changelog:
+
+ Paul Gortmaker : multiple card support for module users, support
+ for non-standard memory sizes.
+
+
+*/
+
+static const char *version =
+ "wd.c:v1.10 9/23/94 Donald Becker (becker@cesdis.gsfc.nasa.gov)\n";
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include "8390.h"
+
+/* A zero-terminated list of I/O addresses to be probed. */
+static unsigned int wd_portlist[] =
+{0x300, 0x280, 0x380, 0x240, 0};
+
+int wd_probe(struct device *dev);
+int wd_probe1(struct device *dev, int ioaddr);
+
+static int wd_open(struct device *dev);
+static void wd_reset_8390(struct device *dev);
+static void wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr,
+ int ring_page);
+static void wd_block_input(struct device *dev, int count,
+ struct sk_buff *skb, int ring_offset);
+static void wd_block_output(struct device *dev, int count,
+ const unsigned char *buf, const start_page);
+static int wd_close_card(struct device *dev);
+
+
+#define WD_START_PG 0x00 /* First page of TX buffer */
+#define WD03_STOP_PG 0x20 /* Last page +1 of RX ring */
+#define WD13_STOP_PG 0x40 /* Last page +1 of RX ring */
+
+#define WD_CMDREG 0 /* Offset to ASIC command register. */
+#define WD_RESET 0x80 /* Board reset, in WD_CMDREG. */
+#define WD_MEMENB 0x40 /* Enable the shared memory. */
+#define WD_CMDREG5 5 /* Offset to 16-bit-only ASIC register 5. */
+#define ISA16 0x80 /* Enable 16 bit access from the ISA bus. */
+#define NIC16 0x40 /* Enable 16 bit access from the 8390. */
+#define WD_NIC_OFFSET 16 /* Offset to the 8390 from the base_addr. */
+#define WD_IO_EXTENT 32
+
+
+/* Probe for the WD8003 and WD8013. These cards have the station
+ address PROM at I/O ports <base>+8 to <base>+13, with a checksum
+ following. A Soundblaster can have the same checksum as an WDethercard,
+ so we have an extra exclusionary check for it.
+
+ The wd_probe1() routine initializes the card and fills the
+ station address field. */
+
+#ifdef HAVE_DEVLIST
+struct netdev_entry wd_drv =
+{"wd", wd_probe1, WD_IO_EXTENT, wd_portlist};
+#else
+
+int wd_probe(struct device *dev)
+{
+ int i;
+ int base_addr = dev ? dev->base_addr : 0;
+
+ if (base_addr > 0x1ff) /* Check a single specified location. */
+ return wd_probe1(dev, base_addr);
+ else if (base_addr != 0) /* Don't probe at all. */
+ return ENXIO;
+
+ for (i = 0; wd_portlist[i]; i++) {
+ int ioaddr = wd_portlist[i];
+ if (check_region(ioaddr, WD_IO_EXTENT))
+ continue;
+ if (wd_probe1(dev, ioaddr) == 0)
+ return 0;
+ }
+
+ return ENODEV;
+}
+#endif
+
+int wd_probe1(struct device *dev, int ioaddr)
+{
+ int i;
+ int checksum = 0;
+ int ancient = 0; /* An old card without config registers. */
+ int word16 = 0; /* 0 = 8 bit, 1 = 16 bit */
+ const char *model_name;
+ static unsigned version_printed = 0;
+
+ for (i = 0; i < 8; i++)
+ checksum += inb(ioaddr + 8 + i);
+ if (inb(ioaddr + 8) == 0xff /* Extra check to avoid soundcard. */
+ || inb(ioaddr + 9) == 0xff
+ || (checksum & 0xff) != 0xFF)
+ return ENODEV;
+
+ /* We should have a "dev" from Space.c or the static module table. */
+ if (dev == NULL) {
+ printk("wd.c: Passed a NULL device.\n");
+ dev = init_etherdev(0, 0);
+ }
+
+ /* Check for semi-valid mem_start/end values if supplied. */
+ if ((dev->mem_start % 0x2000) || (dev->mem_end % 0x2000)) {
+ printk(KERN_WARNING "wd.c: user supplied mem_start or mem_end not on 8kB boundary - ignored.\n");
+ dev->mem_start = 0;
+ dev->mem_end = 0;
+ }
+
+ if (ei_debug && version_printed++ == 0)
+ printk(version);
+
+ printk("%s: WD80x3 at %#3x, ", dev->name, ioaddr);
+ for (i = 0; i < 6; i++)
+ printk(" %2.2X", dev->dev_addr[i] = inb(ioaddr + 8 + i));
+
+ /* The following PureData probe code was contributed by
+ Mike Jagdis <jaggy@purplet.demon.co.uk>. Puredata does software
+ configuration differently from others so we have to check for them.
+ This detects an 8 bit, 16 bit or dumb (Toshiba, jumpered) card.
+ */
+ if (inb(ioaddr+0) == 'P' && inb(ioaddr+1) == 'D') {
+ unsigned char reg5 = inb(ioaddr+5);
+
+ switch (inb(ioaddr+2)) {
+ case 0x03: word16 = 0; model_name = "PDI8023-8"; break;
+ case 0x05: word16 = 0; model_name = "PDUC8023"; break;
+ case 0x0a: word16 = 1; model_name = "PDI8023-16"; break;
+ /* Either 0x01 (dumb) or they've released a new version. */
+ default: word16 = 0; model_name = "PDI8023"; break;
+ }
+ dev->mem_start = ((reg5 & 0x1c) + 0xc0) << 12;
+ dev->irq = (reg5 & 0xe0) == 0xe0 ? 10 : (reg5 >> 5) + 1;
+ } else { /* End of PureData probe */
+ /* This method of checking for a 16-bit board is borrowed from the
+ we.c driver. A simpler method is just to look in ASIC reg. 0x03.
+ I'm comparing the two method in alpha test to make certain they
+ return the same result. */
+ /* Check for the old 8 bit board - it has register 0/8 aliasing.
+ Do NOT check i>=6 here -- it hangs the old 8003 boards! */
+ for (i = 0; i < 6; i++)
+ if (inb(ioaddr+i) != inb(ioaddr+8+i))
+ break;
+ if (i >= 6) {
+ ancient = 1;
+ model_name = "WD8003-old";
+ word16 = 0;
+ } else {
+ int tmp = inb(ioaddr+1); /* fiddle with 16bit bit */
+ outb( tmp ^ 0x01, ioaddr+1 ); /* attempt to clear 16bit bit */
+ if (((inb( ioaddr+1) & 0x01) == 0x01) /* A 16 bit card */
+ && (tmp & 0x01) == 0x01 ) { /* In a 16 slot. */
+ int asic_reg5 = inb(ioaddr+WD_CMDREG5);
+ /* Magic to set ASIC to word-wide mode. */
+ outb( NIC16 | (asic_reg5&0x1f), ioaddr+WD_CMDREG5);
+ outb(tmp, ioaddr+1);
+ model_name = "WD8013";
+ word16 = 1; /* We have a 16bit board here! */
+ } else {
+ model_name = "WD8003";
+ word16 = 0;
+ }
+ outb(tmp, ioaddr+1); /* Restore original reg1 value. */
+ }
+#ifndef final_version
+ if ( !ancient && (inb(ioaddr+1) & 0x01) != (word16 & 0x01))
+ printk("\nWD80?3: Bus width conflict, %d (probe) != %d (reg report).",
+ word16 ? 16 : 8, (inb(ioaddr+1) & 0x01) ? 16 : 8);
+#endif
+ }
+
+#if defined(WD_SHMEM) && WD_SHMEM > 0x80000
+ /* Allow a compile-time override. */
+ dev->mem_start = WD_SHMEM;
+#else
+ if (dev->mem_start == 0) {
+ /* Sanity and old 8003 check */
+ int reg0 = inb(ioaddr);
+ if (reg0 == 0xff || reg0 == 0) {
+ /* Future plan: this could check a few likely locations first. */
+ dev->mem_start = 0xd0000;
+ printk(" assigning address %#lx", dev->mem_start);
+ } else {
+ int high_addr_bits = inb(ioaddr+WD_CMDREG5) & 0x1f;
+ /* Some boards don't have the register 5 -- it returns 0xff. */
+ if (high_addr_bits == 0x1f || word16 == 0)
+ high_addr_bits = 0x01;
+ dev->mem_start = ((reg0&0x3f) << 13) + (high_addr_bits << 19);
+ }
+ }
+#endif
+
+ /* The 8390 isn't at the base address -- the ASIC regs are there! */
+ dev->base_addr = ioaddr+WD_NIC_OFFSET;
+
+ if (dev->irq < 2) {
+ int irqmap[] = {9,3,5,7,10,11,15,4};
+ int reg1 = inb(ioaddr+1);
+ int reg4 = inb(ioaddr+4);
+ if (ancient || reg1 == 0xff) { /* Ack!! No way to read the IRQ! */
+ short nic_addr = ioaddr+WD_NIC_OFFSET;
+
+ /* We have an old-style ethercard that doesn't report its IRQ
+ line. Do autoirq to find the IRQ line. Note that this IS NOT
+ a reliable way to trigger an interrupt. */
+ outb_p(E8390_NODMA + E8390_STOP, nic_addr);
+ outb(0x00, nic_addr+EN0_IMR); /* Disable all intrs. */
+ autoirq_setup(0);
+ outb_p(0xff, nic_addr + EN0_IMR); /* Enable all interrupts. */
+ outb_p(0x00, nic_addr + EN0_RCNTLO);
+ outb_p(0x00, nic_addr + EN0_RCNTHI);
+ outb(E8390_RREAD+E8390_START, nic_addr); /* Trigger it... */
+ dev->irq = autoirq_report(2);
+ outb_p(0x00, nic_addr+EN0_IMR); /* Mask all intrs. again. */
+
+ if (ei_debug > 2)
+ printk(" autoirq is %d", dev->irq);
+ if (dev->irq < 2)
+ dev->irq = word16 ? 10 : 5;
+ } else
+ dev->irq = irqmap[((reg4 >> 5) & 0x03) + (reg1 & 0x04)];
+ } else if (dev->irq == 2) /* Fixup bogosity: IRQ2 is really IRQ9 */
+ dev->irq = 9;
+
+ /* Snarf the interrupt now. There's no point in waiting since we cannot
+ share and the board will usually be enabled. */
+ if (request_irq(dev->irq, ei_interrupt, 0, model_name)) {
+ printk (" unable to get IRQ %d.\n", dev->irq);
+ return EAGAIN;
+ }
+
+ /* Allocate dev->priv and fill in 8390 specific dev fields. */
+ if (ethdev_init(dev)) {
+ printk (" unable to get memory for dev->priv.\n");
+ free_irq(dev->irq);
+ return -ENOMEM;
+ }
+
+ /* OK, were are certain this is going to work. Setup the device. */
+ request_region(ioaddr, WD_IO_EXTENT, model_name);
+
+ ei_status.name = model_name;
+ ei_status.word16 = word16;
+ ei_status.tx_start_page = WD_START_PG;
+ ei_status.rx_start_page = WD_START_PG + TX_PAGES;
+
+ /* Don't map in the shared memory until the board is actually opened. */
+ dev->rmem_start = dev->mem_start + TX_PAGES*256;
+
+ /* Some cards (eg WD8003EBT) can be jumpered for more (32k!) memory. */
+ if (dev->mem_end != 0) {
+ ei_status.stop_page = (dev->mem_end - dev->mem_start)/256;
+ } else {
+ ei_status.stop_page = word16 ? WD13_STOP_PG : WD03_STOP_PG;
+ dev->mem_end = dev->mem_start + (ei_status.stop_page - WD_START_PG)*256;
+ }
+ dev->rmem_end = dev->mem_end;
+
+ printk(" %s, IRQ %d, shared memory at %#lx-%#lx.\n",
+ model_name, dev->irq, dev->mem_start, dev->mem_end-1);
+
+ ei_status.reset_8390 = &wd_reset_8390;
+ ei_status.block_input = &wd_block_input;
+ ei_status.block_output = &wd_block_output;
+ ei_status.get_8390_hdr = &wd_get_8390_hdr;
+ dev->open = &wd_open;
+ dev->stop = &wd_close_card;
+ NS8390_init(dev, 0);
+
+#if 1
+ /* Enable interrupt generation on softconfig cards -- M.U */
+ /* .. but possibly potentially unsafe - Donald */
+ if (inb(ioaddr+14) & 0x20)
+ outb(inb(ioaddr+4)|0x80, ioaddr+4);
+#endif
+
+ return 0;
+}
+
+static int
+wd_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ /* Map in the shared memory. Always set register 0 last to remain
+ compatible with very old boards. */
+ ei_status.reg0 = ((dev->mem_start>>13) & 0x3f) | WD_MEMENB;
+ ei_status.reg5 = ((dev->mem_start>>19) & 0x1f) | NIC16;
+
+ if (ei_status.word16)
+ outb(ei_status.reg5, ioaddr+WD_CMDREG5);
+ outb(ei_status.reg0, ioaddr); /* WD_CMDREG */
+
+ ei_open(dev);
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static void
+wd_reset_8390(struct device *dev)
+{
+ int wd_cmd_port = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ outb(WD_RESET, wd_cmd_port);
+ if (ei_debug > 1) printk("resetting the WD80x3 t=%lu...", jiffies);
+ ei_status.txing = 0;
+
+ /* Set up the ASIC registers, just in case something changed them. */
+ outb((((dev->mem_start>>13) & 0x3f)|WD_MEMENB), wd_cmd_port);
+ if (ei_status.word16)
+ outb(NIC16 | ((dev->mem_start>>19) & 0x1f), wd_cmd_port+WD_CMDREG5);
+
+ if (ei_debug > 1) printk("reset done\n");
+ return;
+}
+
+/* Grab the 8390 specific header. Similar to the block_input routine, but
+ we don't need to be concerned with ring wrap as the header will be at
+ the start of a page, so we optimize accordingly. */
+
+static void
+wd_get_8390_hdr(struct device *dev, struct e8390_pkt_hdr *hdr, int ring_page)
+{
+
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ unsigned long hdr_start = dev->mem_start + ((ring_page - WD_START_PG)<<8);
+
+ /* We'll always get a 4 byte header read followed by a packet read, so
+ we enable 16 bit mode before the header, and disable after the body. */
+ if (ei_status.word16)
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+
+#ifdef notdef
+ /* Officially this is what we are doing, but the readl() is faster */
+ memcpy_fromio(hdr, hdr_start, sizeof(struct e8390_pkt_hdr));
+#else
+ ((unsigned int*)hdr)[0] = readl(hdr_start);
+#endif
+}
+
+/* Block input and output are easy on shared memory ethercards, and trivial
+ on the Western digital card where there is no choice of how to do it.
+ The only complications are that the ring buffer wraps, and need to map
+ switch between 8- and 16-bit modes. */
+
+static void
+wd_block_input(struct device *dev, int count, struct sk_buff *skb, int ring_offset)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ unsigned long xfer_start = dev->mem_start + ring_offset - (WD_START_PG<<8);
+
+ if (xfer_start + count > dev->rmem_end) {
+ /* We must wrap the input move. */
+ int semi_count = dev->rmem_end - xfer_start;
+ memcpy_fromio(skb->data, xfer_start, semi_count);
+ count -= semi_count;
+ memcpy_fromio(skb->data + semi_count, dev->rmem_start, count);
+ } else {
+ /* Packet is in one chunk -- we can copy + cksum. */
+ eth_io_copy_and_sum(skb, xfer_start, count, 0);
+ }
+
+ /* Turn off 16 bit access so that reboot works. ISA brain-damage */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+}
+
+static void
+wd_block_output(struct device *dev, int count, const unsigned char *buf,
+ int start_page)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+ long shmem = dev->mem_start + ((start_page - WD_START_PG)<<8);
+
+
+ if (ei_status.word16) {
+ /* Turn on and off 16 bit access so that reboot works. */
+ outb(ISA16 | ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ memcpy_toio(shmem, buf, count);
+ outb(ei_status.reg5, wd_cmdreg+WD_CMDREG5);
+ } else
+ memcpy_toio(shmem, buf, count);
+}
+
+
+static int
+wd_close_card(struct device *dev)
+{
+ int wd_cmdreg = dev->base_addr - WD_NIC_OFFSET; /* WD_CMDREG */
+
+ if (ei_debug > 1)
+ printk("%s: Shutting down ethercard.\n", dev->name);
+ ei_close(dev);
+
+ /* Change from 16-bit to 8-bit shared memory so reboot works. */
+ if (ei_status.word16)
+ outb(ei_status.reg5, wd_cmdreg + WD_CMDREG5 );
+
+ /* And disable the shared memory. */
+ outb(ei_status.reg0 & ~WD_MEMENB, wd_cmdreg);
+
+ MOD_DEC_USE_COUNT;
+
+ return 0;
+}
+
+
+#ifdef MODULE
+#define MAX_WD_CARDS 4 /* Max number of wd cards per module */
+#define NAMELEN 8 /* # of chars for storing dev->name */
+static char namelist[NAMELEN * MAX_WD_CARDS] = { 0, };
+static struct device dev_wd[MAX_WD_CARDS] = {
+ {
+ NULL, /* assign a chunk of namelist[] below */
+ 0, 0, 0, 0,
+ 0, 0,
+ 0, 0, 0, NULL, NULL
+ },
+};
+
+static int io[MAX_WD_CARDS] = { 0, };
+static int irq[MAX_WD_CARDS] = { 0, };
+static int mem[MAX_WD_CARDS] = { 0, };
+static int mem_end[MAX_WD_CARDS] = { 0, }; /* for non std. mem size */
+
+/* This is set up so that only a single autoprobe takes place per call.
+ISA device autoprobes on a running machine are not recommended. */
+int
+init_module(void)
+{
+ int this_dev, found = 0;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ struct device *dev = &dev_wd[this_dev];
+ dev->name = namelist+(NAMELEN*this_dev);
+ dev->irq = irq[this_dev];
+ dev->base_addr = io[this_dev];
+ dev->mem_start = mem[this_dev];
+ dev->mem_end = mem_end[this_dev];
+ dev->init = wd_probe;
+ if (io[this_dev] == 0) {
+ if (this_dev != 0) break; /* only autoprobe 1st one */
+ printk(KERN_NOTICE "wd.c: Presently autoprobing (not recommended) for a single card.\n");
+ }
+ if (register_netdev(dev) != 0) {
+ printk(KERN_WARNING "wd.c: No wd80x3 card found (i/o = 0x%x).\n", io[this_dev]);
+ if (found != 0) return 0; /* Got at least one. */
+ return -ENXIO;
+ }
+ found++;
+ }
+
+ return 0;
+}
+
+void
+cleanup_module(void)
+{
+ int this_dev;
+
+ for (this_dev = 0; this_dev < MAX_WD_CARDS; this_dev++) {
+ struct device *dev = &dev_wd[this_dev];
+ if (dev->priv != NULL) {
+ int ioaddr = dev->base_addr - WD_NIC_OFFSET;
+ kfree(dev->priv);
+ dev->priv = NULL;
+ free_irq(dev->irq);
+ irq2dev_map[dev->irq] = NULL;
+ release_region(ioaddr, WD_IO_EXTENT);
+ unregister_netdev(dev);
+ }
+ }
+}
+#endif /* MODULE */
+
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c wd.c"
+ * version-control: t
+ * tab-width: 4
+ * kept-new-versions: 5
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/net/znet.c b/i386/i386at/gpl/linux/net/znet.c
new file mode 100644
index 00000000..9f44928e
--- /dev/null
+++ b/i386/i386at/gpl/linux/net/znet.c
@@ -0,0 +1,746 @@
+/* znet.c: An Zenith Z-Note ethernet driver for linux. */
+
+static const char *version = "znet.c:v1.02 9/23/94 becker@cesdis.gsfc.nasa.gov\n";
+
+/*
+ Written by Donald Becker.
+
+ The author may be reached as becker@cesdis.gsfc.nasa.gov.
+ This driver is based on the Linux skeleton driver. The copyright of the
+ skeleton driver is held by the United States Government, as represented
+ by DIRNSA, and it is released under the GPL.
+
+ Thanks to Mike Hollick for alpha testing and suggestions.
+
+ References:
+ The Crynwr packet driver.
+
+ "82593 CSMA/CD Core LAN Controller" Intel datasheet, 1992
+ Intel Microcommunications Databook, Vol. 1, 1990.
+ As usual with Intel, the documentation is incomplete and inaccurate.
+ I had to read the Crynwr packet driver to figure out how to actually
+ use the i82593, and guess at what register bits matched the loosely
+ related i82586.
+
+ Theory of Operation
+
+ The i82593 used in the Zenith Z-Note series operates using two(!) slave
+ DMA channels, one interrupt, and one 8-bit I/O port.
+
+ While there several ways to configure '593 DMA system, I chose the one
+ that seemed commensurate with the highest system performance in the face
+ of moderate interrupt latency: Both DMA channels are configured as
+ recirculating ring buffers, with one channel (#0) dedicated to Rx and
+ the other channel (#1) to Tx and configuration. (Note that this is
+ different than the Crynwr driver, where the Tx DMA channel is initialized
+ before each operation. That approach simplifies operation and Tx error
+ recovery, but requires additional I/O in normal operation and precludes
+ transmit buffer chaining.)
+
+ Both rings are set to 8192 bytes using {TX,RX}_RING_SIZE. This provides
+ a reasonable ring size for Rx, while simplifying DMA buffer allocation --
+ DMA buffers must not cross a 128K boundary. (In truth the size selection
+ was influenced by my lack of '593 documentation. I thus was constrained
+ to use the Crynwr '593 initialization table, which sets the Rx ring size
+ to 8K.)
+
+ Despite my usual low opinion about Intel-designed parts, I must admit
+ that the bulk data handling of the i82593 is a good design for
+ an integrated system, like a laptop, where using two slave DMA channels
+ doesn't pose a problem. I still take issue with using only a single I/O
+ port. In the same controlled environment there are essentially no
+ limitations on I/O space, and using multiple locations would eliminate
+ the need for multiple operations when looking at status registers,
+ setting the Rx ring boundary, or switching to promiscuous mode.
+
+ I also question Zenith's selection of the '593: one of the advertised
+ advantages of earlier Intel parts was that if you figured out the magic
+ initialization incantation you could use the same part on many different
+ network types. Zenith's use of the "FriendlyNet" (sic) connector rather
+ than an on-board transceiver leads me to believe that they were planning
+ to take advantage of this. But, uhmmm, the '593 omits all but ethernet
+ functionality from the serial subsystem.
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ptrace.h>
+#include <linux/errno.h>
+#include <linux/interrupt.h>
+#include <linux/ioport.h>
+#include <asm/system.h>
+#include <asm/bitops.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+
+#include <linux/netdevice.h>
+#include <linux/etherdevice.h>
+#include <linux/skbuff.h>
+#include <linux/if_arp.h>
+
+#ifndef ZNET_DEBUG
+#define ZNET_DEBUG 1
+#endif
+static unsigned int znet_debug = ZNET_DEBUG;
+
+/* The DMA modes we need aren't in <dma.h>. */
+#define DMA_RX_MODE 0x14 /* Auto init, I/O to mem, ++, demand. */
+#define DMA_TX_MODE 0x18 /* Auto init, Mem to I/O, ++, demand. */
+#define dma_page_eq(ptr1, ptr2) ((long)(ptr1)>>17 == (long)(ptr2)>>17)
+#define DMA_BUF_SIZE 8192
+#define RX_BUF_SIZE 8192
+#define TX_BUF_SIZE 8192
+
+/* Commands to the i82593 channel 0. */
+#define CMD0_CHNL_0 0x00
+#define CMD0_CHNL_1 0x10 /* Switch to channel 1. */
+#define CMD0_NOP (CMD0_CHNL_0)
+#define CMD0_PORT_1 CMD0_CHNL_1
+#define CMD1_PORT_0 1
+#define CMD0_IA_SETUP 1
+#define CMD0_CONFIGURE 2
+#define CMD0_MULTICAST_LIST 3
+#define CMD0_TRANSMIT 4
+#define CMD0_DUMP 6
+#define CMD0_DIAGNOSE 7
+#define CMD0_Rx_ENABLE 8
+#define CMD0_Rx_DISABLE 10
+#define CMD0_Rx_STOP 11
+#define CMD0_RETRANSMIT 12
+#define CMD0_ABORT 13
+#define CMD0_RESET 14
+
+#define CMD0_ACK 0x80
+
+#define CMD0_STAT0 (0 << 5)
+#define CMD0_STAT1 (1 << 5)
+#define CMD0_STAT2 (2 << 5)
+#define CMD0_STAT3 (3 << 5)
+
+#define net_local znet_private
+struct znet_private {
+ int rx_dma, tx_dma;
+ struct enet_statistics stats;
+ /* The starting, current, and end pointers for the packet buffers. */
+ ushort *rx_start, *rx_cur, *rx_end;
+ ushort *tx_start, *tx_cur, *tx_end;
+ ushort tx_buf_len; /* Tx buffer length, in words. */
+};
+
+/* Only one can be built-in;-> */
+static struct znet_private zn;
+static ushort dma_buffer1[DMA_BUF_SIZE/2];
+static ushort dma_buffer2[DMA_BUF_SIZE/2];
+static ushort dma_buffer3[DMA_BUF_SIZE/2 + 8];
+
+/* The configuration block. What an undocumented nightmare. The first
+ set of values are those suggested (without explanation) for ethernet
+ in the Intel 82586 databook. The rest appear to be completely undocumented,
+ except for cryptic notes in the Crynwr packet driver. This driver uses
+ the Crynwr values verbatim. */
+
+static unsigned char i593_init[] = {
+ 0xAA, /* 0: 16-byte input & 80-byte output FIFO. */
+ /* threshold, 96-byte FIFO, 82593 mode. */
+ 0x88, /* 1: Continuous w/interrupts, 128-clock DMA.*/
+ 0x2E, /* 2: 8-byte preamble, NO address insertion, */
+ /* 6-byte Ethernet address, loopback off.*/
+ 0x00, /* 3: Default priorities & backoff methods. */
+ 0x60, /* 4: 96-bit interframe spacing. */
+ 0x00, /* 5: 512-bit slot time (low-order). */
+ 0xF2, /* 6: Slot time (high-order), 15 COLL retries. */
+ 0x00, /* 7: Promisc-off, broadcast-on, default CRC. */
+ 0x00, /* 8: Default carrier-sense, collision-detect. */
+ 0x40, /* 9: 64-byte minimum frame length. */
+ 0x5F, /* A: Type/length checks OFF, no CRC input,
+ "jabber" termination, etc. */
+ 0x00, /* B: Full-duplex disabled. */
+ 0x3F, /* C: Default multicast addresses & backoff. */
+ 0x07, /* D: Default IFS retriggering. */
+ 0x31, /* E: Internal retransmit, drop "runt" packets,
+ synchr. DRQ deassertion, 6 status bytes. */
+ 0x22, /* F: Receive ring-buffer size (8K),
+ receive-stop register enable. */
+};
+
+struct netidblk {
+ char magic[8]; /* The magic number (string) "NETIDBLK" */
+ unsigned char netid[8]; /* The physical station address */
+ char nettype, globalopt;
+ char vendor[8]; /* The machine vendor and product name. */
+ char product[8];
+ char irq1, irq2; /* Interrupts, only one is currently used. */
+ char dma1, dma2;
+ short dma_mem_misc[8]; /* DMA buffer locations (unused in Linux). */
+ short iobase1, iosize1;
+ short iobase2, iosize2; /* Second iobase unused. */
+ char driver_options; /* Misc. bits */
+ char pad;
+};
+
+int znet_probe(struct device *dev);
+static int znet_open(struct device *dev);
+static int znet_send_packet(struct sk_buff *skb, struct device *dev);
+static void znet_interrupt(int irq, struct pt_regs *regs);
+static void znet_rx(struct device *dev);
+static int znet_close(struct device *dev);
+static struct enet_statistics *net_get_stats(struct device *dev);
+static void set_multicast_list(struct device *dev);
+static void hardware_init(struct device *dev);
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset);
+
+#ifdef notdef
+static struct sigaction znet_sigaction = { &znet_interrupt, 0, 0, NULL, };
+#endif
+
+
+/* The Z-Note probe is pretty easy. The NETIDBLK exists in the safe-to-probe
+ BIOS area. We just scan for the signature, and pull the vital parameters
+ out of the structure. */
+
+int znet_probe(struct device *dev)
+{
+ int i;
+ struct netidblk *netinfo;
+ char *p;
+
+ /* This code scans the region 0xf0000 to 0xfffff for a "NETIDBLK". */
+ for(p = (char *)0xf0000; p < (char *)0x100000; p++)
+ if (*p == 'N' && strncmp(p, "NETIDBLK", 8) == 0)
+ break;
+
+ if (p >= (char *)0x100000) {
+ if (znet_debug > 1)
+ printk(KERN_INFO "No Z-Note ethernet adaptor found.\n");
+ return ENODEV;
+ }
+ netinfo = (struct netidblk *)p;
+ dev->base_addr = netinfo->iobase1;
+ dev->irq = netinfo->irq1;
+
+ printk(KERN_INFO "%s: ZNET at %#3lx,", dev->name, dev->base_addr);
+
+ /* The station address is in the "netidblk" at 0x0f0000. */
+ for (i = 0; i < 6; i++)
+ printk(" %2.2x", dev->dev_addr[i] = netinfo->netid[i]);
+
+ printk(", using IRQ %d DMA %d and %d.\n", dev->irq, netinfo->dma1,
+ netinfo->dma2);
+
+ if (znet_debug > 1) {
+ printk(KERN_INFO "%s: vendor '%16.16s' IRQ1 %d IRQ2 %d DMA1 %d DMA2 %d.\n",
+ dev->name, netinfo->vendor,
+ netinfo->irq1, netinfo->irq2,
+ netinfo->dma1, netinfo->dma2);
+ printk(KERN_INFO "%s: iobase1 %#x size %d iobase2 %#x size %d net type %2.2x.\n",
+ dev->name, netinfo->iobase1, netinfo->iosize1,
+ netinfo->iobase2, netinfo->iosize2, netinfo->nettype);
+ }
+
+ if (znet_debug > 0)
+ printk("%s%s", KERN_INFO, version);
+
+ dev->priv = (void *) &zn;
+ zn.rx_dma = netinfo->dma1;
+ zn.tx_dma = netinfo->dma2;
+
+ /* These should never fail. You can't add devices to a sealed box! */
+ if (request_irq(dev->irq, &znet_interrupt, 0, "ZNet")
+ || request_dma(zn.rx_dma,"ZNet rx")
+ || request_dma(zn.tx_dma,"ZNet tx")) {
+ printk(KERN_WARNING "%s: Not opened -- resource busy?!?\n", dev->name);
+ return EBUSY;
+ }
+ irq2dev_map[dev->irq] = dev;
+
+ /* Allocate buffer memory. We can cross a 128K boundary, so we
+ must be careful about the allocation. It's easiest to waste 8K. */
+ if (dma_page_eq(dma_buffer1, &dma_buffer1[RX_BUF_SIZE/2-1]))
+ zn.rx_start = dma_buffer1;
+ else
+ zn.rx_start = dma_buffer2;
+
+ if (dma_page_eq(dma_buffer3, &dma_buffer3[RX_BUF_SIZE/2-1]))
+ zn.tx_start = dma_buffer3;
+ else
+ zn.tx_start = dma_buffer2;
+ zn.rx_end = zn.rx_start + RX_BUF_SIZE/2;
+ zn.tx_buf_len = TX_BUF_SIZE/2;
+ zn.tx_end = zn.tx_start + zn.tx_buf_len;
+
+ /* The ZNET-specific entries in the device structure. */
+ dev->open = &znet_open;
+ dev->hard_start_xmit = &znet_send_packet;
+ dev->stop = &znet_close;
+ dev->get_stats = net_get_stats;
+ dev->set_multicast_list = &set_multicast_list;
+
+ /* Fill in the 'dev' with ethernet-generic values. */
+ ether_setup(dev);
+
+ return 0;
+}
+
+
+static int znet_open(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 2)
+ printk(KERN_DEBUG "%s: znet_open() called.\n", dev->name);
+
+ /* Turn on the 82501 SIA, using zenith-specific magic. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) | 0x84, 0xe7); /* Turn on LAN power (bit 2). */
+ /* According to the Crynwr driver we should wait 50 msec. for the
+ LAN clock to stabilize. My experiments indicates that the '593 can
+ be initialized immediately. The delay is probably needed for the
+ DC-to-DC converter to come up to full voltage, and for the oscillator
+ to be spot-on at 20Mhz before transmitting.
+ Until this proves to be a problem we rely on the higher layers for the
+ delay and save allocating a timer entry. */
+
+ /* This follows the packet driver's lead, and checks for success. */
+ if (inb(ioaddr) != 0x10 && inb(ioaddr) != 0x00)
+ printk(KERN_WARNING "%s: Problem turning on the transceiver power.\n",
+ dev->name);
+
+ dev->tbusy = 0;
+ dev->interrupt = 0;
+ hardware_init(dev);
+ dev->start = 1;
+
+ return 0;
+}
+
+static int znet_send_packet(struct sk_buff *skb, struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: ZNet_send_packet(%ld).\n", dev->name, dev->tbusy);
+
+ /* Transmitter timeout, likely just recovery after suspending the machine. */
+ if (dev->tbusy) {
+ ushort event, tx_status, rx_offset, state;
+ int tickssofar = jiffies - dev->trans_start;
+ if (tickssofar < 10)
+ return 1;
+ outb(CMD0_STAT0, ioaddr); event = inb(ioaddr);
+ outb(CMD0_STAT1, ioaddr); tx_status = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr); rx_offset = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr); state = inb(ioaddr);
+ printk(KERN_WARNING "%s: transmit timed out, status %02x %04x %04x %02x,"
+ " resetting.\n", dev->name, event, tx_status, rx_offset, state);
+ if (tx_status == 0x0400)
+ printk(KERN_WARNING "%s: Tx carrier error, check transceiver cable.\n",
+ dev->name);
+ outb(CMD0_RESET, ioaddr);
+ hardware_init(dev);
+ }
+
+ if (skb == NULL) {
+ dev_tint(dev);
+ return 0;
+ }
+
+ /* Check that the part hasn't reset itself, probably from suspend. */
+ outb(CMD0_STAT0, ioaddr);
+ if (inw(ioaddr) == 0x0010
+ && inw(ioaddr) == 0x0000
+ && inw(ioaddr) == 0x0010)
+ hardware_init(dev);
+
+ /* Block a timer-based transmit from overlapping. This could better be
+ done with atomic_swap(1, dev->tbusy), but set_bit() works as well. */
+ if (set_bit(0, (void*)&dev->tbusy) != 0)
+ printk(KERN_WARNING "%s: Transmitter access conflict.\n", dev->name);
+ else {
+ short length = ETH_ZLEN < skb->len ? skb->len : ETH_ZLEN;
+ unsigned char *buf = (void *)skb->data;
+ ushort *tx_link = zn.tx_cur - 1;
+ ushort rnd_len = (length + 1)>>1;
+
+ {
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ addr <<= 1;
+ if (((int)zn.tx_cur & 0x1ffff) != addr)
+ printk(KERN_WARNING "Address mismatch at Tx: %#x vs %#x.\n",
+ (int)zn.tx_cur & 0xffff, addr);
+ zn.tx_cur = (ushort *)(((int)zn.tx_cur & 0xfe0000) | addr);
+ }
+
+ if (zn.tx_cur >= zn.tx_end)
+ zn.tx_cur = zn.tx_start;
+ *zn.tx_cur++ = length;
+ if (zn.tx_cur + rnd_len + 1 > zn.tx_end) {
+ int semi_cnt = (zn.tx_end - zn.tx_cur)<<1; /* Cvrt to byte cnt. */
+ memcpy(zn.tx_cur, buf, semi_cnt);
+ rnd_len -= semi_cnt>>1;
+ memcpy(zn.tx_start, buf + semi_cnt, length - semi_cnt);
+ zn.tx_cur = zn.tx_start + rnd_len;
+ } else {
+ memcpy(zn.tx_cur, buf, skb->len);
+ zn.tx_cur += rnd_len;
+ }
+ *zn.tx_cur++ = 0;
+ cli(); {
+ *tx_link = CMD0_TRANSMIT + CMD0_CHNL_1;
+ /* Is this always safe to do? */
+ outb(CMD0_TRANSMIT + CMD0_CHNL_1,ioaddr);
+ } sti();
+
+ dev->trans_start = jiffies;
+ if (znet_debug > 4)
+ printk(KERN_DEBUG "%s: Transmitter queued, length %d.\n", dev->name, length);
+ }
+ dev_kfree_skb(skb, FREE_WRITE);
+ return 0;
+}
+
+/* The ZNET interrupt handler. */
+static void znet_interrupt(int irq, struct pt_regs * regs)
+{
+ struct device *dev = irq2dev_map[irq];
+ int ioaddr;
+ int boguscnt = 20;
+
+ if (dev == NULL) {
+ printk(KERN_WARNING "znet_interrupt(): IRQ %d for unknown device.\n", irq);
+ return;
+ }
+
+ dev->interrupt = 1;
+ ioaddr = dev->base_addr;
+
+ outb(CMD0_STAT0, ioaddr);
+ do {
+ ushort status = inb(ioaddr);
+ if (znet_debug > 5) {
+ ushort result, rx_ptr, running;
+ outb(CMD0_STAT1, ioaddr);
+ result = inw(ioaddr);
+ outb(CMD0_STAT2, ioaddr);
+ rx_ptr = inw(ioaddr);
+ outb(CMD0_STAT3, ioaddr);
+ running = inb(ioaddr);
+ printk(KERN_DEBUG "%s: interrupt, status %02x, %04x %04x %02x serial %d.\n",
+ dev->name, status, result, rx_ptr, running, boguscnt);
+ }
+ if ((status & 0x80) == 0)
+ break;
+
+ if ((status & 0x0F) == 4) { /* Transmit done. */
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int tx_status;
+ outb(CMD0_STAT1, ioaddr);
+ tx_status = inw(ioaddr);
+ /* It's undocumented, but tx_status seems to match the i82586. */
+ if (tx_status & 0x2000) {
+ lp->stats.tx_packets++;
+ lp->stats.collisions += tx_status & 0xf;
+ } else {
+ if (tx_status & 0x0600) lp->stats.tx_carrier_errors++;
+ if (tx_status & 0x0100) lp->stats.tx_fifo_errors++;
+ if (!(tx_status & 0x0040)) lp->stats.tx_heartbeat_errors++;
+ if (tx_status & 0x0020) lp->stats.tx_aborted_errors++;
+ /* ...and the catch-all. */
+ if ((tx_status | 0x0760) != 0x0760)
+ lp->stats.tx_errors++;
+ }
+ dev->tbusy = 0;
+ mark_bh(NET_BH); /* Inform upper layers. */
+ }
+
+ if ((status & 0x40)
+ || (status & 0x0f) == 11) {
+ znet_rx(dev);
+ }
+ /* Clear the interrupts we've handled. */
+ outb(CMD0_ACK,ioaddr);
+ } while (boguscnt--);
+
+ dev->interrupt = 0;
+ return;
+}
+
+static void znet_rx(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+ int ioaddr = dev->base_addr;
+ int boguscount = 1;
+ short next_frame_end_offset = 0; /* Offset of next frame start. */
+ short *cur_frame_end;
+ short cur_frame_end_offset;
+
+ outb(CMD0_STAT2, ioaddr);
+ cur_frame_end_offset = inw(ioaddr);
+
+ if (cur_frame_end_offset == zn.rx_cur - zn.rx_start) {
+ printk(KERN_WARNING "%s: Interrupted, but nothing to receive, offset %03x.\n",
+ dev->name, cur_frame_end_offset);
+ return;
+ }
+
+ /* Use same method as the Crynwr driver: construct a forward list in
+ the same area of the backwards links we now have. This allows us to
+ pass packets to the upper layers in the order they were received --
+ important for fast-path sequential operations. */
+ while (zn.rx_start + cur_frame_end_offset != zn.rx_cur
+ && ++boguscount < 5) {
+ unsigned short hi_cnt, lo_cnt, hi_status, lo_status;
+ int count, status;
+
+ if (cur_frame_end_offset < 4) {
+ /* Oh no, we have a special case: the frame trailer wraps around
+ the end of the ring buffer. We've saved space at the end of
+ the ring buffer for just this problem. */
+ memcpy(zn.rx_end, zn.rx_start, 8);
+ cur_frame_end_offset += (RX_BUF_SIZE/2);
+ }
+ cur_frame_end = zn.rx_start + cur_frame_end_offset - 4;
+
+ lo_status = *cur_frame_end++;
+ hi_status = *cur_frame_end++;
+ status = ((hi_status & 0xff) << 8) + (lo_status & 0xff);
+ lo_cnt = *cur_frame_end++;
+ hi_cnt = *cur_frame_end++;
+ count = ((hi_cnt & 0xff) << 8) + (lo_cnt & 0xff);
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Constructing trailer at location %03x, %04x %04x %04x %04x"
+ " count %#x status %04x.\n",
+ cur_frame_end_offset<<1, lo_status, hi_status, lo_cnt, hi_cnt,
+ count, status);
+ cur_frame_end[-4] = status;
+ cur_frame_end[-3] = next_frame_end_offset;
+ cur_frame_end[-2] = count;
+ next_frame_end_offset = cur_frame_end_offset;
+ cur_frame_end_offset -= ((count + 1)>>1) + 3;
+ if (cur_frame_end_offset < 0)
+ cur_frame_end_offset += RX_BUF_SIZE/2;
+ };
+
+ /* Now step forward through the list. */
+ do {
+ ushort *this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ int status = this_rfp_ptr[-4];
+ int pkt_len = this_rfp_ptr[-2];
+
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Looking at trailer ending at %04x status %04x length %03x"
+ " next %04x.\n", next_frame_end_offset<<1, status, pkt_len,
+ this_rfp_ptr[-3]<<1);
+ /* Once again we must assume that the i82586 docs apply. */
+ if ( ! (status & 0x2000)) { /* There was an error. */
+ lp->stats.rx_errors++;
+ if (status & 0x0800) lp->stats.rx_crc_errors++;
+ if (status & 0x0400) lp->stats.rx_frame_errors++;
+ if (status & 0x0200) lp->stats.rx_over_errors++; /* Wrong. */
+ if (status & 0x0100) lp->stats.rx_fifo_errors++;
+ if (status & 0x0080) lp->stats.rx_length_errors++;
+ } else if (pkt_len > 1536) {
+ lp->stats.rx_length_errors++;
+ } else {
+ /* Malloc up new buffer. */
+ struct sk_buff *skb;
+
+ skb = dev_alloc_skb(pkt_len);
+ if (skb == NULL) {
+ if (znet_debug)
+ printk(KERN_WARNING "%s: Memory squeeze, dropping packet.\n", dev->name);
+ lp->stats.rx_dropped++;
+ break;
+ }
+ skb->dev = dev;
+
+ if (&zn.rx_cur[(pkt_len+1)>>1] > zn.rx_end) {
+ int semi_cnt = (zn.rx_end - zn.rx_cur)<<1;
+ memcpy(skb_put(skb,semi_cnt), zn.rx_cur, semi_cnt);
+ memcpy(skb_put(skb,pkt_len-semi_cnt), zn.rx_start,
+ pkt_len - semi_cnt);
+ } else {
+ memcpy(skb_put(skb,pkt_len), zn.rx_cur, pkt_len);
+ if (znet_debug > 6) {
+ unsigned int *packet = (unsigned int *) skb->data;
+ printk(KERN_DEBUG "Packet data is %08x %08x %08x %08x.\n", packet[0],
+ packet[1], packet[2], packet[3]);
+ }
+ }
+ skb->protocol=eth_type_trans(skb,dev);
+ netif_rx(skb);
+ lp->stats.rx_packets++;
+ }
+ zn.rx_cur = this_rfp_ptr;
+ if (zn.rx_cur >= zn.rx_end)
+ zn.rx_cur -= RX_BUF_SIZE/2;
+ update_stop_hit(ioaddr, (zn.rx_cur - zn.rx_start)<<1);
+ next_frame_end_offset = this_rfp_ptr[-3];
+ if (next_frame_end_offset == 0) /* Read all the frames? */
+ break; /* Done for now */
+ this_rfp_ptr = zn.rx_start + next_frame_end_offset;
+ } while (--boguscount);
+
+ /* If any worth-while packets have been received, dev_rint()
+ has done a mark_bh(INET_BH) for us and will work on them
+ when we get to the bottom-half routine. */
+ return;
+}
+
+/* The inverse routine to znet_open(). */
+static int znet_close(struct device *dev)
+{
+ int ioaddr = dev->base_addr;
+
+ dev->tbusy = 1;
+ dev->start = 0;
+
+ outb(CMD0_RESET, ioaddr); /* CMD0_RESET */
+
+ disable_dma(zn.rx_dma);
+ disable_dma(zn.tx_dma);
+
+ free_irq(dev->irq);
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Shutting down ethercard.\n", dev->name);
+ /* Turn off transceiver power. */
+ outb(0x10, 0xe6); /* Select LAN control register */
+ outb(inb(0xe7) & ~0x84, 0xe7); /* Turn on LAN power (bit 2). */
+
+ return 0;
+}
+
+/* Get the current statistics. This may be called with the card open or
+ closed. */
+static struct enet_statistics *net_get_stats(struct device *dev)
+{
+ struct net_local *lp = (struct net_local *)dev->priv;
+
+ return &lp->stats;
+}
+
+/* Set or clear the multicast filter for this adaptor.
+ As a side effect this routine must also initialize the device parameters.
+ This is taken advantage of in open().
+
+ N.B. that we change i593_init[] in place. This (properly) makes the
+ mode change persistent, but must be changed if this code is moved to
+ a multiple adaptor environment.
+ */
+static void set_multicast_list(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ if (dev->flags&IFF_PROMISC) {
+ /* Enable promiscuous mode */
+ i593_init[7] &= ~3; i593_init[7] |= 1;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else if (dev->mc_list || (dev->flags&IFF_ALLMULTI)) {
+ /* Enable accept-all-multicast mode */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 8;
+ } else { /* Enable normal mode. */
+ i593_init[7] &= ~3; i593_init[7] |= 0;
+ i593_init[13] &= ~8; i593_init[13] |= 0;
+ }
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+#ifdef not_tested
+ if (num_addrs > 0) {
+ int addrs_len = 6*num_addrs;
+ *zn.tx_cur++ = addrs_len;
+ memcpy(zn.tx_cur, addrs, addrs_len);
+ outb(CMD0_MULTICAST_LIST+CMD0_CHNL_1, ioaddr);
+ zn.tx_cur += addrs_len>>1;
+ }
+#endif
+}
+
+void show_dma(void)
+{
+ short dma_port = ((zn.tx_dma&3)<<2) + IO_DMA2_BASE;
+ unsigned addr = inb(dma_port);
+ addr |= inb(dma_port) << 8;
+ printk("Addr: %04x cnt:%3x...", addr<<1, get_dma_residue(zn.tx_dma));
+}
+
+/* Initialize the hardware. We have to do this when the board is open()ed
+ or when we come out of suspend mode. */
+static void hardware_init(struct device *dev)
+{
+ short ioaddr = dev->base_addr;
+
+ zn.rx_cur = zn.rx_start;
+ zn.tx_cur = zn.tx_start;
+
+ /* Reset the chip, and start it up. */
+ outb(CMD0_RESET, ioaddr);
+
+ cli(); { /* Protect against a DMA flip-flop */
+ disable_dma(zn.rx_dma); /* reset by an interrupting task. */
+ clear_dma_ff(zn.rx_dma);
+ set_dma_mode(zn.rx_dma, DMA_RX_MODE);
+ set_dma_addr(zn.rx_dma, (unsigned int) zn.rx_start);
+ set_dma_count(zn.rx_dma, RX_BUF_SIZE);
+ enable_dma(zn.rx_dma);
+ /* Now set up the Tx channel. */
+ disable_dma(zn.tx_dma);
+ clear_dma_ff(zn.tx_dma);
+ set_dma_mode(zn.tx_dma, DMA_TX_MODE);
+ set_dma_addr(zn.tx_dma, (unsigned int) zn.tx_start);
+ set_dma_count(zn.tx_dma, zn.tx_buf_len<<1);
+ enable_dma(zn.tx_dma);
+ } sti();
+
+ if (znet_debug > 1)
+ printk(KERN_DEBUG "%s: Initializing the i82593, tx buf %p... ", dev->name,
+ zn.tx_start);
+ /* Do an empty configure command, just like the Crynwr driver. This
+ resets to chip to its default values. */
+ *zn.tx_cur++ = 0;
+ *zn.tx_cur++ = 0;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = sizeof(i593_init);
+ memcpy(zn.tx_cur, i593_init, sizeof(i593_init));
+ zn.tx_cur += sizeof(i593_init)/2;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_CONFIGURE+CMD0_CHNL_1, ioaddr);
+ *zn.tx_cur++ = 6;
+ memcpy(zn.tx_cur, dev->dev_addr, 6);
+ zn.tx_cur += 3;
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+ outb(CMD0_IA_SETUP + CMD0_CHNL_1, ioaddr);
+ printk("stat:%02x ", inb(ioaddr)); show_dma();
+
+ update_stop_hit(ioaddr, 8192);
+ if (znet_debug > 1) printk("enabling Rx.\n");
+ outb(CMD0_Rx_ENABLE+CMD0_CHNL_0, ioaddr);
+ dev->tbusy = 0;
+}
+
+static void update_stop_hit(short ioaddr, unsigned short rx_stop_offset)
+{
+ outb(CMD0_PORT_1, ioaddr);
+ if (znet_debug > 5)
+ printk(KERN_DEBUG "Updating stop hit with value %02x.\n",
+ (rx_stop_offset >> 6) | 0x80);
+ outb((rx_stop_offset >> 6) | 0x80, ioaddr);
+ outb(CMD1_PORT_0, ioaddr);
+}
+
+/*
+ * Local variables:
+ * compile-command: "gcc -D__KERNEL__ -I/usr/src/linux/net/inet -Wall -Wstrict-prototypes -O6 -m486 -c znet.c"
+ * version-control: t
+ * kept-new-versions: 5
+ * c-indent-level: 4
+ * tab-width: 4
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/pci/bios32.c b/i386/i386at/gpl/linux/pci/bios32.c
new file mode 100644
index 00000000..e10fdab3
--- /dev/null
+++ b/i386/i386at/gpl/linux/pci/bios32.c
@@ -0,0 +1,460 @@
+/*
+ * bios32.c - BIOS32, PCI BIOS functions.
+ *
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * Drew@Colorado.EDU
+ * +1 (303) 786-7975
+ *
+ * For more information, please consult
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ *
+ * Manuals are $25 each or $50 for all three, plus $7 shipping
+ * within the United States, $35 abroad.
+ *
+ *
+ * CHANGELOG :
+ * Jun 17, 1994 : Modified to accommodate the broken pre-PCI BIOS SPECIFICATION
+ * Revision 2.0 present on <thys@dennis.ee.up.ac.za>'s ASUS mainboard.
+ *
+ * Jan 5, 1995 : Modified to probe PCI hardware at boot time by Frederic
+ * Potter, potter@cao-vlsi.ibp.fr
+ *
+ * Jan 10, 1995 : Modified to store the information about configured pci
+ * devices into a list, which can be accessed via /proc/pci by
+ * Curtis Varner, cvarner@cs.ucr.edu
+ *
+ * Jan 12, 1995 : CPU-PCI bridge optimization support by Frederic Potter.
+ * Alpha version. Intel & UMC chipset support only.
+ *
+ * Apr 16, 1995 : Source merge with the DEC Alpha PCI support. Most of the code
+ * moved to drivers/pci/pci.c.
+ *
+ *
+ */
+
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+
+#include <asm/segment.h>
+
+#define PCIBIOS_PCI_FUNCTION_ID 0xb1XX
+#define PCIBIOS_PCI_BIOS_PRESENT 0xb101
+#define PCIBIOS_FIND_PCI_DEVICE 0xb102
+#define PCIBIOS_FIND_PCI_CLASS_CODE 0xb103
+#define PCIBIOS_GENERATE_SPECIAL_CYCLE 0xb106
+#define PCIBIOS_READ_CONFIG_BYTE 0xb108
+#define PCIBIOS_READ_CONFIG_WORD 0xb109
+#define PCIBIOS_READ_CONFIG_DWORD 0xb10a
+#define PCIBIOS_WRITE_CONFIG_BYTE 0xb10b
+#define PCIBIOS_WRITE_CONFIG_WORD 0xb10c
+#define PCIBIOS_WRITE_CONFIG_DWORD 0xb10d
+
+
+/* BIOS32 signature: "_32_" */
+#define BIOS32_SIGNATURE (('_' << 0) + ('3' << 8) + ('2' << 16) + ('_' << 24))
+
+/* PCI signature: "PCI " */
+#define PCI_SIGNATURE (('P' << 0) + ('C' << 8) + ('I' << 16) + (' ' << 24))
+
+/* PCI service signature: "$PCI" */
+#define PCI_SERVICE (('$' << 0) + ('P' << 8) + ('C' << 16) + ('I' << 24))
+
+/*
+ * This is the standard structure used to identify the entry point
+ * to the BIOS32 Service Directory, as documented in
+ * Standard BIOS 32-bit Service Directory Proposal
+ * Revision 0.4 May 24, 1993
+ * Phoenix Technologies Ltd.
+ * Norwood, MA
+ * and the PCI BIOS specification.
+ */
+
+union bios32 {
+ struct {
+ unsigned long signature; /* _32_ */
+ unsigned long entry; /* 32 bit physical address */
+ unsigned char revision; /* Revision level, 0 */
+ unsigned char length; /* Length in paragraphs should be 01 */
+ unsigned char checksum; /* All bytes must add up to zero */
+ unsigned char reserved[5]; /* Must be zero */
+ } fields;
+ char chars[16];
+};
+
+/*
+ * Physical address of the service directory. I don't know if we're
+ * allowed to have more than one of these or not, so just in case
+ * we'll make pcibios_present() take a memory start parameter and store
+ * the array there.
+ */
+
+static unsigned long bios32_entry = 0;
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} bios32_indirect = { 0, KERNEL_CS };
+
+#ifdef CONFIG_PCI
+/*
+ * Returns the entry point for the given service, NULL on error
+ */
+
+static unsigned long bios32_service(unsigned long service)
+{
+ unsigned char return_code; /* %al */
+ unsigned long address; /* %ebx */
+ unsigned long length; /* %ecx */
+ unsigned long entry; /* %edx */
+
+ __asm__("lcall (%%edi)"
+ : "=a" (return_code),
+ "=b" (address),
+ "=c" (length),
+ "=d" (entry)
+ : "0" (service),
+ "1" (0),
+ "D" (&bios32_indirect));
+
+ switch (return_code) {
+ case 0:
+ return address + entry;
+ case 0x80: /* Not present */
+ printk("bios32_service(%ld) : not present\n", service);
+ return 0;
+ default: /* Shouldn't happen */
+ printk("bios32_service(%ld) : returned 0x%x, mail drew@colorado.edu\n",
+ service, return_code);
+ return 0;
+ }
+}
+
+static long pcibios_entry = 0;
+static struct {
+ unsigned long address;
+ unsigned short segment;
+} pci_indirect = { 0, KERNEL_CS };
+
+
+extern unsigned long check_pcibios(unsigned long memory_start, unsigned long memory_end)
+{
+ unsigned long signature;
+ unsigned char present_status;
+ unsigned char major_revision;
+ unsigned char minor_revision;
+ int pack;
+
+ if ((pcibios_entry = bios32_service(PCI_SERVICE))) {
+ pci_indirect.address = pcibios_entry;
+
+ __asm__("lcall (%%edi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:\tshl $8, %%eax\n\t"
+ "movw %%bx, %%ax"
+ : "=d" (signature),
+ "=a" (pack)
+ : "1" (PCIBIOS_PCI_BIOS_PRESENT),
+ "D" (&pci_indirect)
+ : "bx", "cx");
+
+ present_status = (pack >> 16) & 0xff;
+ major_revision = (pack >> 8) & 0xff;
+ minor_revision = pack & 0xff;
+ if (present_status || (signature != PCI_SIGNATURE)) {
+ printk ("pcibios_init : %s : BIOS32 Service Directory says PCI BIOS is present,\n"
+ " but PCI_BIOS_PRESENT subfunction fails with present status of 0x%x\n"
+ " and signature of 0x%08lx (%c%c%c%c). mail drew@Colorado.EDU\n",
+ (signature == PCI_SIGNATURE) ? "WARNING" : "ERROR",
+ present_status, signature,
+ (char) (signature >> 0), (char) (signature >> 8),
+ (char) (signature >> 16), (char) (signature >> 24));
+
+ if (signature != PCI_SIGNATURE)
+ pcibios_entry = 0;
+ }
+ if (pcibios_entry) {
+ printk ("pcibios_init : PCI BIOS revision %x.%02x entry at 0x%lx\n",
+ major_revision, minor_revision, pcibios_entry);
+ }
+ }
+ return memory_start;
+}
+
+int pcibios_present(void)
+{
+ return pcibios_entry ? 1 : 0;
+}
+
+int pcibios_find_class (unsigned int class_code, unsigned short index,
+ unsigned char *bus, unsigned char *device_fn)
+{
+ unsigned long bx;
+ unsigned long ret;
+
+ __asm__ ("lcall (%%edi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=b" (bx),
+ "=a" (ret)
+ : "1" (PCIBIOS_FIND_PCI_CLASS_CODE),
+ "c" (class_code),
+ "S" ((int) index),
+ "D" (&pci_indirect));
+ *bus = (bx >> 8) & 0xff;
+ *device_fn = bx & 0xff;
+ return (int) (ret & 0xff00) >> 8;
+}
+
+
+int pcibios_find_device (unsigned short vendor, unsigned short device_id,
+ unsigned short index, unsigned char *bus, unsigned char *device_fn)
+{
+ unsigned short bx;
+ unsigned short ret;
+
+ __asm__("lcall (%%edi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=b" (bx),
+ "=a" (ret)
+ : "1" (PCIBIOS_FIND_PCI_DEVICE),
+ "c" (device_id),
+ "d" (vendor),
+ "S" ((int) index),
+ "D" (&pci_indirect));
+ *bus = (bx >> 8) & 0xff;
+ *device_fn = bx & 0xff;
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_byte(unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned char *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_BYTE),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_word (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned short *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_WORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_read_config_dword (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned int *value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=c" (*value),
+ "=a" (ret)
+ : "1" (PCIBIOS_READ_CONFIG_DWORD),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_byte (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned char value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_BYTE),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_word (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned short value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_WORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+int pcibios_write_config_dword (unsigned char bus,
+ unsigned char device_fn, unsigned char where, unsigned int value)
+{
+ unsigned long ret;
+ unsigned long bx = (bus << 8) | device_fn;
+
+ __asm__("lcall (%%esi)\n\t"
+ "jc 1f\n\t"
+ "xor %%ah, %%ah\n"
+ "1:"
+ : "=a" (ret)
+ : "0" (PCIBIOS_WRITE_CONFIG_DWORD),
+ "c" (value),
+ "b" (bx),
+ "D" ((long) where),
+ "S" (&pci_indirect));
+ return (int) (ret & 0xff00) >> 8;
+}
+
+const char *pcibios_strerror (int error)
+{
+ static char buf[80];
+
+ switch (error) {
+ case PCIBIOS_SUCCESSFUL:
+ return "SUCCESSFUL";
+
+ case PCIBIOS_FUNC_NOT_SUPPORTED:
+ return "FUNC_NOT_SUPPORTED";
+
+ case PCIBIOS_BAD_VENDOR_ID:
+ return "SUCCESSFUL";
+
+ case PCIBIOS_DEVICE_NOT_FOUND:
+ return "DEVICE_NOT_FOUND";
+
+ case PCIBIOS_BAD_REGISTER_NUMBER:
+ return "BAD_REGISTER_NUMBER";
+
+ case PCIBIOS_SET_FAILED:
+ return "SET_FAILED";
+
+ case PCIBIOS_BUFFER_TOO_SMALL:
+ return "BUFFER_TOO_SMALL";
+
+ default:
+ sprintf (buf, "UNKNOWN RETURN 0x%x", error);
+ return buf;
+ }
+}
+
+
+unsigned long pcibios_fixup(unsigned long mem_start, unsigned long mem_end)
+{
+return mem_start;
+}
+
+
+#endif
+
+unsigned long pcibios_init(unsigned long memory_start, unsigned long memory_end)
+{
+ union bios32 *check;
+ unsigned char sum;
+ int i, length;
+
+ /*
+ * Follow the standard procedure for locating the BIOS32 Service
+ * directory by scanning the permissible address range from
+ * 0xe0000 through 0xfffff for a valid BIOS32 structure.
+ *
+ */
+
+ for (check = (union bios32 *) 0xe0000; check <= (union bios32 *) 0xffff0; ++check) {
+ if (check->fields.signature != BIOS32_SIGNATURE)
+ continue;
+ length = check->fields.length * 16;
+ if (!length)
+ continue;
+ sum = 0;
+ for (i = 0; i < length ; ++i)
+ sum += check->chars[i];
+ if (sum != 0)
+ continue;
+ if (check->fields.revision != 0) {
+ printk("pcibios_init : unsupported revision %d at 0x%p, mail drew@colorado.edu\n",
+ check->fields.revision, check);
+ continue;
+ }
+ printk ("pcibios_init : BIOS32 Service Directory structure at 0x%p\n", check);
+ if (!bios32_entry) {
+ if (check->fields.entry >= 0x100000) {
+ printk("pcibios_init: entry in high memory, unable to access\n");
+ } else {
+ bios32_indirect.address = bios32_entry = check->fields.entry;
+ printk ("pcibios_init : BIOS32 Service Directory entry at 0x%lx\n", bios32_entry);
+ }
+ } else {
+ printk ("pcibios_init : multiple entries, mail drew@colorado.edu\n");
+ /*
+ * Jeremy Fitzhardinge reports at least one PCI BIOS
+ * with two different service directories, and as both
+ * worked for him, we'll just mention the fact, and
+ * not actually disallow it..
+ */
+ }
+ }
+#ifdef CONFIG_PCI
+ if (bios32_entry) {
+ memory_start = check_pcibios (memory_start, memory_end);
+ }
+#endif
+ return memory_start;
+}
diff --git a/i386/i386at/gpl/linux/pci/pci.c b/i386/i386at/gpl/linux/pci/pci.c
new file mode 100644
index 00000000..03846d02
--- /dev/null
+++ b/i386/i386at/gpl/linux/pci/pci.c
@@ -0,0 +1,915 @@
+/*
+ * drivers/pci/pci.c
+ *
+ * PCI services that are built on top of the BIOS32 service.
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt, Frederic Potter,
+ * David Mosberger-Tang
+ */
+#include <linux/config.h>
+#include <linux/ptrace.h>
+#include <linux/types.h>
+#include <linux/kernel.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+
+#include <asm/page.h>
+
+struct pci_bus pci_root;
+struct pci_dev *pci_devices = 0;
+
+
+/*
+ * The bridge_id field is an offset of an item into the array
+ * BRIDGE_MAPPING_TYPE. 0xff indicates that the device is not a PCI
+ * bridge, or that we don't know for the moment how to configure it.
+ * I'm trying to do my best so that the kernel stays small. Different
+ * chipset can have same optimization structure. i486 and pentium
+ * chipsets from the same manufacturer usually have the same
+ * structure.
+ */
+#define DEVICE(vid,did,name) \
+ {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), 0xff}
+
+#define BRIDGE(vid,did,name,bridge) \
+ {PCI_VENDOR_ID_##vid, PCI_DEVICE_ID_##did, (name), (bridge)}
+
+/*
+ * Sorted in ascending order by vendor and device.
+ * Use binary search for lookup. If you add a device make sure
+ * it is sequential by both vendor and device id.
+ */
+struct pci_dev_info dev_info[] = {
+ DEVICE( COMPAQ, COMPAQ_1280, "QVision 1280/p"),
+ DEVICE( COMPAQ, COMPAQ_THUNDER, "ThunderLAN"),
+ DEVICE( NCR, NCR_53C810, "53c810"),
+ DEVICE( NCR, NCR_53C820, "53c820"),
+ DEVICE( NCR, NCR_53C825, "53c825"),
+ DEVICE( NCR, NCR_53C815, "53c815"),
+ DEVICE( ATI, ATI_68800, "68800AX"),
+ DEVICE( ATI, ATI_215CT222, "215CT222"),
+ DEVICE( ATI, ATI_210888CX, "210888CX"),
+ DEVICE( ATI, ATI_210888GX, "210888GX"),
+ DEVICE( VLSI, VLSI_82C592, "82C592-FC1"),
+ DEVICE( VLSI, VLSI_82C593, "82C593-FC1"),
+ DEVICE( VLSI, VLSI_82C594, "82C594-AFC2"),
+ DEVICE( VLSI, VLSI_82C597, "82C597-AFC2"),
+ DEVICE( ADL, ADL_2301, "2301"),
+ DEVICE( NS, NS_87410, "87410"),
+ DEVICE( TSENG, TSENG_W32P_2, "ET4000W32P"),
+ DEVICE( TSENG, TSENG_W32P_b, "ET4000W32P rev B"),
+ DEVICE( TSENG, TSENG_W32P_c, "ET4000W32P rev C"),
+ DEVICE( TSENG, TSENG_W32P_d, "ET4000W32P rev D"),
+ DEVICE( WEITEK, WEITEK_P9000, "P9000"),
+ DEVICE( WEITEK, WEITEK_P9100, "P9100"),
+ BRIDGE( DEC, DEC_BRD, "DC21050", 0x00),
+ DEVICE( DEC, DEC_TULIP, "DC21040"),
+ DEVICE( DEC, DEC_TGA, "DC21030"),
+ DEVICE( DEC, DEC_TULIP_FAST, "DC21140"),
+ DEVICE( DEC, DEC_FDDI, "DEFPA"),
+ DEVICE( DEC, DEC_TULIP_PLUS, "DC21041"),
+ DEVICE( CIRRUS, CIRRUS_5430, "GD 5430"),
+ DEVICE( CIRRUS, CIRRUS_5434_4, "GD 5434"),
+ DEVICE( CIRRUS, CIRRUS_5434_8, "GD 5434"),
+ DEVICE( CIRRUS, CIRRUS_5436, "GD 5436"),
+ DEVICE( CIRRUS, CIRRUS_6205, "GD 6205"),
+ DEVICE( CIRRUS, CIRRUS_6729, "CL 6729"),
+ DEVICE( CIRRUS, CIRRUS_7542, "CL 7542"),
+ DEVICE( CIRRUS, CIRRUS_7543, "CL 7543"),
+ DEVICE( IBM, IBM_82G2675, "82G2675"),
+ DEVICE( WD, WD_7197, "WD 7197"),
+ DEVICE( AMD, AMD_LANCE, "79C970"),
+ DEVICE( AMD, AMD_SCSI, "53C974"),
+ DEVICE( TRIDENT, TRIDENT_9420, "TG 9420"),
+ DEVICE( TRIDENT, TRIDENT_9440, "TG 9440"),
+ DEVICE( TRIDENT, TRIDENT_9660, "TG 9660"),
+ DEVICE( AI, AI_M1435, "M1435"),
+ DEVICE( MATROX, MATROX_MGA_2, "Atlas PX2085"),
+ DEVICE( MATROX, MATROX_MIL ,"Millenium"),
+ DEVICE( MATROX, MATROX_MGA_IMP, "MGA Impression"),
+ DEVICE( CT, CT_65545, "65545"),
+ DEVICE( FD, FD_36C70, "TMC-18C30"),
+ DEVICE( SI, SI_6201, "6201"),
+ DEVICE( SI, SI_6202, "6202"),
+ DEVICE( SI, SI_503, "85C503"),
+ DEVICE( SI, SI_501, "85C501"),
+ DEVICE( SI, SI_496, "85C496"),
+ DEVICE( SI, SI_601, "85C601"),
+ DEVICE( SI, SI_5511, "85C5511"),
+ DEVICE( SI, SI_5513, "85C5513"),
+ DEVICE( HP, HP_J2585A, "J2585A"),
+ DEVICE( PCTECH, PCTECH_RZ1000, "RZ1000 (buggy)"),
+ DEVICE( DPT, DPT, "SmartCache/Raid"),
+ DEVICE( OPTI, OPTI_92C178, "92C178"),
+ DEVICE( OPTI, OPTI_82C557, "82C557"),
+ DEVICE( OPTI, OPTI_82C558, "82C558"),
+ DEVICE( OPTI, OPTI_82C621, "82C621"),
+ DEVICE( OPTI, OPTI_82C822, "82C822"),
+ DEVICE( SGS, SGS_2000, "STG 2000X"),
+ DEVICE( SGS, SGS_1764, "STG 1764X"),
+ DEVICE( BUSLOGIC, BUSLOGIC_946C_2,"BT-946C"),
+ DEVICE( BUSLOGIC, BUSLOGIC_946C, "BT-946C"),
+ DEVICE( BUSLOGIC, BUSLOGIC_930, "BT-930"),
+ DEVICE( OAK, OAK_OTI107, "OTI107"),
+ DEVICE( PROMISE, PROMISE_5300, "DC5030"),
+ DEVICE( N9, N9_I128, "Imagine 128"),
+ DEVICE( N9, N9_I128_2, "Imagine 128v2"),
+ DEVICE( UMC, UMC_UM8673F, "UM8673F"),
+ BRIDGE( UMC, UMC_UM8891A, "UM8891A", 0x01),
+ DEVICE( UMC, UMC_UM8886BF, "UM8886BF"),
+ DEVICE( UMC, UMC_UM8886A, "UM8886A"),
+ BRIDGE( UMC, UMC_UM8881F, "UM8881F", 0x02),
+ DEVICE( UMC, UMC_UM8886F, "UM8886F"),
+ DEVICE( UMC, UMC_UM9017F, "UM9017F"),
+ DEVICE( UMC, UMC_UM8886N, "UM8886N"),
+ DEVICE( UMC, UMC_UM8891N, "UM8891N"),
+ DEVICE( X, X_AGX016, "ITT AGX016"),
+ DEVICE( NEXGEN, NEXGEN_82C501, "82C501"),
+ DEVICE( QLOGIC, QLOGIC_ISP1020, "ISP1020"),
+ DEVICE( QLOGIC, QLOGIC_ISP1022, "ISP1022"),
+ DEVICE( LEADTEK, LEADTEK_805, "S3 805"),
+ DEVICE( CONTAQ, CONTAQ_82C599, "82C599"),
+ DEVICE( CMD, CMD_640, "640 (buggy)"),
+ DEVICE( CMD, CMD_646, "646"),
+ DEVICE( VISION, VISION_QD8500, "QD-8500"),
+ DEVICE( VISION, VISION_QD8580, "QD-8580"),
+ DEVICE( SIERRA, SIERRA_STB, "STB Horizon 64"),
+ DEVICE( ACC, ACC_2056, "2056"),
+ DEVICE( WINBOND, WINBOND_83769, "W83769F"),
+ DEVICE( WINBOND, WINBOND_82C105, "SL82C105"),
+ DEVICE( 3COM, 3COM_3C590, "3C590 10bT"),
+ DEVICE( 3COM, 3COM_3C595TX, "3C595 100bTX"),
+ DEVICE( 3COM, 3COM_3C595T4, "3C595 100bT4"),
+ DEVICE( 3COM, 3COM_3C595MII, "3C595 100b-MII"),
+ DEVICE( AL, AL_M1445, "M1445"),
+ DEVICE( AL, AL_M1449, "M1449"),
+ DEVICE( AL, AL_M1451, "M1451"),
+ DEVICE( AL, AL_M1461, "M1461"),
+ DEVICE( AL, AL_M1489, "M1489"),
+ DEVICE( AL, AL_M1511, "M1511"),
+ DEVICE( AL, AL_M1513, "M1513"),
+ DEVICE( AL, AL_M4803, "M4803"),
+ DEVICE( ASP, ASP_ABP940, "ABP940"),
+ DEVICE( IMS, IMS_8849, "8849"),
+ DEVICE( TEKRAM2, TEKRAM2_690c, "DC690c"),
+ DEVICE( AMCC, AMCC_MYRINET, "Myrinet PCI (M2-PCI-32)"),
+ DEVICE( INTERG, INTERG_1680, "IGA-1680"),
+ DEVICE( REALTEK, REALTEK_8029, "8029"),
+ DEVICE( INIT, INIT_320P, "320 P"),
+ DEVICE( VIA, VIA_82C505, "VT 82C505"),
+ DEVICE( VIA, VIA_82C561, "VT 82C561"),
+ DEVICE( VIA, VIA_82C576, "VT 82C576 3V"),
+ DEVICE( VIA, VIA_82C416, "VT 82C416MV"),
+ DEVICE( VORTEX, VORTEX_GDT, "GDT 6000b"),
+ DEVICE( EF, EF_ATM_FPGA, "155P-MF1 (FPGA)"),
+ DEVICE( EF, EF_ATM_ASIC, "155P-MF1 (ASIC)"),
+ DEVICE( IMAGINGTECH, IMAGINGTECH_ICPCI, "MVC IC-PCI"),
+ DEVICE( FORE, FORE_PCA200PC, "PCA-200PC"),
+ DEVICE( PLX, PLX_9060, "PCI9060 i960 bridge"),
+ DEVICE( ALLIANCE, ALLIANCE_PROMOTIO, "Promotion-6410"),
+ DEVICE( ALLIANCE, ALLIANCE_PROVIDEO, "Provideo"),
+ DEVICE( MUTECH, MUTECH_MV1000, "MV-1000"),
+ DEVICE( ZEITNET, ZEITNET_1221, "1221"),
+ DEVICE( ZEITNET, ZEITNET_1225, "1225"),
+ DEVICE( SPECIALIX, SPECIALIX_XIO, "XIO/SIO host"),
+ DEVICE( SPECIALIX, SPECIALIX_RIO, "RIO host"),
+ DEVICE( RP, RP8OCTA, "RocketPort 8 Oct"),
+ DEVICE( RP, RP8INTF, "RocketPort 8 Intf"),
+ DEVICE( RP, RP16INTF, "RocketPort 16 Intf"),
+ DEVICE( RP, RP32INTF, "RocketPort 32 Intf"),
+ DEVICE( CYCLADES, CYCLADES_Y, "Cyclome-Y"),
+ DEVICE( SYMPHONY, SYMPHONY_101, "82C101"),
+ DEVICE( TEKRAM, TEKRAM_DC290, "DC-290"),
+ DEVICE( AVANCE, AVANCE_2302, "ALG-2302"),
+ DEVICE( S3, S3_811, "Trio32/Trio64"),
+ DEVICE( S3, S3_868, "Vision 868"),
+ DEVICE( S3, S3_928, "Vision 928-P"),
+ DEVICE( S3, S3_864_1, "Vision 864-P"),
+ DEVICE( S3, S3_864_2, "Vision 864-P"),
+ DEVICE( S3, S3_964_1, "Vision 964-P"),
+ DEVICE( S3, S3_964_2, "Vision 964-P"),
+ DEVICE( S3, S3_968, "Vision 968"),
+ DEVICE( INTEL, INTEL_82375, "82375EB"),
+ BRIDGE( INTEL, INTEL_82424, "82424ZX Saturn", 0x00),
+ DEVICE( INTEL, INTEL_82378, "82378IB"),
+ DEVICE( INTEL, INTEL_82430, "82430ZX Aries"),
+ BRIDGE( INTEL, INTEL_82434, "82434LX Mercury/Neptune", 0x00),
+ DEVICE( INTEL, INTEL_7116, "SAA7116"),
+ DEVICE( INTEL, INTEL_82596, "82596"),
+ DEVICE( INTEL, INTEL_82865, "82865"),
+ DEVICE( INTEL, INTEL_82557, "82557"),
+ DEVICE( INTEL, INTEL_82437, "82437"),
+ DEVICE( INTEL, INTEL_82371_0, "82371 Triton PIIX"),
+ DEVICE( INTEL, INTEL_82371_1, "82371 Triton PIIX"),
+ DEVICE( INTEL, INTEL_P6, "Orion P6"),
+ DEVICE( ADAPTEC, ADAPTEC_7850, "AIC-7850"),
+ DEVICE( ADAPTEC, ADAPTEC_7870, "AIC-7870"),
+ DEVICE( ADAPTEC, ADAPTEC_7871, "AIC-7871"),
+ DEVICE( ADAPTEC, ADAPTEC_7872, "AIC-7872"),
+ DEVICE( ADAPTEC, ADAPTEC_7873, "AIC-7873"),
+ DEVICE( ADAPTEC, ADAPTEC_7874, "AIC-7874"),
+ DEVICE( ADAPTEC, ADAPTEC_7880, "AIC-7880U"),
+ DEVICE( ADAPTEC, ADAPTEC_7881, "AIC-7881U"),
+ DEVICE( ADAPTEC, ADAPTEC_7882, "AIC-7882U"),
+ DEVICE( ADAPTEC, ADAPTEC_7883, "AIC-7883U"),
+ DEVICE( ADAPTEC, ADAPTEC_7884, "AIC-7884U"),
+ DEVICE( ATRONICS, ATRONICS_2015, "IDE-2015PL"),
+ DEVICE( HER, HER_STING, "Stingray"),
+ DEVICE( HER, HER_STINGARK, "Stingray ARK 2000PV")
+};
+
+
+#ifdef CONFIG_PCI_OPTIMIZE
+
+/*
+ * An item of this structure has the following meaning:
+ * for each optimization, the register address, the mask
+ * and value to write to turn it on.
+ * There are 5 optimizations for the moment:
+ * Cache L2 write back best than write through
+ * Posted Write for CPU to PCI enable
+ * Posted Write for CPU to MEMORY enable
+ * Posted Write for PCI to MEMORY enable
+ * PCI Burst enable
+ *
+ * Half of the bios I've meet don't allow you to turn that on, and you
+ * can gain more than 15% on graphic accesses using those
+ * optimizations...
+ */
+struct optimization_type {
+ const char *type;
+ const char *off;
+ const char *on;
+} bridge_optimization[] = {
+ {"Cache L2", "write through", "write back"},
+ {"CPU-PCI posted write", "off", "on"},
+ {"CPU-Memory posted write", "off", "on"},
+ {"PCI-Memory posted write", "off", "on"},
+ {"PCI burst", "off", "on"}
+};
+
+#define NUM_OPTIMIZATIONS \
+ (sizeof(bridge_optimization) / sizeof(bridge_optimization[0]))
+
+struct bridge_mapping_type {
+ unsigned char addr; /* config space address */
+ unsigned char mask;
+ unsigned char value;
+} bridge_mapping[] = {
+ /*
+ * Intel Neptune/Mercury/Saturn:
+ * If the internal cache is write back,
+ * the L2 cache must be write through!
+ * I've to check out how to control that
+ * for the moment, we won't touch the cache
+ */
+ {0x0 ,0x02 ,0x02 },
+ {0x53 ,0x02 ,0x02 },
+ {0x53 ,0x01 ,0x01 },
+ {0x54 ,0x01 ,0x01 },
+ {0x54 ,0x02 ,0x02 },
+
+ /*
+ * UMC 8891A Pentium chipset:
+ * Why did you think UMC was cheaper ??
+ */
+ {0x50 ,0x10 ,0x00 },
+ {0x51 ,0x40 ,0x40 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+
+ /*
+ * UMC UM8881F
+ * This is a dummy entry for my tests.
+ * I have this chipset and no docs....
+ */
+ {0x0 ,0x1 ,0x1 },
+ {0x0 ,0x2 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 },
+ {0x0 ,0x0 ,0x0 }
+};
+
+#endif /* CONFIG_PCI_OPTIMIZE */
+
+
+/*
+ * device_info[] is sorted so we can use binary search
+ */
+struct pci_dev_info *pci_lookup_dev(unsigned int vendor, unsigned int dev)
+{
+ int min = 0,
+ max = sizeof(dev_info)/sizeof(dev_info[0]) - 1;
+
+ for ( ; ; )
+ {
+ int i = (min + max) >> 1;
+ long order;
+
+ order = dev_info[i].vendor - (long) vendor;
+ if (!order)
+ order = dev_info[i].device - (long) dev;
+
+ if (order < 0)
+ {
+ min = i + 1;
+ if ( min > max )
+ return 0;
+ continue;
+ }
+
+ if (order > 0)
+ {
+ max = i - 1;
+ if ( min > max )
+ return 0;
+ continue;
+ }
+
+ return & dev_info[ i ];
+ }
+}
+
+const char *pci_strclass (unsigned int class)
+{
+ switch (class >> 8) {
+ case PCI_CLASS_NOT_DEFINED: return "Non-VGA device";
+ case PCI_CLASS_NOT_DEFINED_VGA: return "VGA compatible device";
+
+ case PCI_CLASS_STORAGE_SCSI: return "SCSI storage controller";
+ case PCI_CLASS_STORAGE_IDE: return "IDE interface";
+ case PCI_CLASS_STORAGE_FLOPPY: return "Floppy disk controller";
+ case PCI_CLASS_STORAGE_IPI: return "IPI bus controller";
+ case PCI_CLASS_STORAGE_RAID: return "RAID bus controller";
+ case PCI_CLASS_STORAGE_OTHER: return "Unknown mass storage controller";
+
+ case PCI_CLASS_NETWORK_ETHERNET: return "Ethernet controller";
+ case PCI_CLASS_NETWORK_TOKEN_RING: return "Token ring network controller";
+ case PCI_CLASS_NETWORK_FDDI: return "FDDI network controller";
+ case PCI_CLASS_NETWORK_ATM: return "ATM network controller";
+ case PCI_CLASS_NETWORK_OTHER: return "Network controller";
+
+ case PCI_CLASS_DISPLAY_VGA: return "VGA compatible controller";
+ case PCI_CLASS_DISPLAY_XGA: return "XGA compatible controller";
+ case PCI_CLASS_DISPLAY_OTHER: return "Display controller";
+
+ case PCI_CLASS_MULTIMEDIA_VIDEO: return "Multimedia video controller";
+ case PCI_CLASS_MULTIMEDIA_AUDIO: return "Multimedia audio controller";
+ case PCI_CLASS_MULTIMEDIA_OTHER: return "Multimedia controller";
+
+ case PCI_CLASS_MEMORY_RAM: return "RAM memory";
+ case PCI_CLASS_MEMORY_FLASH: return "FLASH memory";
+ case PCI_CLASS_MEMORY_OTHER: return "Memory";
+
+ case PCI_CLASS_BRIDGE_HOST: return "Host bridge";
+ case PCI_CLASS_BRIDGE_ISA: return "ISA bridge";
+ case PCI_CLASS_BRIDGE_EISA: return "EISA bridge";
+ case PCI_CLASS_BRIDGE_MC: return "MicroChannel bridge";
+ case PCI_CLASS_BRIDGE_PCI: return "PCI bridge";
+ case PCI_CLASS_BRIDGE_PCMCIA: return "PCMCIA bridge";
+ case PCI_CLASS_BRIDGE_NUBUS: return "NuBus bridge";
+ case PCI_CLASS_BRIDGE_CARDBUS: return "CardBus bridge";
+ case PCI_CLASS_BRIDGE_OTHER: return "Bridge";
+
+ case PCI_CLASS_COMMUNICATION_SERIAL: return "Serial controller";
+ case PCI_CLASS_COMMUNICATION_PARALLEL: return "Parallel controller";
+ case PCI_CLASS_COMMUNICATION_OTHER: return "Communication controller";
+
+ case PCI_CLASS_SYSTEM_PIC: return "PIC";
+ case PCI_CLASS_SYSTEM_DMA: return "DMA controller";
+ case PCI_CLASS_SYSTEM_TIMER: return "Timer";
+ case PCI_CLASS_SYSTEM_RTC: return "RTC";
+ case PCI_CLASS_SYSTEM_OTHER: return "System peripheral";
+
+ case PCI_CLASS_INPUT_KEYBOARD: return "Keyboard controller";
+ case PCI_CLASS_INPUT_PEN: return "Digitizer Pen";
+ case PCI_CLASS_INPUT_MOUSE: return "Mouse controller";
+ case PCI_CLASS_INPUT_OTHER: return "Input device controller";
+
+ case PCI_CLASS_DOCKING_GENERIC: return "Generic Docking Station";
+ case PCI_CLASS_DOCKING_OTHER: return "Docking Station";
+
+ case PCI_CLASS_PROCESSOR_386: return "386";
+ case PCI_CLASS_PROCESSOR_486: return "486";
+ case PCI_CLASS_PROCESSOR_PENTIUM: return "Pentium";
+ case PCI_CLASS_PROCESSOR_ALPHA: return "Alpha";
+ case PCI_CLASS_PROCESSOR_POWERPC: return "Power PC";
+ case PCI_CLASS_PROCESSOR_CO: return "Co-processor";
+
+ case PCI_CLASS_SERIAL_FIREWIRE: return "FireWire (IEEE 1394)";
+ case PCI_CLASS_SERIAL_ACCESS: return "ACCESS Bus";
+ case PCI_CLASS_SERIAL_SSA: return "SSA";
+ case PCI_CLASS_SERIAL_FIBER: return "Fiber Channel";
+
+ default: return "Unknown class";
+ }
+}
+
+
+const char *pci_strvendor(unsigned int vendor)
+{
+ switch (vendor) {
+ case PCI_VENDOR_ID_COMPAQ: return "Compaq";
+ case PCI_VENDOR_ID_NCR: return "NCR";
+ case PCI_VENDOR_ID_ATI: return "ATI";
+ case PCI_VENDOR_ID_VLSI: return "VLSI";
+ case PCI_VENDOR_ID_ADL: return "Advance Logic";
+ case PCI_VENDOR_ID_NS: return "NS";
+ case PCI_VENDOR_ID_TSENG: return "Tseng'Lab";
+ case PCI_VENDOR_ID_WEITEK: return "Weitek";
+ case PCI_VENDOR_ID_DEC: return "DEC";
+ case PCI_VENDOR_ID_CIRRUS: return "Cirrus Logic";
+ case PCI_VENDOR_ID_IBM: return "IBM";
+ case PCI_VENDOR_ID_WD: return "Western Digital";
+ case PCI_VENDOR_ID_AMD: return "AMD";
+ case PCI_VENDOR_ID_TRIDENT: return "Trident";
+ case PCI_VENDOR_ID_AI: return "Acer Incorporated";
+ case PCI_VENDOR_ID_MATROX: return "Matrox";
+ case PCI_VENDOR_ID_CT: return "Chips & Technologies";
+ case PCI_VENDOR_ID_FD: return "Future Domain";
+ case PCI_VENDOR_ID_SI: return "Silicon Integrated Systems";
+ case PCI_VENDOR_ID_HP: return "Hewlett Packard";
+ case PCI_VENDOR_ID_PCTECH: return "PCTECH";
+ case PCI_VENDOR_ID_DPT: return "DPT";
+ case PCI_VENDOR_ID_OPTI: return "OPTI";
+ case PCI_VENDOR_ID_SGS: return "SGS Thomson";
+ case PCI_VENDOR_ID_BUSLOGIC: return "BusLogic";
+ case PCI_VENDOR_ID_OAK: return "OAK";
+ case PCI_VENDOR_ID_PROMISE: return "Promise Technology";
+ case PCI_VENDOR_ID_N9: return "Number Nine";
+ case PCI_VENDOR_ID_UMC: return "UMC";
+ case PCI_VENDOR_ID_X: return "X TECHNOLOGY";
+ case PCI_VENDOR_ID_NEXGEN: return "Nexgen";
+ case PCI_VENDOR_ID_QLOGIC: return "Q Logic";
+ case PCI_VENDOR_ID_LEADTEK: return "Leadtek Research";
+ case PCI_VENDOR_ID_CONTAQ: return "Contaq";
+ case PCI_VENDOR_ID_FOREX: return "Forex";
+ case PCI_VENDOR_ID_OLICOM: return "Olicom";
+ case PCI_VENDOR_ID_CMD: return "CMD";
+ case PCI_VENDOR_ID_VISION: return "Vision";
+ case PCI_VENDOR_ID_SIERRA: return "Sierra";
+ case PCI_VENDOR_ID_ACC: return "ACC MICROELECTRONICS";
+ case PCI_VENDOR_ID_WINBOND: return "Winbond";
+ case PCI_VENDOR_ID_3COM: return "3Com";
+ case PCI_VENDOR_ID_AL: return "Acer Labs";
+ case PCI_VENDOR_ID_ASP: return "Advanced System Products";
+ case PCI_VENDOR_ID_IMS: return "IMS";
+ case PCI_VENDOR_ID_TEKRAM2: return "Tekram";
+ case PCI_VENDOR_ID_AMCC: return "AMCC";
+ case PCI_VENDOR_ID_INTERG: return "Intergraphics";
+ case PCI_VENDOR_ID_REALTEK: return "Realtek";
+ case PCI_VENDOR_ID_INIT: return "Initio Corp";
+ case PCI_VENDOR_ID_VIA: return "VIA Technologies";
+ case PCI_VENDOR_ID_VORTEX: return "VORTEX";
+ case PCI_VENDOR_ID_EF: return "Efficient Networks";
+ case PCI_VENDOR_ID_FORE: return "Fore Systems";
+ case PCI_VENDOR_ID_IMAGINGTECH: return "Imaging Technology";
+ case PCI_VENDOR_ID_PLX: return "PLX";
+ case PCI_VENDOR_ID_ALLIANCE: return "Alliance";
+ case PCI_VENDOR_ID_MUTECH: return "Mutech";
+ case PCI_VENDOR_ID_ZEITNET: return "ZeitNet";
+ case PCI_VENDOR_ID_SPECIALIX: return "Specialix";
+ case PCI_VENDOR_ID_RP: return "Comtrol";
+ case PCI_VENDOR_ID_CYCLADES: return "Cyclades";
+ case PCI_VENDOR_ID_SYMPHONY: return "Symphony";
+ case PCI_VENDOR_ID_TEKRAM: return "Tekram";
+ case PCI_VENDOR_ID_AVANCE: return "Avance";
+ case PCI_VENDOR_ID_S3: return "S3 Inc.";
+ case PCI_VENDOR_ID_INTEL: return "Intel";
+ case PCI_VENDOR_ID_ADAPTEC: return "Adaptec";
+ case PCI_VENDOR_ID_ATRONICS: return "Atronics";
+ case PCI_VENDOR_ID_HER: return "Hercules";
+ default: return "Unknown vendor";
+ }
+}
+
+
+const char *pci_strdev(unsigned int vendor, unsigned int device)
+{
+ struct pci_dev_info *info;
+
+ info = pci_lookup_dev(vendor, device);
+ return info ? info->name : "Unknown device";
+}
+
+
+
+/*
+ * Turn on/off PCI bridge optimization. This should allow benchmarking.
+ */
+static void burst_bridge(unsigned char bus, unsigned char devfn,
+ unsigned char pos, int turn_on)
+{
+#ifdef CONFIG_PCI_OPTIMIZE
+ struct bridge_mapping_type *bmap;
+ unsigned char val;
+ int i;
+
+ pos *= NUM_OPTIMIZATIONS;
+ printk("PCI bridge optimization.\n");
+ for (i = 0; i < NUM_OPTIMIZATIONS; i++) {
+ printk(" %s: ", bridge_optimization[i].type);
+ bmap = &bridge_mapping[pos + i];
+ if (!bmap->addr) {
+ printk("Not supported.");
+ } else {
+ pcibios_read_config_byte(bus, devfn, bmap->addr, &val);
+ if ((val & bmap->mask) == bmap->value) {
+ printk("%s.", bridge_optimization[i].on);
+ if (!turn_on) {
+ pcibios_write_config_byte(bus, devfn,
+ bmap->addr,
+ (val | bmap->mask)
+ - bmap->value);
+ printk("Changed! Now %s.", bridge_optimization[i].off);
+ }
+ } else {
+ printk("%s.", bridge_optimization[i].off);
+ if (turn_on) {
+ pcibios_write_config_byte(bus, devfn,
+ bmap->addr,
+ (val & (0xff - bmap->mask))
+ + bmap->value);
+ printk("Changed! Now %s.", bridge_optimization[i].on);
+ }
+ }
+ }
+ printk("\n");
+ }
+#endif /* CONFIG_PCI_OPTIMIZE */
+}
+
+
+/*
+ * Convert some of the configuration space registers of the device at
+ * address (bus,devfn) into a string (possibly several lines each).
+ * The configuration string is stored starting at buf[len]. If the
+ * string would exceed the size of the buffer (SIZE), 0 is returned.
+ */
+static int sprint_dev_config(struct pci_dev *dev, char *buf, int size)
+{
+ unsigned long base;
+ unsigned int l, class_rev, bus, devfn;
+ unsigned short vendor, device, status;
+ unsigned char bist, latency, min_gnt, max_lat;
+ int reg, len = 0;
+ const char *str;
+
+ bus = dev->bus->number;
+ devfn = dev->devfn;
+
+ pcibios_read_config_dword(bus, devfn, PCI_CLASS_REVISION, &class_rev);
+ pcibios_read_config_word (bus, devfn, PCI_VENDOR_ID, &vendor);
+ pcibios_read_config_word (bus, devfn, PCI_DEVICE_ID, &device);
+ pcibios_read_config_word (bus, devfn, PCI_STATUS, &status);
+ pcibios_read_config_byte (bus, devfn, PCI_BIST, &bist);
+ pcibios_read_config_byte (bus, devfn, PCI_LATENCY_TIMER, &latency);
+ pcibios_read_config_byte (bus, devfn, PCI_MIN_GNT, &min_gnt);
+ pcibios_read_config_byte (bus, devfn, PCI_MAX_LAT, &max_lat);
+ if (len + 80 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, " Bus %2d, device %3d, function %2d:\n",
+ bus, PCI_SLOT(devfn), PCI_FUNC(devfn));
+
+ if (len + 80 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, " %s: %s %s (rev %d).\n ",
+ pci_strclass(class_rev >> 8), pci_strvendor(vendor),
+ pci_strdev(vendor, device), class_rev & 0xff);
+
+ if (!pci_lookup_dev(vendor, device)) {
+ len += sprintf(buf + len,
+ "Vendor id=%x. Device id=%x.\n ",
+ vendor, device);
+ }
+
+ str = 0; /* to keep gcc shut... */
+ switch (status & PCI_STATUS_DEVSEL_MASK) {
+ case PCI_STATUS_DEVSEL_FAST: str = "Fast devsel. "; break;
+ case PCI_STATUS_DEVSEL_MEDIUM: str = "Medium devsel. "; break;
+ case PCI_STATUS_DEVSEL_SLOW: str = "Slow devsel. "; break;
+ }
+ if (len + strlen(str) > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, str);
+
+ if (status & PCI_STATUS_FAST_BACK) {
+# define fast_b2b_capable "Fast back-to-back capable. "
+ if (len + strlen(fast_b2b_capable) > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, fast_b2b_capable);
+# undef fast_b2b_capable
+ }
+
+ if (bist & PCI_BIST_CAPABLE) {
+# define BIST_capable "BIST capable. "
+ if (len + strlen(BIST_capable) > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, BIST_capable);
+# undef BIST_capable
+ }
+
+ if (dev->irq) {
+ if (len + 40 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, "IRQ %d. ", dev->irq);
+ }
+
+ if (dev->master) {
+ if (len + 80 > size) {
+ return -1;
+ }
+ len += sprintf(buf + len, "Master Capable. ");
+ if (latency)
+ len += sprintf(buf + len, "Latency=%d. ", latency);
+ else
+ len += sprintf(buf + len, "No bursts. ");
+ if (min_gnt)
+ len += sprintf(buf + len, "Min Gnt=%d.", min_gnt);
+ if (max_lat)
+ len += sprintf(buf + len, "Max Lat=%d.", max_lat);
+ }
+
+ for (reg = PCI_BASE_ADDRESS_0; reg <= PCI_BASE_ADDRESS_5; reg += 4) {
+ if (len + 40 > size) {
+ return -1;
+ }
+ pcibios_read_config_dword(bus, devfn, reg, &l);
+ base = l;
+ if (!base) {
+ continue;
+ }
+
+ if (base & PCI_BASE_ADDRESS_SPACE_IO) {
+ len += sprintf(buf + len,
+ "\n I/O at 0x%lx.",
+ base & PCI_BASE_ADDRESS_IO_MASK);
+ } else {
+ const char *pref, *type = "unknown";
+
+ if (base & PCI_BASE_ADDRESS_MEM_PREFETCH) {
+ pref = "P";
+ } else {
+ pref = "Non-p";
+ }
+ switch (base & PCI_BASE_ADDRESS_MEM_TYPE_MASK) {
+ case PCI_BASE_ADDRESS_MEM_TYPE_32:
+ type = "32 bit"; break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_1M:
+ type = "20 bit"; break;
+ case PCI_BASE_ADDRESS_MEM_TYPE_64:
+ type = "64 bit";
+ /* read top 32 bit address of base addr: */
+ reg += 4;
+ pcibios_read_config_dword(bus, devfn, reg, &l);
+ base |= ((u64) l) << 32;
+ break;
+ }
+ len += sprintf(buf + len,
+ "\n %srefetchable %s memory at "
+ "0x%lx.", pref, type,
+ base & PCI_BASE_ADDRESS_MEM_MASK);
+ }
+ }
+
+ len += sprintf(buf + len, "\n");
+ return len;
+}
+
+
+/*
+ * Return list of PCI devices as a character string for /proc/pci.
+ * BUF is a buffer that is PAGE_SIZE bytes long.
+ */
+int get_pci_list(char *buf)
+{
+ int nprinted, len, size;
+ struct pci_dev *dev;
+# define MSG "\nwarning: page-size limit reached!\n"
+
+ /* reserve same for truncation warning message: */
+ size = PAGE_SIZE - (strlen(MSG) + 1);
+ len = sprintf(buf, "PCI devices found:\n");
+
+ for (dev = pci_devices; dev; dev = dev->next) {
+ nprinted = sprint_dev_config(dev, buf + len, size - len);
+ if (nprinted < 0) {
+ return len + sprintf(buf + len, MSG);
+ }
+ len += nprinted;
+ }
+ return len;
+}
+
+
+/*
+ * pci_malloc() returns initialized memory of size SIZE. Can be
+ * used only while pci_init() is active.
+ */
+static void *pci_malloc(long size, unsigned long *mem_startp)
+{
+ void *mem;
+
+#ifdef DEBUG
+ printk("...pci_malloc(size=%ld,mem=%p)", size, *mem_startp);
+#endif
+ mem = (void*) *mem_startp;
+ *mem_startp += (size + sizeof(void*) - 1) & ~(sizeof(void*) - 1);
+ memset(mem, 0, size);
+ return mem;
+}
+
+
+static unsigned int scan_bus(struct pci_bus *bus, unsigned long *mem_startp)
+{
+ unsigned int devfn, l, max;
+ unsigned char cmd, tmp, hdr_type = 0;
+ struct pci_dev_info *info;
+ struct pci_dev *dev;
+ struct pci_bus *child;
+
+#ifdef DEBUG
+ printk("...scan_bus(busno=%d,mem=%p)\n", bus->number, *mem_startp);
+#endif
+
+ max = bus->secondary;
+ for (devfn = 0; devfn < 0xff; ++devfn) {
+ if (PCI_FUNC(devfn) == 0) {
+ pcibios_read_config_byte(bus->number, devfn,
+ PCI_HEADER_TYPE, &hdr_type);
+ } else if (!(hdr_type & 0x80)) {
+ /* not a multi-function device */
+ continue;
+ }
+
+ pcibios_read_config_dword(bus->number, devfn, PCI_VENDOR_ID,
+ &l);
+ /* some broken boards return 0 if a slot is empty: */
+ if (l == 0xffffffff || l == 0x00000000) {
+ hdr_type = 0;
+ continue;
+ }
+
+ dev = pci_malloc(sizeof(*dev), mem_startp);
+ dev->bus = bus;
+ /*
+ * Put it into the simple chain of devices on this
+ * bus. It is used to find devices once everything is
+ * set up.
+ */
+ dev->next = pci_devices;
+ pci_devices = dev;
+
+ dev->devfn = devfn;
+ dev->vendor = l & 0xffff;
+ dev->device = (l >> 16) & 0xffff;
+
+ /*
+ * Check to see if we know about this device and report
+ * a message at boot time. This is the only way to
+ * learn about new hardware...
+ */
+ info = pci_lookup_dev(dev->vendor, dev->device);
+ if (!info) {
+ printk("Warning : Unknown PCI device (%x:%x). Please read include/linux/pci.h \n",
+ dev->vendor, dev->device);
+ } else {
+ /* Some BIOS' are lazy. Let's do their job: */
+ if (info->bridge_type != 0xff) {
+ burst_bridge(bus->number, devfn,
+ info->bridge_type, 1);
+ }
+ }
+
+ /* non-destructively determine if device can be a master: */
+ pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND,
+ &cmd);
+ pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND,
+ cmd | PCI_COMMAND_MASTER);
+ pcibios_read_config_byte(bus->number, devfn, PCI_COMMAND,
+ &tmp);
+ dev->master = ((tmp & PCI_COMMAND_MASTER) != 0);
+ pcibios_write_config_byte(bus->number, devfn, PCI_COMMAND,
+ cmd);
+
+ /* read irq level (may be changed during pcibios_fixup()): */
+ pcibios_read_config_byte(bus->number, devfn,
+ PCI_INTERRUPT_LINE, &dev->irq);
+
+ /* check to see if this device is a PCI-PCI bridge: */
+ pcibios_read_config_dword(bus->number, devfn,
+ PCI_CLASS_REVISION, &l);
+ l = l >> 8; /* upper 3 bytes */
+ dev->class = l;
+ /*
+ * Now insert it into the list of devices held
+ * by the parent bus.
+ */
+ dev->sibling = bus->devices;
+ bus->devices = dev;
+
+ if (dev->class >> 8 == PCI_CLASS_BRIDGE_PCI) {
+ unsigned int buses;
+ unsigned short cr;
+
+ /*
+ * Insert it into the tree of buses.
+ */
+ child = pci_malloc(sizeof(*child), mem_startp);
+ child->next = bus->children;
+ bus->children = child;
+ child->self = dev;
+ child->parent = bus;
+
+ /*
+ * Set up the primary, secondary and subordinate
+ * bus numbers.
+ */
+ child->number = child->secondary = ++max;
+ child->primary = bus->secondary;
+ child->subordinate = 0xff;
+ /*
+ * Clear all status bits and turn off memory,
+ * I/O and master enables.
+ */
+ pcibios_read_config_word(bus->number, devfn,
+ PCI_COMMAND, &cr);
+ pcibios_write_config_word(bus->number, devfn,
+ PCI_COMMAND, 0x0000);
+ pcibios_write_config_word(bus->number, devfn,
+ PCI_STATUS, 0xffff);
+ /*
+ * Configure the bus numbers for this bridge:
+ */
+ pcibios_read_config_dword(bus->number, devfn, 0x18,
+ &buses);
+ buses &= 0xff000000;
+ buses |= (((unsigned int)(child->primary) << 0) |
+ ((unsigned int)(child->secondary) << 8) |
+ ((unsigned int)(child->subordinate) << 16));
+ pcibios_write_config_dword(bus->number, devfn, 0x18,
+ buses);
+ /*
+ * Now we can scan all subordinate buses:
+ */
+ max = scan_bus(child, mem_startp);
+ /*
+ * Set the subordinate bus number to its real
+ * value:
+ */
+ child->subordinate = max;
+ buses = (buses & 0xff00ffff)
+ | ((unsigned int)(child->subordinate) << 16);
+ pcibios_write_config_dword(bus->number, devfn, 0x18,
+ buses);
+ pcibios_write_config_word(bus->number, devfn,
+ PCI_COMMAND, cr);
+ }
+ }
+ /*
+ * We've scanned the bus and so we know all about what's on
+ * the other side of any bridges that may be on this bus plus
+ * any devices.
+ *
+ * Return how far we've got finding sub-buses.
+ */
+ return max;
+}
+
+
+unsigned long pci_init (unsigned long mem_start, unsigned long mem_end)
+{
+ mem_start = pcibios_init(mem_start, mem_end);
+
+ if (!pcibios_present()) {
+ printk("pci_init: no BIOS32 detected\n");
+ return mem_start;
+ }
+
+ printk("Probing PCI hardware.\n");
+
+ memset(&pci_root, 0, sizeof(pci_root));
+ pci_root.subordinate = scan_bus(&pci_root, &mem_start);
+
+ /* give BIOS a chance to apply platform specific fixes: */
+ mem_start = pcibios_fixup(mem_start, mem_end);
+
+#ifdef DEBUG
+ {
+ int len = get_pci_list((char*)mem_start);
+ if (len) {
+ ((char *) mem_start)[len] = '\0';
+ printk("%s\n", (char *) mem_start);
+ }
+ }
+#endif
+ return mem_start;
+}
diff --git a/i386/i386at/gpl/linux/scsi/53c7,8xx.c b/i386/i386at/gpl/linux/scsi/53c7,8xx.c
new file mode 100644
index 00000000..74350b08
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/53c7,8xx.c
@@ -0,0 +1,6381 @@
+/*
+ * PERM_OPTIONS are driver options which will be enabled for all NCR boards
+ * in the system at driver initialization time.
+ *
+ * Don't THINK about touching these in PERM_OPTIONS :
+ * OPTION_IO_MAPPED
+ * Memory mapped IO does not work under i86 Linux.
+ *
+ * OPTION_DEBUG_TEST1
+ * Test 1 does bus mastering and interrupt tests, which will help weed
+ * out brain damaged main boards.
+ *
+ * These are development kernel changes. Code for them included in this
+ * driver release may or may not work. If you turn them on, you should be
+ * running the latest copy of the development sources from
+ *
+ * ftp://tsx-11.mit.edu/pub/linux/ALPHA/scsi/53c7,8xx
+ *
+ * and be subscribed to the ncr53c810@colorado.edu mailing list. To
+ * subscribe, send mail to majordomo@colorado.edu with
+ *
+ * subscribe ncr53c810
+ *
+ * in the text.
+ *
+ *
+ * OPTION_NOASYNC
+ * Don't negotiate for asynchronous transfers on the first command
+ * when OPTION_ALWAYS_SYNCHRONOUS is set. Useful for dain bramaged
+ * devices which do something bad rather than sending a MESSAGE
+ * REJECT back to us like they should if they can't cope.
+ *
+ * OPTION_SYNCHRONOUS
+ * Enable support for synchronous transfers. Target negotiated
+ * synchronous transfers will be responded to. To initiate
+ * a synchronous transfer request, call
+ *
+ * request_synchronous (hostno, target)
+ *
+ * from within KGDB.
+ *
+ * OPTION_ALWAYS_SYNCHRONOUS
+ * Negotiate for synchronous transfers with every target after
+ * driver initialization or a SCSI bus reset. This is a bit dangerous,
+ * since there are some dain bramaged SCSI devices which will accept
+ * SDTR messages but keep talking asynchronously.
+ *
+ * OPTION_DISCONNECT
+ * Enable support for disconnect/reconnect. To change the
+ * default setting on a given host adapter, call
+ *
+ * request_disconnect (hostno, allow)
+ *
+ * where allow is non-zero to allow, 0 to disallow.
+ *
+ * If you really want to run 10MHz FAST SCSI-II transfers, you should
+ * know that the NCR driver currently ignores parity information. Most
+ * systems do 5MHz SCSI fine. I've seen a lot that have problems faster
+ * than 8MHz. To play it safe, we only request 5MHz transfers.
+ *
+ * If you'd rather get 10MHz transfers, edit sdtr_message and change
+ * the fourth byte from 50 to 25.
+ */
+
+#define PERM_OPTIONS (OPTION_IO_MAPPED|OPTION_DEBUG_TEST1|OPTION_DISCONNECT|\
+ OPTION_SYNCHRONOUS)
+
+/*
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@PoohSticks.ORG
+ * +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ *
+ * For more information, please consult
+ *
+ * NCR53C810
+ * SCSI I/O Processor
+ * Programmer's Guide
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810/53C820
+ * PCI-SCSI I/O Processor Design In Guide
+ *
+ * For literature on Symbios Logic Inc. formerly NCR, SCSI,
+ * and Communication products please call (800) 334-5454 or
+ * (719) 536-3300.
+ *
+ * PCI BIOS Specification Revision
+ * PCI Local Bus Specification
+ * PCI System Design Guide
+ *
+ * PCI Special Interest Group
+ * M/S HF3-15A
+ * 5200 N.E. Elam Young Parkway
+ * Hillsboro, Oregon 97124-6497
+ * +1 (503) 696-2000
+ * +1 (800) 433-5177
+ */
+
+/*
+ * Design issues :
+ * The cumulative latency needed to propagate a read/write request
+ * through the file system, buffer cache, driver stacks, SCSI host, and
+ * SCSI device is ultimately the limiting factor in throughput once we
+ * have a sufficiently fast host adapter.
+ *
+ * So, to maximize performance we want to keep the ratio of latency to data
+ * transfer time to a minimum by
+ * 1. Minimizing the total number of commands sent (typical command latency
+ * including drive and bus mastering host overhead is as high as 4.5ms)
+ * to transfer a given amount of data.
+ *
+ * This is accomplished by placing no arbitrary limit on the number
+ * of scatter/gather buffers supported, since we can transfer 1K
+ * per scatter/gather buffer without Eric's cluster patches,
+ * 4K with.
+ *
+ * 2. Minimizing the number of fatal interrupts serviced, since
+ * fatal interrupts halt the SCSI I/O processor. Basically,
+ * this means offloading the practical maximum amount of processing
+ * to the SCSI chip.
+ *
+ * On the NCR53c810/820/720, this is accomplished by using
+ * interrupt-on-the-fly signals when commands complete,
+ * and only handling fatal errors and SDTR / WDTR messages
+ * in the host code.
+ *
+ * On the NCR53c710, interrupts are generated as on the NCR53c8x0,
+ * only the lack of a interrupt-on-the-fly facility complicates
+ * things. Also, SCSI ID registers and commands are
+ * bit fielded rather than binary encoded.
+ *
+ * On the NCR53c700 and NCR53c700-66, operations that are done via
+ * indirect, table mode on the more advanced chips must be
+ * replaced by calls through a jump table which
+ * acts as a surrogate for the DSA. Unfortunately, this
+ * will mean that we must service an interrupt for each
+ * disconnect/reconnect.
+ *
+ * 3. Eliminating latency by pipelining operations at the different levels.
+ *
+ * This driver allows a configurable number of commands to be enqueued
+ * for each target/lun combination (experimentally, I have discovered
+ * that two seems to work best) and will ultimately allow for
+ * SCSI-II tagged queuing.
+ *
+ *
+ * Architecture :
+ * This driver is built around a Linux queue of commands waiting to
+ * be executed, and a shared Linux/NCR array of commands to start. Commands
+ * are transfered to the array by the run_process_issue_queue() function
+ * which is called whenever a command completes.
+ *
+ * As commands are completed, the interrupt routine is triggered,
+ * looks for commands in the linked list of completed commands with
+ * valid status, removes these commands from a list of running commands,
+ * calls the done routine, and flags their target/luns as not busy.
+ *
+ * Due to limitations in the intelligence of the NCR chips, certain
+ * concessions are made. In many cases, it is easier to dynamically
+ * generate/fix-up code rather than calculate on the NCR at run time.
+ * So, code is generated or fixed up for
+ *
+ * - Handling data transfers, using a variable number of MOVE instructions
+ * interspersed with CALL MSG_IN, WHEN MSGIN instructions.
+ *
+ * The DATAIN and DATAOUT routines are separate, so that an incorrect
+ * direction can be trapped, and space isn't wasted.
+ *
+ * It may turn out that we're better off using some sort
+ * of table indirect instruction in a loop with a variable
+ * sized table on the NCR53c710 and newer chips.
+ *
+ * - Checking for reselection (NCR53c710 and better)
+ *
+ * - Handling the details of SCSI context switches (NCR53c710 and better),
+ * such as reprogramming appropriate synchronous parameters,
+ * removing the dsa structure from the NCR's queue of outstanding
+ * commands, etc.
+ *
+ */
+
+/*
+ * Accommodate differences between stock 1.2.x and 1.3.x asm-i386/types.h
+ * so lusers can drop in 53c7,8xx.* and get something which compiles
+ * without warnings.
+ */
+
+#if !defined(LINUX_1_2) && !defined(LINUX_1_3)
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > 65536 + 3 * 256
+#define LINUX_1_3
+#else
+#define LINUX_1_2
+#endif
+#endif
+
+#ifdef LINUX_1_2
+#define u32 bogus_u32
+#define s32 bogus_s32
+#include <asm/types.h>
+#undef u32
+#undef s32
+typedef __signed__ int s32;
+typedef unsigned int u32;
+#endif /* def LINUX_1_2 */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <linux/mm.h>
+#include <linux/ioport.h>
+#include <linux/time.h>
+#ifdef LINUX_1_2
+#include "../block/blk.h"
+#else
+#include <linux/blk.h>
+#endif
+#undef current
+
+#include "scsi.h"
+#include "hosts.h"
+#include "53c7,8xx.h"
+#include "constants.h"
+#include "sd.h"
+#include <linux/stat.h>
+#include <linux/stddef.h>
+
+#ifndef LINUX_1_2
+struct proc_dir_entry proc_scsi_ncr53c7xx = {
+ PROC_SCSI_NCR53C7xx, 9, "ncr53c7xx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+#endif
+
+static int check_address (unsigned long addr, int size);
+static void dump_events (struct Scsi_Host *host, int count);
+static Scsi_Cmnd * return_outstanding_commands (struct Scsi_Host *host,
+ int free, int issue);
+static void hard_reset (struct Scsi_Host *host);
+static void ncr_scsi_reset (struct Scsi_Host *host);
+static void print_lots (struct Scsi_Host *host);
+static void set_synchronous (struct Scsi_Host *host, int target, int sxfer,
+ int scntl3, int now_connected);
+static int datapath_residual (struct Scsi_Host *host);
+static const char * sbcl_to_phase (int sbcl);
+static void print_progress (Scsi_Cmnd *cmd);
+static void print_queues (struct Scsi_Host *host);
+static void process_issue_queue (unsigned long flags);
+static int shutdown (struct Scsi_Host *host);
+static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int result);
+static int disable (struct Scsi_Host *host);
+static int NCR53c8xx_run_tests (struct Scsi_Host *host);
+static int NCR53c8xx_script_len;
+static int NCR53c8xx_dsa_len;
+static void NCR53c7x0_intr(int irq, struct pt_regs * regs);
+static int ncr_halt (struct Scsi_Host *host);
+static void intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd
+ *cmd);
+static void intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+static void print_dsa (struct Scsi_Host *host, u32 *dsa,
+ const char *prefix);
+static int print_insn (struct Scsi_Host *host, const u32 *insn,
+ const char *prefix, int kernel);
+
+static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd);
+static void NCR53c8x0_init_fixup (struct Scsi_Host *host);
+static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd);
+static void NCR53c8x0_soft_reset (struct Scsi_Host *host);
+
+/* INSMOD variables */
+static long long perm_options = PERM_OPTIONS;
+/* 14 = .5s; 15 is max; decreasing divides by two. */
+static int selection_timeout = 14;
+/* Size of event list (per host adapter) */
+static int track_events = 0;
+
+static struct Scsi_Host *first_host = NULL; /* Head of list of NCR boards */
+static Scsi_Host_Template *the_template = NULL;
+
+/*
+ * KNOWN BUGS :
+ * - There is some sort of conflict when the PPP driver is compiled with
+ * support for 16 channels?
+ *
+ * - On systems which predate the 1.3.x initialization order change,
+ * the NCR driver will cause Cannot get free page messages to appear.
+ * These are harmless, but I don't know of an easy way to avoid them.
+ *
+ * - With OPTION_DISCONNECT, on two systems under unknown circumstances,
+ * we get a PHASE MISMATCH with DSA set to zero (suggests that we
+ * are occurring somewhere in the reselection code) where
+ * DSP=some value DCMD|DBC=same value.
+ *
+ * Closer inspection suggests that we may be trying to execute
+ * some portion of the DSA?
+ * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
+ * scsi0 : handling residual transfer (+ 0 bytes from DMA FIFO)
+ * scsi0 : no current command : unexpected phase MSGIN.
+ * DSP=0x1c46cc, DCMD|DBC=0x1c46ac, DSA=0x0
+ * DSPS=0x0, TEMP=0x1c3e70, DMODE=0x80
+ * scsi0 : DSP->
+ * 001c46cc : 0x001c46cc 0x00000000
+ * 001c46d4 : 0x001c5ea0 0x000011f8
+ *
+ * Changed the print code in the phase_mismatch handler so
+ * that we call print_lots to try and diagnose this.
+ *
+ */
+
+/*
+ * Possible future direction of architecture for max performance :
+ *
+ * We're using a single start array for the NCR chip. This is
+ * sub-optimal, because we cannot add a command which would conflict with
+ * an executing command to this start queue, and therefore must insert the
+ * next command for a given I/T/L combination after the first has completed;
+ * incurring our interrupt latency between SCSI commands.
+ *
+ * To allow furthur pipelining of the NCR and host CPU operation, we want
+ * to set things up so that immediately on termination of a command destined
+ * for a given LUN, we get that LUN busy again.
+ *
+ * To do this, we need to add a 32 bit pointer to which is jumped to
+ * on completion of a command. If no new command is available, this
+ * would point to the usual DSA issue queue select routine.
+ *
+ * If one were, it would point to a per-NCR53c7x0_cmd select routine
+ * which starts execution immediately, inserting the command at the head
+ * of the start queue if the NCR chip is selected or reselected.
+ *
+ * We would chanage so that we keep a list of outstanding commands
+ * for each unit, rather than a single running_list. We'd insert
+ * a new command into the right running list; if the NCR didn't
+ * have something running for that yet, we'd put it in the
+ * start queue as well. Some magic needs to happen to handle the
+ * race condition between the first command terminating before the
+ * new one is written.
+ *
+ * Potential for profiling :
+ * Call do_gettimeofday(struct timeval *tv) to get 800ns resolution.
+ */
+
+
+/*
+ * TODO :
+ * 1. To support WIDE transfers, not much needs to happen. We
+ * should do CHMOVE instructions instead of MOVEs when
+ * we have scatter/gather segments of uneven length. When
+ * we do this, we need to handle the case where we disconnect
+ * between segments.
+ *
+ * 2. Currently, when Icky things happen we do a FATAL(). Instead,
+ * we want to do an integrity check on the parts of the NCR hostdata
+ * structure which were initialized at boot time; FATAL() if that
+ * fails, and otherwise try to recover. Keep track of how many
+ * times this has happened within a single SCSI command; if it
+ * gets excessive, then FATAL().
+ *
+ * 3. Parity checking is currently disabled, and a few things should
+ * happen here now that we support synchronous SCSI transfers :
+ * 1. On soft-reset, we shuld set the EPC (Enable Parity Checking)
+ * and AAP (Assert SATN/ on parity error) bits in SCNTL0.
+ *
+ * 2. We should enable the parity interrupt in the SIEN0 register.
+ *
+ * 3. intr_phase_mismatch() needs to believe that message out is
+ * allways an "acceptable" phase to have a mismatch in. If
+ * the old phase was MSG_IN, we should send a MESSAGE PARITY
+ * error. If the old phase was something else, we should send
+ * a INITIATOR_DETECTED_ERROR message. Note that this could
+ * cause a RESTORE POINTERS message; so we should handle that
+ * correctly first. Instead, we should probably do an
+ * initiator_abort.
+ *
+ * 4. MPEE bit of CTEST4 should be set so we get interrupted if
+ * we detect an error.
+ *
+ *
+ * 5. The initial code has been tested on the NCR53c810. I don't
+ * have access to NCR53c700, 700-66 (Forex boards), NCR53c710
+ * (NCR Pentium systems), NCR53c720, NCR53c820, or NCR53c825 boards to
+ * finish development on those platforms.
+ *
+ * NCR53c820/825/720 - need to add wide transfer support, including WDTR
+ * negotiation, programming of wide transfer capabilities
+ * on reselection and table indirect selection.
+ *
+ * NCR53c710 - need to add fatal interrupt or GEN code for
+ * command completion signaling. Need to modify all
+ * SDID, SCID, etc. registers, and table indirect select code
+ * since these use bit fielded (ie 1<<target) instead of
+ * binary encoded target ids. Need to accomodate
+ * different register mappings, probably scan through
+ * the SCRIPT code and change the non SFBR register operand
+ * of all MOVE instructions.
+ *
+ * NCR53c700/700-66 - need to add code to refix addresses on
+ * every nexus change, eliminate all table indirect code,
+ * very messy.
+ *
+ * 6. The NCR53c7x0 series is very popular on other platforms that
+ * could be running Linux - ie, some high performance AMIGA SCSI
+ * boards use it.
+ *
+ * So, I should include #ifdef'd code so that it is
+ * compatible with these systems.
+ *
+ * Specifically, the little Endian assumptions I made in my
+ * bit fields need to change, and if the NCR doesn't see memory
+ * the right way, we need to provide options to reverse words
+ * when the scripts are relocated.
+ *
+ * 7. Use vremap() to access memory mapped boards.
+ */
+
+/*
+ * Allow for simultaneous existence of multiple SCSI scripts so we
+ * can have a single driver binary for all of the family.
+ *
+ * - one for NCR53c700 and NCR53c700-66 chips (not yet supported)
+ * - one for rest (only the NCR53c810, 815, 820, and 825 are currently
+ * supported)
+ *
+ * So that we only need two SCSI scripts, we need to modify things so
+ * that we fixup register accesses in READ/WRITE instructions, and
+ * we'll also have to accomodate the bit vs. binary encoding of IDs
+ * with the 7xx chips.
+ */
+
+/*
+ * Use pci_chips_ids to translate in both directions between PCI device ID
+ * and chip numbers.
+ */
+
+static struct {
+ unsigned short pci_device_id;
+ int chip;
+/*
+ * The revision field of the PCI_CLASS_REVISION register is compared
+ * against each of these fields if the field is not -1. If it
+ * is less than min_revision or larger than max_revision, a warning
+ * message is printed.
+ */
+ int max_revision;
+ int min_revision;
+} pci_chip_ids[] = {
+ {PCI_DEVICE_ID_NCR_53C810, 810, 2, 1},
+ {PCI_DEVICE_ID_NCR_53C815, 815, 3, 2},
+ {PCI_DEVICE_ID_NCR_53C820, 820, -1, -1},
+ {PCI_DEVICE_ID_NCR_53C825, 825, -1, -1}
+};
+
+#define NPCI_CHIP_IDS (sizeof (pci_chip_ids) / sizeof(pci_chip_ids[0]))
+
+#define ROUNDUP(adr,type) \
+ ((void *) (((long) (adr) + sizeof(type) - 1) & ~(sizeof(type) - 1)))
+
+/*
+ * Forced detection and autoprobe code for various hardware. Currently,
+ * entry points for these are not included in init/main.c because if the
+ * PCI BIOS code isn't working right, you're not going to be able to use
+ * the hardware anyways; this way we force users to solve their
+ * problems rather than forcing detection and blaming us when it
+ * does not work.
+ */
+
+static struct override {
+ int chip; /* 700, 70066, 710, 720, 810, 820 */
+ int board; /* Any special board level gunk */
+ unsigned pci:1;
+ union {
+ struct {
+ int base; /* Memory address - indicates memory mapped regs */
+ int io_port;/* I/O port address - indicates I/O mapped regs */
+ int irq; /* IRQ line */
+ int dma; /* DMA channel - often none */
+ } normal;
+ struct {
+ int bus;
+ int device;
+ int function;
+ } pci;
+ } data;
+ long long options;
+} overrides [4] = {{0,},};
+static int commandline_current = 0;
+static int no_overrides = 0;
+
+#if 0
+#define OVERRIDE_LIMIT (sizeof(overrides) / sizeof(struct override))
+#else
+#define OVERRIDE_LIMIT commandline_current
+#endif
+
+/*
+ * Function: issue_to_cmd
+ *
+ * Purpose: convert jump instruction in issue array to NCR53c7x0_cmd
+ * structure pointer.
+ *
+ * Inputs; issue - pointer to start of NOP or JUMP instruction
+ * in issue array.
+ *
+ * Returns: pointer to command on success; 0 if opcode is NOP.
+ */
+
+static inline struct NCR53c7x0_cmd *
+issue_to_cmd (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ u32 *issue)
+{
+ return (issue[0] != hostdata->NOP_insn) ?
+ /*
+ * If the IF TRUE bit is set, it's a JUMP instruction. The
+ * operand is a bus pointer to the dsa_begin routine for this DSA. The
+ * dsa field of the NCR53c7x0_cmd structure starts with the
+ * DSA code template. By converting to a virtual address,
+ * subtracting the code template size, and offset of the
+ * dsa field, we end up with a pointer to the start of the
+ * structure (alternatively, we could use the
+ * dsa_cmnd field, an anachronism from when we weren't
+ * sure what the relationship between the NCR structures
+ * and host structures were going to be.
+ */
+ (struct NCR53c7x0_cmd *) ((char *) bus_to_virt (issue[1]) -
+ (hostdata->E_dsa_code_begin - hostdata->E_dsa_code_template) -
+ offsetof(struct NCR53c7x0_cmd, dsa))
+ /* If the IF TRUE bit is not set, it's a NOP */
+ : NULL;
+}
+
+
+/*
+ * Function : static internal_setup(int board, int chip, char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : board - currently, unsupported. chip - 700, 70066, 710, 720
+ * 810, 815, 820, 825, although currently only the NCR53c810 is
+ * supported.
+ *
+ */
+
+static void
+internal_setup(int board, int chip, char *str, int *ints) {
+ unsigned char pci; /* Specifies a PCI override, with bus, device,
+ function */
+
+ pci = (str && !strcmp (str, "pci")) ? 1 : 0;
+
+/*
+ * Override syntaxes are as follows :
+ * ncr53c700,ncr53c700-66,ncr53c710,ncr53c720=mem,io,irq,dma
+ * ncr53c810,ncr53c820,ncr53c825=mem,io,irq or pci,bus,device,function
+ */
+
+ if (commandline_current < OVERRIDE_LIMIT) {
+ overrides[commandline_current].pci = pci ? 1 : 0;
+ if (!pci) {
+ overrides[commandline_current].data.normal.base = ints[1];
+ overrides[commandline_current].data.normal.io_port = ints[2];
+ overrides[commandline_current].data.normal.irq = ints[3];
+ overrides[commandline_current].data.normal.dma = (ints[0] >= 4) ?
+ ints[4] : DMA_NONE;
+ /* FIXME: options is now a long long */
+ overrides[commandline_current].options = (ints[0] >= 5) ?
+ ints[5] : 0;
+ } else {
+ overrides[commandline_current].data.pci.bus = ints[1];
+ overrides[commandline_current].data.pci.device = ints[2];
+ overrides[commandline_current].data.pci.function = ints[3];
+ /* FIXME: options is now a long long */
+ overrides[commandline_current].options = (ints[0] >= 4) ?
+ ints[4] : 0;
+ }
+ overrides[commandline_current].board = board;
+ overrides[commandline_current].chip = chip;
+ ++commandline_current;
+ ++no_overrides;
+ } else {
+ printk ("53c7,7x0.c:internal_setup() : too many overrides\n");
+ }
+}
+
+/*
+ * XXX - we might want to implement a single override function
+ * with a chip type field, revamp the command line configuration,
+ * etc.
+ */
+
+#define setup_wrapper(x) \
+void ncr53c##x##_setup (char *str, int *ints) { \
+ internal_setup (BOARD_GENERIC, x, str, ints); \
+}
+
+setup_wrapper(700)
+setup_wrapper(70066)
+setup_wrapper(710)
+setup_wrapper(720)
+setup_wrapper(810)
+setup_wrapper(815)
+setup_wrapper(820)
+setup_wrapper(825)
+
+/*
+ * FIXME: we should junk these, in favor of synchronous_want and
+ * wide_want in the NCR53c7x0_hostdata structure.
+ */
+
+/* Template for "preferred" synchronous transfer parameters. */
+
+static const unsigned char sdtr_message[] = {
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 50 /* *4ns */, 8 /* off */
+};
+
+/* Template to request asynchronous transfers */
+
+static const unsigned char async_message[] = {
+ EXTENDED_MESSAGE, 3 /* length */, EXTENDED_SDTR, 0, 0 /* asynchronous */
+};
+
+/* Template for "preferred" WIDE transfer parameters */
+
+static const unsigned char wdtr_message[] = {
+ EXTENDED_MESSAGE, 2 /* length */, EXTENDED_WDTR, 1 /* 2^1 bytes */
+};
+
+/*
+ * Function : struct Scsi_Host *find_host (int host)
+ *
+ * Purpose : KGDB support function which translates a host number
+ * to a host structure.
+ *
+ * Inputs : host - number of SCSI host
+ *
+ * Returns : NULL on failure, pointer to host structure on success.
+ */
+
+static struct Scsi_Host *
+find_host (int host) {
+ struct Scsi_Host *h;
+ for (h = first_host; h && h->host_no != host; h = h->next);
+ if (!h) {
+ printk (KERN_ALERT "scsi%d not found\n", host);
+ return NULL;
+ } else if (h->hostt != the_template) {
+ printk (KERN_ALERT "scsi%d is not a NCR board\n", host);
+ return NULL;
+ }
+ return h;
+}
+
+/*
+ * Function : request_synchronous (int host, int target)
+ *
+ * Purpose : KGDB interface which will allow us to negotiate for
+ * synchronous transfers. This ill be replaced with a more
+ * integrated function; perhaps a new entry in the scsi_host
+ * structure, accessable via an ioctl() or perhaps /proc/scsi.
+ *
+ * Inputs : host - number of SCSI host; target - number of target.
+ *
+ * Returns : 0 when negotiation has been setup for next SCSI command,
+ * -1 on failure.
+ */
+
+static int
+request_synchronous (int host, int target) {
+ struct Scsi_Host *h;
+ struct NCR53c7x0_hostdata *hostdata;
+ unsigned long flags;
+ if (target < 0) {
+ printk (KERN_ALERT "target %d is bogus\n", target);
+ return -1;
+ }
+ if (!(h = find_host (host)))
+ return -1;
+ else if (h->this_id == target) {
+ printk (KERN_ALERT "target %d is host ID\n", target);
+ return -1;
+ }
+#ifndef LINUX_1_2
+ else if (target > h->max_id) {
+ printk (KERN_ALERT "target %d exceeds maximum of %d\n", target,
+ h->max_id);
+ return -1;
+ }
+#endif
+ hostdata = (struct NCR53c7x0_hostdata *)h->hostdata;
+
+ save_flags(flags);
+ cli();
+ if (hostdata->initiate_sdtr & (1 << target)) {
+ restore_flags(flags);
+ printk (KERN_ALERT "target %d allready doing SDTR\n", target);
+ return -1;
+ }
+ hostdata->initiate_sdtr |= (1 << target);
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Function : request_disconnect (int host, int on_or_off)
+ *
+ * Purpose : KGDB support function, tells us to allow or disallow
+ * disconnections.
+ *
+ * Inputs : host - number of SCSI host; on_or_off - non-zero to allow,
+ * zero to disallow.
+ *
+ * Returns : 0 on success, * -1 on failure.
+ */
+
+static int
+request_disconnect (int host, int on_or_off) {
+ struct Scsi_Host *h;
+ struct NCR53c7x0_hostdata *hostdata;
+ if (!(h = find_host (host)))
+ return -1;
+ hostdata = (struct NCR53c7x0_hostdata *) h->hostdata;
+ if (on_or_off)
+ hostdata->options |= OPTION_DISCONNECT;
+ else
+ hostdata->options &= ~OPTION_DISCONNECT;
+ return 0;
+}
+
+/*
+ * Function : static void NCR53c7x0_driver_init (struct Scsi_Host *host)
+ *
+ * Purpose : Initialize internal structures, as required on startup, or
+ * after a SCSI bus reset.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ */
+
+static void
+NCR53c7x0_driver_init (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int i, j;
+ u32 *current;
+ for (i = 0; i < 16; ++i) {
+ hostdata->request_sense[i] = 0;
+ for (j = 0; j < 8; ++j)
+ hostdata->busy[i][j] = 0;
+ set_synchronous (host, i, /* sxfer */ 0, hostdata->saved_scntl3, 0);
+ }
+ hostdata->issue_queue = NULL;
+ hostdata->running_list = hostdata->finished_queue =
+ hostdata->current = NULL;
+ for (i = 0, current = (u32 *) hostdata->schedule;
+ i < host->can_queue; ++i, current += 2) {
+ current[0] = hostdata->NOP_insn;
+ current[1] = 0xdeadbeef;
+ }
+ current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) | DBC_TCI_TRUE;
+ current[1] = (u32) virt_to_bus (hostdata->script) +
+ hostdata->E_wait_reselect;
+ hostdata->reconnect_dsa_head = 0;
+ hostdata->addr_reconnect_dsa_head = (u32)
+ virt_to_bus((void *) &(hostdata->reconnect_dsa_head));
+ hostdata->expecting_iid = 0;
+ hostdata->expecting_sto = 0;
+ if (hostdata->options & OPTION_ALWAYS_SYNCHRONOUS)
+ hostdata->initiate_sdtr = 0xffff;
+ else
+ hostdata->initiate_sdtr = 0;
+ hostdata->talked_to = 0;
+ hostdata->idle = 1;
+}
+
+/*
+ * Function : static int ccf_to_clock (int ccf)
+ *
+ * Purpose : Return the largest SCSI clock allowable for a given
+ * clock conversion factor, allowing us to do synchronous periods
+ * when we don't know what the SCSI clock is by taking at least
+ * as long as the device says we can.
+ *
+ * Inputs : ccf
+ *
+ * Returns : clock on success, -1 on failure.
+ */
+
+static int
+ccf_to_clock (int ccf) {
+ switch (ccf) {
+ case 1: return 25000000; /* Divide by 1.0 */
+ case 2: return 37500000; /* Divide by 1.5 */
+ case 3: return 50000000; /* Divide by 2.0 */
+ case 0: /* Divide by 3.0 */
+ case 4: return 66000000;
+ default: return -1;
+ }
+}
+
+/*
+ * Function : static int clock_to_ccf (int clock)
+ *
+ * Purpose : Return the clock conversion factor for a given SCSI clock.
+ *
+ * Inputs : clock - SCSI clock expressed in Hz.
+ *
+ * Returns : ccf on success, -1 on failure.
+ */
+
+static int
+clock_to_ccf (int clock) {
+ if (clock < 16666666)
+ return -1;
+ if (clock < 25000000)
+ return 1; /* Divide by 1.0 */
+ else if (clock < 37500000)
+ return 2; /* Divide by 1.5 */
+ else if (clock < 50000000)
+ return 3; /* Divide by 2.0 */
+ else if (clock < 66000000)
+ return 4; /* Divide by 3.0 */
+ else
+ return -1;
+}
+
+/*
+ * Function : static int NCR53c7x0_init (struct Scsi_Host *host)
+ *
+ * Purpose : initialize the internal structures for a given SCSI host
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : when this function is called, the chip_type
+ * field of the hostdata structure MUST have been set.
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int
+NCR53c7x0_init (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ int i, ccf, expected_ccf;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct Scsi_Host *search;
+ /*
+ * There are some things which we need to know about in order to provide
+ * a semblance of support. Print 'em if they aren't what we expect,
+ * otherwise don't add to the noise.
+ *
+ * -1 means we don't know what to expect.
+ */
+ int expected_id = -1;
+ int expected_clock = -1;
+ int uninitialized = 0;
+ /*
+ * FIXME : this is only on Intel boxes. On other platforms, this
+ * will differ.
+ */
+ int expected_mapping = OPTION_IO_MAPPED;
+ NCR53c7x0_local_setup(host);
+
+ switch (hostdata->chip) {
+ case 820:
+ case 825:
+#ifdef notyet
+ host->max_id = 15;
+#endif
+ /* Fall through */
+ case 810:
+ case 815:
+ hostdata->dstat_sir_intr = NCR53c8x0_dstat_sir_intr;
+ hostdata->init_save_regs = NULL;
+ hostdata->dsa_fixup = NCR53c8xx_dsa_fixup;
+ hostdata->init_fixup = NCR53c8x0_init_fixup;
+ hostdata->soft_reset = NCR53c8x0_soft_reset;
+ hostdata->run_tests = NCR53c8xx_run_tests;
+/* Is the SCSI clock ever anything else on these chips? */
+ expected_clock = hostdata->scsi_clock = 40000000;
+ expected_id = 7;
+ break;
+ default:
+ printk ("scsi%d : chip type of %d is not supported yet, detaching.\n",
+ host->host_no, hostdata->chip);
+ scsi_unregister (host);
+ return -1;
+ }
+
+ /* Assign constants accessed by NCR */
+ hostdata->NCR53c7xx_zero = 0;
+ hostdata->NCR53c7xx_msg_reject = MESSAGE_REJECT;
+ hostdata->NCR53c7xx_msg_abort = ABORT;
+ hostdata->NCR53c7xx_msg_nop = NOP;
+ hostdata->NOP_insn = (DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24;
+
+ if (expected_mapping == -1 ||
+ (hostdata->options & (OPTION_MEMORY_MAPPED)) !=
+ (expected_mapping & OPTION_MEMORY_MAPPED))
+ printk ("scsi%d : using %s mapped access\n", host->host_no,
+ (hostdata->options & OPTION_MEMORY_MAPPED) ? "memory" :
+ "io");
+
+ hostdata->dmode = (hostdata->chip == 700 || hostdata->chip == 70066) ?
+ DMODE_REG_00 : DMODE_REG_10;
+ hostdata->istat = ((hostdata->chip / 100) == 8) ?
+ ISTAT_REG_800 : ISTAT_REG_700;
+
+/* Only the ISTAT register is readable when the NCR is running, so make
+ sure it's halted. */
+ ncr_halt(host);
+
+/*
+ * XXX - the NCR53c700 uses bitfielded registers for SCID, SDID, etc,
+ * as does the 710 with one bit per SCSI ID. Conversely, the NCR
+ * uses a normal, 3 bit binary representation of these values.
+ *
+ * Get the rest of the NCR documentation, and FIND OUT where the change
+ * was.
+ */
+#if 0
+ tmp = hostdata->this_id_mask = NCR53c7x0_read8(SCID_REG);
+ for (host->this_id = 0; tmp != 1; tmp >>=1, ++host->this_id);
+#else
+ host->this_id = NCR53c7x0_read8(SCID_REG) & 15;
+ if (host->this_id == 0)
+ host->this_id = 7; /* sanitize hostid---0 doesn't make sense */
+ hostdata->this_id_mask = 1 << host->this_id;
+#endif
+
+/*
+ * Note : we should never encounter a board setup for ID0. So,
+ * if we see ID0, assume that it was uninitialized and set it
+ * to the industry standard 7.
+ */
+ if (!host->this_id) {
+ printk("scsi%d : initiator ID was %d, changing to 7\n",
+ host->host_no, host->this_id);
+ host->this_id = 7;
+ hostdata->this_id_mask = 1 << 7;
+ uninitialized = 1;
+ };
+
+ if (expected_id == -1 || host->this_id != expected_id)
+ printk("scsi%d : using initiator ID %d\n", host->host_no,
+ host->this_id);
+
+ /*
+ * Save important registers to allow a soft reset.
+ */
+
+ if ((hostdata->chip / 100) == 8) {
+ /*
+ * CTEST4 controls burst mode disable.
+ */
+ hostdata->saved_ctest4 = NCR53c7x0_read8(CTEST4_REG_800) &
+ CTEST4_800_SAVE;
+ } else {
+ /*
+ * CTEST7 controls cache snooping, burst mode, and support for
+ * external differential drivers.
+ */
+ hostdata->saved_ctest7 = NCR53c7x0_read8(CTEST7_REG) & CTEST7_SAVE;
+ }
+
+ /*
+ * On NCR53c700 series chips, DCNTL controls the SCSI clock divisor,
+ * on 800 series chips, it allows for a totem-pole IRQ driver.
+ */
+
+ hostdata->saved_dcntl = NCR53c7x0_read8(DCNTL_REG);
+
+ /*
+ * DCNTL_800_IRQM controls weather we are using an open drain
+ * driver (reset) or totem pole driver (set). In all cases,
+ * it's level active. I suppose this is an issue when we're trying to
+ * wire-or the same PCI INTx line?
+ */
+ if ((hostdata->chip / 100) == 8)
+ hostdata->saved_dcntl &= ~DCNTL_800_IRQM;
+
+ /*
+ * DMODE controls DMA burst length, and on 700 series chips,
+ * 286 mode and bus width
+ */
+ hostdata->saved_dmode = NCR53c7x0_read8(hostdata->dmode);
+
+ /*
+ * Now that burst length and enabled/disabled status is known,
+ * clue the user in on it.
+ */
+
+ if ((hostdata->chip / 100) == 8) {
+ if (hostdata->saved_ctest4 & CTEST4_800_BDIS) {
+ printk ("scsi%d : burst mode disabled\n", host->host_no);
+ } else {
+ switch (hostdata->saved_dmode & DMODE_BL_MASK) {
+ case DMODE_BL_2: i = 2; break;
+ case DMODE_BL_4: i = 4; break;
+ case DMODE_BL_8: i = 8; break;
+ case DMODE_BL_16: i = 16; break;
+ default: i = 0;
+ }
+ printk ("scsi%d : burst length %d\n", host->host_no, i);
+ }
+ }
+
+ /*
+ * On NCR53c810 and NCR53c820 chips, SCNTL3 contails the synchronous
+ * and normal clock conversion factors.
+ */
+ if (hostdata->chip / 100 == 8) {
+ expected_ccf = clock_to_ccf (expected_clock);
+ hostdata->saved_scntl3 = NCR53c7x0_read8(SCNTL3_REG_800);
+ ccf = hostdata->saved_scntl3 & SCNTL3_800_CCF_MASK;
+ if (expected_ccf != -1 && ccf != expected_ccf && !ccf) {
+ hostdata->saved_scntl3 = (hostdata->saved_scntl3 &
+ ~SCNTL3_800_CCF_MASK) | expected_ccf;
+ if (!uninitialized) {
+ printk ("scsi%d : reset ccf to %d from %d\n",
+ host->host_no, expected_ccf, ccf);
+ uninitialized = 1;
+ }
+ }
+ } else
+ ccf = 0;
+
+ /*
+ * If we don't have a SCSI clock programmed, pick one on the upper
+ * bound of that allowed by NCR so that our transfers err on the
+ * slow side, since transfer period must be >= the agreed
+ * upon period.
+ */
+
+ if ((!hostdata->scsi_clock) && (hostdata->scsi_clock = ccf_to_clock (ccf))
+ == -1) {
+ printk ("scsi%d : clock conversion factor %d unknown.\n"
+ " synchronous transfers disabled\n",
+ host->host_no, ccf);
+ hostdata->options &= ~OPTION_SYNCHRONOUS;
+ hostdata->scsi_clock = 0;
+ }
+
+ if (expected_clock == -1 || hostdata->scsi_clock != expected_clock)
+ printk ("scsi%d : using %dMHz SCSI clock\n", host->host_no,
+ hostdata->scsi_clock / 1000000);
+
+ for (i = 0; i < 16; ++i)
+ hostdata->cmd_allocated[i] = 0;
+
+ if (hostdata->init_save_regs)
+ hostdata->init_save_regs (host);
+ if (hostdata->init_fixup)
+ hostdata->init_fixup (host);
+
+ if (!the_template) {
+ the_template = host->hostt;
+ first_host = host;
+ }
+
+ /*
+ * Linux SCSI drivers have always been plagued with initialization
+ * problems - some didn't work with the BIOS disabled since they expected
+ * initialization from it, some didn't work when the networking code
+ * was enabled and registers got scrambled, etc.
+ *
+ * To avoid problems like this, in the future, we will do a soft
+ * reset on the SCSI chip, taking it back to a sane state.
+ */
+
+ hostdata->soft_reset (host);
+
+#if 1
+ hostdata->debug_count_limit = -1;
+#else
+ hostdata->debug_count_limit = 1;
+#endif
+ hostdata->intrs = -1;
+ hostdata->resets = -1;
+ memcpy ((void *) hostdata->synchronous_want, (void *) sdtr_message,
+ sizeof (hostdata->synchronous_want));
+
+ NCR53c7x0_driver_init (host);
+
+ /*
+ * Set up an interrupt handler if we aren't already sharing an IRQ
+ * with another board.
+ */
+
+ for (search = first_host; search && !(search->hostt == the_template &&
+ search->irq == host->irq && search != host); search=search->next);
+
+ if (!search) {
+ if (request_irq(host->irq, NCR53c7x0_intr, SA_INTERRUPT, "53c7,8xx")) {
+ printk("scsi%d : IRQ%d not free, detaching\n"
+ " You have either a configuration problem, or a\n"
+ " broken BIOS. You may wish to manually assign\n"
+ " an interrupt to the NCR board rather than using\n"
+ " an automatic setting.\n",
+ host->host_no, host->irq);
+ scsi_unregister (host);
+ return -1;
+ }
+ } else {
+ printk("scsi%d : using interrupt handler previously installed for scsi%d\n",
+ host->host_no, search->host_no);
+ }
+
+
+ if ((hostdata->run_tests && hostdata->run_tests(host) == -1) ||
+ (hostdata->options & OPTION_DEBUG_TESTS_ONLY)) {
+ /* XXX Should disable interrupts, etc. here */
+ scsi_unregister (host);
+ return -1;
+ } else {
+ if (host->io_port) {
+ host->n_io_port = 128;
+ request_region (host->io_port, host->n_io_port, "ncr53c7,8xx");
+ }
+ }
+
+ if (NCR53c7x0_read8 (SBCL_REG) & SBCL_BSY) {
+ printk ("scsi%d : bus wedge, doing SCSI reset\n", host->host_no);
+ hard_reset (host);
+ }
+ return 0;
+}
+
+/*
+ * Function : static int normal_init(Scsi_Host_Template *tpnt, int board,
+ * int chip, u32 base, int io_port, int irq, int dma, int pcivalid,
+ * unsigned char pci_bus, unsigned char pci_device_fn,
+ * long long options);
+ *
+ * Purpose : initializes a NCR53c7,8x0 based on base addresses,
+ * IRQ, and DMA channel.
+ *
+ * Useful where a new NCR chip is backwards compatible with
+ * a supported chip, but the DEVICE ID has changed so it
+ * doesn't show up when the autoprobe does a pcibios_find_device.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ * product, chip - 810, 820, or 825, bus - PCI bus, device_fn -
+ * device and function encoding as used by PCI BIOS calls.
+ *
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+static int
+normal_init (Scsi_Host_Template *tpnt, int board, int chip,
+ u32 base, int io_port, int irq, int dma, int pci_valid,
+ unsigned char pci_bus, unsigned char pci_device_fn, long long options) {
+ struct Scsi_Host *instance;
+ struct NCR53c7x0_hostdata *hostdata;
+ char chip_str[80];
+ int script_len = 0, dsa_len = 0, size = 0, max_cmd_size = 0,
+ schedule_size = 0, ok = 0;
+ void *tmp;
+
+ options |= perm_options;
+
+ switch (chip) {
+ case 825:
+ case 820:
+ case 815:
+ case 810:
+ schedule_size = (tpnt->can_queue + 1) * 8 /* JUMP instruction size */;
+ script_len = NCR53c8xx_script_len;
+ dsa_len = NCR53c8xx_dsa_len;
+ options |= OPTION_INTFLY;
+ sprintf (chip_str, "NCR53c%d", chip);
+ break;
+ default:
+ printk("scsi-ncr53c7,8xx : unsupported SCSI chip %d\n", chip);
+ return -1;
+ }
+
+ printk("scsi-ncr53c7,8xx : %s at memory 0x%x, io 0x%x, irq %d",
+ chip_str, (unsigned) base, io_port, irq);
+ if (dma == DMA_NONE)
+ printk("\n");
+ else
+ printk(", dma %d\n", dma);
+
+ if ((chip / 100 == 8) && !pci_valid)
+ printk ("scsi-ncr53c7,8xx : for better reliability and performance, please use the\n"
+ " PCI override instead.\n"
+ " Syntax : ncr53c8{10,15,20,25}=pci,<bus>,<device>,<function>\n"
+ " <bus> and <device> are usually 0.\n");
+
+ if (options & OPTION_DEBUG_PROBE_ONLY) {
+ printk ("scsi-ncr53c7,8xx : probe only enabled, aborting initialization\n");
+ return -1;
+ }
+
+ max_cmd_size = sizeof(struct NCR53c7x0_cmd) + dsa_len +
+ /* Size of dynamic part of command structure : */
+ 2 * /* Worst case : we don't know if we need DATA IN or DATA out */
+ ( 2 * /* Current instructions per scatter/gather segment */
+ tpnt->sg_tablesize +
+ 3 /* Current startup / termination required per phase */
+ ) *
+ 8 /* Each instruction is eight bytes */;
+
+ /* Allocate fixed part of hostdata, dynamic part to hold appropriate
+ SCSI SCRIPT(tm) plus a single, maximum-sized NCR53c7x0_cmd structure.
+
+ We need a NCR53c7x0_cmd structure for scan_scsis() when we are
+ not loaded as a module, and when we're loaded as a module, we
+ can't use a non-dynamically allocated structure because modules
+ are vmalloc()'d, which can allow structures to cross page
+ boundaries and breaks our physical/virtual address assumptions
+ for DMA.
+
+ So, we stick it past the end of our hostdata structure.
+
+ ASSUMPTION :
+ Regardless of how many simultaneous SCSI commands we allow,
+ the probe code only executes a _single_ instruction at a time,
+ so we only need one here, and don't need to allocate NCR53c7x0_cmd
+ structures for each target until we are no longer in scan_scsis
+ and kmalloc() has become functional (memory_init() happens
+ after all device driver initialization).
+ */
+
+ size = sizeof(struct NCR53c7x0_hostdata) + script_len +
+ /* Note that alignment will be guaranteed, since we put the command
+ allocated at probe time after the fixed-up SCSI script, which
+ consists of 32 bit words, aligned on a 32 bit boundary. But
+ on a 64bit machine we need 8 byte alignment for hostdata->free, so
+ we add in another 4 bytes to take care of potential misalignment
+ */
+ (sizeof(void *) - sizeof(u32)) + max_cmd_size + schedule_size;
+
+ instance = scsi_register (tpnt, size);
+ if (!instance)
+ return -1;
+
+ /* FIXME : if we ever support an ISA NCR53c7xx based board, we
+ need to check if the chip is running in a 16 bit mode, and if so
+ unregister it if it is past the 16M (0x1000000) mark */
+
+ hostdata = (struct NCR53c7x0_hostdata *)
+ instance->hostdata;
+ hostdata->size = size;
+ hostdata->script_count = script_len / sizeof(u32);
+ hostdata = (struct NCR53c7x0_hostdata *) instance->hostdata;
+ hostdata->board = board;
+ hostdata->chip = chip;
+ if ((hostdata->pci_valid = pci_valid)) {
+ hostdata->pci_bus = pci_bus;
+ hostdata->pci_device_fn = pci_device_fn;
+ }
+
+ /*
+ * Being memory mapped is more desirable, since
+ *
+ * - Memory accesses may be faster.
+ *
+ * - The destination and source address spaces are the same for
+ * all instructions, meaning we don't have to twiddle dmode or
+ * any other registers.
+ *
+ * So, we try for memory mapped, and if we don't get it,
+ * we go for port mapped, and that failing we tell the user
+ * it can't work.
+ */
+
+ if (base) {
+ instance->base = (unsigned char *) (unsigned long) base;
+ /* Check for forced I/O mapping */
+ if (!(options & OPTION_IO_MAPPED)) {
+ options |= OPTION_MEMORY_MAPPED;
+ ok = 1;
+ }
+ } else {
+ options &= ~OPTION_MEMORY_MAPPED;
+ }
+
+ if (io_port) {
+ instance->io_port = io_port;
+ options |= OPTION_IO_MAPPED;
+ ok = 1;
+ } else {
+ options &= ~OPTION_IO_MAPPED;
+ }
+
+ if (!ok) {
+ printk ("scsi%d : not initializing, no I/O or memory mapping known \n",
+ instance->host_no);
+ scsi_unregister (instance);
+ return -1;
+ }
+ instance->irq = irq;
+ instance->dma_channel = dma;
+
+ hostdata->options = options;
+ hostdata->dsa_len = dsa_len;
+ hostdata->max_cmd_size = max_cmd_size;
+ hostdata->num_cmds = 1;
+ /* Initialize single command */
+ tmp = (hostdata->script + hostdata->script_count);
+ hostdata->free = ROUNDUP(tmp, void *);
+ hostdata->free->real = tmp;
+ hostdata->free->size = max_cmd_size;
+ hostdata->free->free = NULL;
+ hostdata->free->next = NULL;
+ hostdata->extra_allocate = 0;
+
+ /* Allocate command start code space */
+ hostdata->schedule = (chip == 700 || chip == 70066) ?
+ NULL : (u32 *) ((char *)hostdata->free + max_cmd_size);
+
+/*
+ * For diagnostic purposes, we don't really care how fast things blaze.
+ * For profiling, we want to access the 800ns resolution system clock,
+ * using a 'C' call on the host processor.
+ *
+ * Therefore, there's no need for the NCR chip to directly manipulate
+ * this data, and we should put it wherever is most convienient for
+ * Linux.
+ */
+ if (track_events)
+ hostdata->events = (struct NCR53c7x0_event *) (track_events ?
+ vmalloc (sizeof (struct NCR53c7x0_event) * track_events) : NULL);
+ else
+ hostdata->events = NULL;
+
+ if (hostdata->events) {
+ memset ((void *) hostdata->events, 0, sizeof(struct NCR53c7x0_event) *
+ track_events);
+ hostdata->event_size = track_events;
+ hostdata->event_index = 0;
+ } else
+ hostdata->event_size = 0;
+
+ return NCR53c7x0_init(instance);
+}
+
+
+/*
+ * Function : static int ncr_pci_init(Scsi_Host_Template *tpnt, int board,
+ * int chip, int bus, int device_fn, long long options)
+ *
+ * Purpose : initializes a NCR53c800 family based on the PCI
+ * bus, device, and function location of it. Allows
+ * reprogramming of latency timer and determining addresses
+ * and whether bus mastering, etc. are OK.
+ *
+ * Useful where a new NCR chip is backwards compatible with
+ * a supported chip, but the DEVICE ID has changed so it
+ * doesn't show up when the autoprobe does a pcibios_find_device.
+ *
+ * Inputs : tpnt - Template for this SCSI adapter, board - board level
+ * product, chip - 810, 820, or 825, bus - PCI bus, device_fn -
+ * device and function encoding as used by PCI BIOS calls.
+ *
+ * Returns : 0 on success, -1 on failure.
+ *
+ */
+
+static int
+ncr_pci_init (Scsi_Host_Template *tpnt, int board, int chip,
+ unsigned char bus, unsigned char device_fn, long long options) {
+ unsigned short vendor_id, device_id, command;
+#ifdef LINUX_1_2
+ unsigned long
+#else
+ unsigned int
+#endif
+ base, io_port;
+ unsigned char irq, revision;
+ int error, expected_chip;
+ int expected_id = -1, max_revision = -1, min_revision = -1;
+ int i;
+
+ printk("scsi-ncr53c7,8xx : at PCI bus %d, device %d, function %d\n",
+ bus, (int) (device_fn & 0xf8) >> 3,
+ (int) device_fn & 7);
+
+ if (!pcibios_present()) {
+ printk("scsi-ncr53c7,8xx : not initializing due to lack of PCI BIOS,\n"
+ " try using memory, port, irq override instead.\n");
+ return -1;
+ }
+
+ if ((error = pcibios_read_config_word (bus, device_fn, PCI_VENDOR_ID,
+ &vendor_id)) ||
+ (error = pcibios_read_config_word (bus, device_fn, PCI_DEVICE_ID,
+ &device_id)) ||
+ (error = pcibios_read_config_word (bus, device_fn, PCI_COMMAND,
+ &command)) ||
+ (error = pcibios_read_config_dword (bus, device_fn,
+ PCI_BASE_ADDRESS_0, &io_port)) ||
+ (error = pcibios_read_config_dword (bus, device_fn,
+ PCI_BASE_ADDRESS_1, &base)) ||
+ (error = pcibios_read_config_byte (bus, device_fn, PCI_CLASS_REVISION,
+ &revision)) ||
+ (error = pcibios_read_config_byte (bus, device_fn, PCI_INTERRUPT_LINE,
+ &irq))) {
+ printk ("scsi-ncr53c7,8xx : error %s not initializing due to error reading configuration space\n"
+ " perhaps you specified an incorrect PCI bus, device, or function.\n"
+ , pcibios_strerror(error));
+ return -1;
+ }
+
+ /* If any one ever clones the NCR chips, this will have to change */
+
+ if (vendor_id != PCI_VENDOR_ID_NCR) {
+ printk ("scsi-ncr53c7,8xx : not initializing, 0x%04x is not NCR vendor ID\n",
+ (int) vendor_id);
+ return -1;
+ }
+
+
+ /*
+ * Bit 0 is the address space indicator and must be one for I/O
+ * space mappings, bit 1 is reserved, discard them after checking
+ * that they have the correct value of 1.
+ */
+
+ if (command & PCI_COMMAND_IO) {
+ if ((io_port & 3) != 1) {
+ printk ("scsi-ncr53c7,8xx : disabling I/O mapping since base address 0 (0x%x)\n"
+ " bits 0..1 indicate a non-IO mapping\n",
+ (unsigned) io_port);
+ io_port = 0;
+ } else
+ io_port &= PCI_BASE_ADDRESS_IO_MASK;
+ } else {
+ io_port = 0;
+ }
+
+ if (command & PCI_COMMAND_MEMORY) {
+ if ((base & PCI_BASE_ADDRESS_SPACE) != PCI_BASE_ADDRESS_SPACE_MEMORY) {
+ printk("scsi-ncr53c7,8xx : disabling memory mapping since base address 1\n"
+ " contains a non-memory mapping\n");
+ base = 0;
+ } else
+ base &= PCI_BASE_ADDRESS_MEM_MASK;
+ } else {
+ base = 0;
+ }
+
+ if (!io_port && !base) {
+ printk ("scsi-ncr53c7,8xx : not initializing, both I/O and memory mappings disabled\n");
+ return -1;
+ }
+
+ if (!(command & PCI_COMMAND_MASTER)) {
+ printk ("scsi-ncr53c7,8xx : not initializing, BUS MASTERING was disabled\n");
+ return -1;
+ }
+
+ for (i = 0; i < NPCI_CHIP_IDS; ++i) {
+ if (device_id == pci_chip_ids[i].pci_device_id) {
+ max_revision = pci_chip_ids[i].max_revision;
+ min_revision = pci_chip_ids[i].min_revision;
+ expected_chip = pci_chip_ids[i].chip;
+ }
+ if (chip == pci_chip_ids[i].chip)
+ expected_id = pci_chip_ids[i].pci_device_id;
+ }
+
+ if (chip && device_id != expected_id)
+ printk ("scsi-ncr53c7,8xx : warning : device id of 0x%04x doesn't\n"
+ " match expected 0x%04x\n",
+ (unsigned int) device_id, (unsigned int) expected_id );
+
+ if (max_revision != -1 && revision > max_revision)
+ printk ("scsi-ncr53c7,8xx : warning : revision of %d is greater than %d.\n",
+ (int) revision, max_revision);
+ else if (min_revision != -1 && revision < min_revision)
+ printk ("scsi-ncr53c7,8xx : warning : revision of %d is less than %d.\n",
+ (int) revision, min_revision);
+
+ if (io_port && check_region (io_port, 128)) {
+ printk ("scsi-ncr53c7,8xx : IO region 0x%x to 0x%x is in use\n",
+ (unsigned) io_port, (unsigned) io_port + 127);
+ return -1;
+ }
+
+ return normal_init (tpnt, board, chip, (int) base, io_port,
+ (int) irq, DMA_NONE, 1, bus, device_fn, options);
+}
+
+
+/*
+ * Function : int NCR53c7xx_detect(Scsi_Host_Template *tpnt)
+ *
+ * Purpose : detects and initializes NCR53c7,8x0 SCSI chips
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter
+ *
+ * Returns : number of host adapters detected
+ *
+ */
+
+int
+NCR53c7xx_detect(Scsi_Host_Template *tpnt) {
+ int i;
+ int current_override;
+ int count; /* Number of boards detected */
+ unsigned char pci_bus, pci_device_fn;
+ static short pci_index=0; /* Device index to PCI BIOS calls */
+
+#ifndef LINUX_1_2
+ tpnt->proc_dir = &proc_scsi_ncr53c7xx;
+#endif
+
+ for (current_override = count = 0; current_override < OVERRIDE_LIMIT;
+ ++current_override) {
+ if (overrides[current_override].pci ?
+ !ncr_pci_init (tpnt, overrides[current_override].board,
+ overrides[current_override].chip,
+ (unsigned char) overrides[current_override].data.pci.bus,
+ (((overrides[current_override].data.pci.device
+ << 3) & 0xf8)|(overrides[current_override].data.pci.function &
+ 7)), overrides[current_override].options):
+ !normal_init (tpnt, overrides[current_override].board,
+ overrides[current_override].chip,
+ overrides[current_override].data.normal.base,
+ overrides[current_override].data.normal.io_port,
+ overrides[current_override].data.normal.irq,
+ overrides[current_override].data.normal.dma,
+ 0 /* PCI data invalid */, 0 /* PCI bus place holder */,
+ 0 /* PCI device_function place holder */,
+ overrides[current_override].options)) {
+ ++count;
+ }
+ }
+
+ if (pcibios_present()) {
+ for (i = 0; i < NPCI_CHIP_IDS; ++i)
+ for (pci_index = 0;
+ !pcibios_find_device (PCI_VENDOR_ID_NCR,
+ pci_chip_ids[i].pci_device_id, pci_index, &pci_bus,
+ &pci_device_fn);
+ ++pci_index)
+ if (!ncr_pci_init (tpnt, BOARD_GENERIC, pci_chip_ids[i].chip,
+ pci_bus, pci_device_fn, /* no options */ 0))
+ ++count;
+ }
+ return count;
+}
+
+/* NCR53c810 and NCR53c820 script handling code */
+
+#include "53c8xx_d.h"
+#ifdef A_int_debug_sync
+#define DEBUG_SYNC_INTR A_int_debug_sync
+#endif
+static int NCR53c8xx_script_len = sizeof (SCRIPT);
+static int NCR53c8xx_dsa_len = A_dsa_end + Ent_dsa_zero - Ent_dsa_code_template;
+
+/*
+ * Function : static void NCR53c8x0_init_fixup (struct Scsi_Host *host)
+ *
+ * Purpose : copy and fixup the SCSI SCRIPTS(tm) code for this device.
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ */
+
+static void
+NCR53c8x0_init_fixup (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned char tmp;
+ int i, ncr_to_memory, memory_to_ncr;
+ u32 base;
+ NCR53c7x0_local_setup(host);
+
+
+ /* XXX - NOTE : this code MUST be made endian aware */
+ /* Copy code into buffer that was allocated at detection time. */
+ memcpy ((void *) hostdata->script, (void *) SCRIPT,
+ sizeof(SCRIPT));
+ /* Fixup labels */
+ for (i = 0; i < PATCHES; ++i)
+ hostdata->script[LABELPATCHES[i]] +=
+ virt_to_bus(hostdata->script);
+ /* Fixup addresses of constants that used to be EXTERNAL */
+
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_abort,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_abort)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_msg_reject,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_reject)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_zero,
+ virt_to_bus(&(hostdata->NCR53c7xx_zero)));
+ patch_abs_32 (hostdata->script, 0, NCR53c7xx_sink,
+ virt_to_bus(&(hostdata->NCR53c7xx_sink)));
+ patch_abs_32 (hostdata->script, 0, NOP_insn,
+ virt_to_bus(&(hostdata->NOP_insn)));
+ patch_abs_32 (hostdata->script, 0, schedule,
+ virt_to_bus((void *) hostdata->schedule));
+
+ /* Fixup references to external variables: */
+ for (i = 0; i < EXTERNAL_PATCHES_LEN; ++i)
+ hostdata->script[EXTERNAL_PATCHES[i].offset] +=
+ virt_to_bus(EXTERNAL_PATCHES[i].address);
+
+ /*
+ * Fixup absolutes set at boot-time.
+ *
+ * All non-code absolute variables suffixed with "dsa_" and "int_"
+ * are constants, and need no fixup provided the assembler has done
+ * it for us (I don't know what the "real" NCR assembler does in
+ * this case, my assembler does the right magic).
+ */
+
+ patch_abs_rwri_data (hostdata->script, 0, dsa_save_data_pointer,
+ Ent_dsa_code_save_data_pointer - Ent_dsa_zero);
+ patch_abs_rwri_data (hostdata->script, 0, dsa_restore_pointers,
+ Ent_dsa_code_restore_pointers - Ent_dsa_zero);
+ patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect,
+ Ent_dsa_code_check_reselect - Ent_dsa_zero);
+
+ /*
+ * Just for the hell of it, preserve the settings of
+ * Burst Length and Enable Read Line bits from the DMODE
+ * register. Make sure SCRIPTS start automagically.
+ */
+
+ tmp = NCR53c7x0_read8(DMODE_REG_10);
+ tmp &= (DMODE_800_ERL | DMODE_BL_MASK);
+
+ if (!(hostdata->options & OPTION_MEMORY_MAPPED)) {
+ base = (u32) host->io_port;
+ memory_to_ncr = tmp|DMODE_800_DIOM;
+ ncr_to_memory = tmp|DMODE_800_SIOM;
+ } else {
+ base = virt_to_bus(host->base);
+ memory_to_ncr = ncr_to_memory = tmp;
+ }
+
+ patch_abs_32 (hostdata->script, 0, addr_scratch, base + SCRATCHA_REG_800);
+ patch_abs_32 (hostdata->script, 0, addr_temp, base + TEMP_REG);
+
+ /*
+ * I needed some variables in the script to be accessible to
+ * both the NCR chip and the host processor. For these variables,
+ * I made the arbitrary decision to store them directly in the
+ * hostdata structure rather than in the RELATIVE area of the
+ * SCRIPTS.
+ */
+
+
+ patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_memory, tmp);
+ patch_abs_rwri_data (hostdata->script, 0, dmode_memory_to_ncr, memory_to_ncr);
+ patch_abs_rwri_data (hostdata->script, 0, dmode_ncr_to_memory, ncr_to_memory);
+
+ patch_abs_32 (hostdata->script, 0, msg_buf,
+ virt_to_bus((void *)&(hostdata->msg_buf)));
+ patch_abs_32 (hostdata->script, 0, reconnect_dsa_head,
+ virt_to_bus((void *)&(hostdata->reconnect_dsa_head)));
+ patch_abs_32 (hostdata->script, 0, addr_reconnect_dsa_head,
+ virt_to_bus((void *)&(hostdata->addr_reconnect_dsa_head)));
+ patch_abs_32 (hostdata->script, 0, reselected_identify,
+ virt_to_bus((void *)&(hostdata->reselected_identify)));
+/* reselected_tag is currently unused */
+#if 0
+ patch_abs_32 (hostdata->script, 0, reselected_tag,
+ virt_to_bus((void *)&(hostdata->reselected_tag)));
+#endif
+
+ patch_abs_32 (hostdata->script, 0, test_dest,
+ virt_to_bus((void*)&hostdata->test_dest));
+ patch_abs_32 (hostdata->script, 0, test_src,
+ virt_to_bus(&hostdata->test_source));
+
+ patch_abs_rwri_data (hostdata->script, 0, dsa_check_reselect,
+ (unsigned char)(Ent_dsa_code_check_reselect - Ent_dsa_zero));
+
+/* These are for event logging; the ncr_event enum contains the
+ actual interrupt numbers. */
+#ifdef A_int_EVENT_SELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT, (u32) EVENT_SELECT);
+#endif
+#ifdef A_int_EVENT_DISCONNECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_DISCONNECT, (u32) EVENT_DISCONNECT);
+#endif
+#ifdef A_int_EVENT_RESELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT, (u32) EVENT_RESELECT);
+#endif
+#ifdef A_int_EVENT_COMPLETE
+ patch_abs_32 (hostdata->script, 0, int_EVENT_COMPLETE, (u32) EVENT_COMPLETE);
+#endif
+#ifdef A_int_EVENT_IDLE
+ patch_abs_32 (hostdata->script, 0, int_EVENT_IDLE, (u32) EVENT_IDLE);
+#endif
+#ifdef A_int_EVENT_SELECT_FAILED
+ patch_abs_32 (hostdata->script, 0, int_EVENT_SELECT_FAILED,
+ (u32) EVENT_SELECT_FAILED);
+#endif
+#ifdef A_int_EVENT_BEFORE_SELECT
+ patch_abs_32 (hostdata->script, 0, int_EVENT_BEFORE_SELECT,
+ (u32) EVENT_BEFORE_SELECT);
+#endif
+#ifdef A_int_EVENT_RESELECT_FAILED
+ patch_abs_32 (hostdata->script, 0, int_EVENT_RESELECT_FAILED,
+ (u32) EVENT_RESELECT_FAILED);
+#endif
+
+ /*
+ * Make sure the NCR and Linux code agree on the location of
+ * certain fields.
+ */
+
+ hostdata->E_accept_message = Ent_accept_message;
+ hostdata->E_command_complete = Ent_command_complete;
+ hostdata->E_cmdout_cmdout = Ent_cmdout_cmdout;
+ hostdata->E_data_transfer = Ent_data_transfer;
+ hostdata->E_debug_break = Ent_debug_break;
+ hostdata->E_dsa_code_template = Ent_dsa_code_template;
+ hostdata->E_dsa_code_template_end = Ent_dsa_code_template_end;
+ hostdata->E_end_data_transfer = Ent_end_data_transfer;
+ hostdata->E_initiator_abort = Ent_initiator_abort;
+ hostdata->E_msg_in = Ent_msg_in;
+ hostdata->E_other_transfer = Ent_other_transfer;
+ hostdata->E_other_in = Ent_other_in;
+ hostdata->E_other_out = Ent_other_out;
+ hostdata->E_reject_message = Ent_reject_message;
+ hostdata->E_respond_message = Ent_respond_message;
+ hostdata->E_select = Ent_select;
+ hostdata->E_select_msgout = Ent_select_msgout;
+ hostdata->E_target_abort = Ent_target_abort;
+#ifdef Ent_test_0
+ hostdata->E_test_0 = Ent_test_0;
+#endif
+ hostdata->E_test_1 = Ent_test_1;
+ hostdata->E_test_2 = Ent_test_2;
+#ifdef Ent_test_3
+ hostdata->E_test_3 = Ent_test_3;
+#endif
+ hostdata->E_wait_reselect = Ent_wait_reselect;
+ hostdata->E_dsa_code_begin = Ent_dsa_code_begin;
+
+ hostdata->dsa_cmdout = A_dsa_cmdout;
+ hostdata->dsa_cmnd = A_dsa_cmnd;
+ hostdata->dsa_datain = A_dsa_datain;
+ hostdata->dsa_dataout = A_dsa_dataout;
+ hostdata->dsa_end = A_dsa_end;
+ hostdata->dsa_msgin = A_dsa_msgin;
+ hostdata->dsa_msgout = A_dsa_msgout;
+ hostdata->dsa_msgout_other = A_dsa_msgout_other;
+ hostdata->dsa_next = A_dsa_next;
+ hostdata->dsa_select = A_dsa_select;
+ hostdata->dsa_start = Ent_dsa_code_template - Ent_dsa_zero;
+ hostdata->dsa_status = A_dsa_status;
+ hostdata->dsa_jump_dest = Ent_dsa_code_fix_jump - Ent_dsa_zero +
+ 8 /* destination operand */;
+
+ /* sanity check */
+ if (A_dsa_fields_start != Ent_dsa_code_template_end -
+ Ent_dsa_zero)
+ printk("scsi%d : NCR dsa_fields start is %d not %d\n",
+ host->host_no, A_dsa_fields_start, Ent_dsa_code_template_end -
+ Ent_dsa_zero);
+
+ printk("scsi%d : NCR code relocated to 0x%lx (virt 0x%p)\n", host->host_no,
+ virt_to_bus(hostdata->script), hostdata->script);
+}
+
+/*
+ * Function : static int NCR53c8xx_run_tests (struct Scsi_Host *host)
+ *
+ * Purpose : run various verification tests on the NCR chip,
+ * including interrupt generation, and proper bus mastering
+ * operation.
+ *
+ * Inputs : host - a properly initialized Scsi_Host structure
+ *
+ * Preconditions : the NCR chip must be in a halted state.
+ *
+ * Returns : 0 if all tests were successful, -1 on error.
+ *
+ */
+
+static int
+NCR53c8xx_run_tests (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long timeout;
+ u32 start;
+ int failed, i;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ /* The NCR chip _must_ be idle to run the test scripts */
+
+ save_flags(flags);
+ cli();
+ if (!hostdata->idle) {
+ printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+
+ /*
+ * Check for functional interrupts, this could work as an
+ * autoprobe routine.
+ */
+
+ if ((hostdata->options & OPTION_DEBUG_TEST1) &&
+ hostdata->state != STATE_DISABLED) {
+ hostdata->idle = 0;
+ hostdata->test_running = 1;
+ hostdata->test_completed = -1;
+ hostdata->test_dest = 0;
+ hostdata->test_source = 0xdeadbeef;
+ start = virt_to_bus (hostdata->script) + hostdata->E_test_1;
+ hostdata->state = STATE_RUNNING;
+ printk ("scsi%d : test 1", host->host_no);
+ NCR53c7x0_write32 (DSP_REG, start);
+ printk (" started\n");
+ sti();
+
+ /*
+ * This is currently a .5 second timeout, since (in theory) no slow
+ * board will take that long. In practice, we've seen one
+ * pentium which ocassionally fails with this, but works with
+ * 10 times as much?
+ */
+
+ timeout = jiffies + 5 * HZ / 10;
+ while ((hostdata->test_completed == -1) && jiffies < timeout)
+ barrier();
+
+ failed = 1;
+ if (hostdata->test_completed == -1)
+ printk ("scsi%d : driver test 1 timed out%s\n",host->host_no ,
+ (hostdata->test_dest == 0xdeadbeef) ?
+ " due to lost interrupt.\n"
+ " Please verify that the correct IRQ is being used for your board,\n"
+ " and that the motherboard IRQ jumpering matches the PCI setup on\n"
+ " PCI systems.\n"
+ " If you are using a NCR53c810 board in a PCI system, you should\n"
+ " also verify that the board is jumpered to use PCI INTA, since\n"
+ " most PCI motherboards lack support for INTB, INTC, and INTD.\n"
+ : "");
+ else if (hostdata->test_completed != 1)
+ printk ("scsi%d : test 1 bad interrupt value (%d)\n",
+ host->host_no, hostdata->test_completed);
+ else
+ failed = (hostdata->test_dest != 0xdeadbeef);
+
+ if (hostdata->test_dest != 0xdeadbeef) {
+ printk ("scsi%d : driver test 1 read 0x%x instead of 0xdeadbeef indicating a\n"
+ " probable cache invalidation problem. Please configure caching\n"
+ " as write-through or disabled\n",
+ host->host_no, hostdata->test_dest);
+ }
+
+ if (failed) {
+ printk ("scsi%d : DSP = 0x%p (script at 0x%p, start at 0x%x)\n",
+ host->host_no, bus_to_virt(NCR53c7x0_read32(DSP_REG)),
+ hostdata->script, start);
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no,
+ NCR53c7x0_read32(DSPS_REG));
+ restore_flags(flags);
+ return -1;
+ }
+ hostdata->test_running = 0;
+ }
+
+ if ((hostdata->options & OPTION_DEBUG_TEST2) &&
+ hostdata->state != STATE_DISABLED) {
+ u32 dsa[48];
+ unsigned char identify = IDENTIFY(0, 0);
+ unsigned char cmd[6];
+ unsigned char data[36];
+ unsigned char status = 0xff;
+ unsigned char msg = 0xff;
+
+ cmd[0] = INQUIRY;
+ cmd[1] = cmd[2] = cmd[3] = cmd[5] = 0;
+ cmd[4] = sizeof(data);
+
+ dsa[2] = 1;
+ dsa[3] = virt_to_bus(&identify);
+ dsa[4] = 6;
+ dsa[5] = virt_to_bus(&cmd);
+ dsa[6] = sizeof(data);
+ dsa[7] = virt_to_bus(&data);
+ dsa[8] = 1;
+ dsa[9] = virt_to_bus(&status);
+ dsa[10] = 1;
+ dsa[11] = virt_to_bus(&msg);
+
+ for (i = 0; i < 3; ++i) {
+ cli();
+ if (!hostdata->idle) {
+ printk ("scsi%d : chip not idle, aborting tests\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+
+ /* SCNTL3 SDID */
+ dsa[0] = (0x33 << 24) | (i << 16) ;
+ hostdata->idle = 0;
+ hostdata->test_running = 2;
+ hostdata->test_completed = -1;
+ start = virt_to_bus(hostdata->script) + hostdata->E_test_2;
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSA_REG, virt_to_bus(dsa));
+ NCR53c7x0_write32 (DSP_REG, start);
+ sti();
+
+ timeout = jiffies + 5 * HZ; /* arbitrary */
+ while ((hostdata->test_completed == -1) && jiffies < timeout)
+ barrier();
+ NCR53c7x0_write32 (DSA_REG, 0);
+
+ if (hostdata->test_completed == 2) {
+ data[35] = 0;
+ printk ("scsi%d : test 2 INQUIRY to target %d, lun 0 : %s\n",
+ host->host_no, i, data + 8);
+ printk ("scsi%d : status ", host->host_no);
+ print_status (status);
+ printk ("\nscsi%d : message ", host->host_no);
+ print_msg (&msg);
+ printk ("\n");
+ } else if (hostdata->test_completed == 3) {
+ printk("scsi%d : test 2 no connection with target %d\n",
+ host->host_no, i);
+ if (!hostdata->idle) {
+ printk("scsi%d : not idle\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+ } else if (hostdata->test_completed == -1) {
+ printk ("scsi%d : test 2 timed out\n", host->host_no);
+ restore_flags(flags);
+ return -1;
+ }
+ hostdata->test_running = 0;
+ }
+ }
+
+ restore_flags(flags);
+ return 0;
+}
+
+/*
+ * Function : static void NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : copy the NCR53c8xx dsa structure into cmd's dsa buffer,
+ * performing all necessary relocation.
+ *
+ * Inputs : cmd, a NCR53c7x0_cmd structure with a dsa area large
+ * enough to hold the NCR53c8xx dsa.
+ */
+
+static void
+NCR53c8xx_dsa_fixup (struct NCR53c7x0_cmd *cmd) {
+ Scsi_Cmnd *c = cmd->cmd;
+ struct Scsi_Host *host = c->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int i;
+
+ memcpy (cmd->dsa, hostdata->script + (hostdata->E_dsa_code_template / 4),
+ hostdata->E_dsa_code_template_end - hostdata->E_dsa_code_template);
+
+ /*
+ * Note : within the NCR 'C' code, dsa points to the _start_
+ * of the DSA structure, and _not_ the offset of dsa_zero within
+ * that structure used to facilitate shorter signed offsets
+ * for the 8 bit ALU.
+ *
+ * The implications of this are that
+ *
+ * - 32 bit A_dsa_* absolute values require an additional
+ * dsa_zero added to their value to be correct, since they are
+ * relative to dsa_zero which is in essentially a separate
+ * space from the code symbols.
+ *
+ * - All other symbols require no special treatment.
+ */
+
+ patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_lun, c->lun);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_next, virt_to_bus(&cmd->dsa_next_addr));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_next, virt_to_bus(cmd->dsa) + Ent_dsa_zero -
+ Ent_dsa_code_template + A_dsa_next);
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_sync, virt_to_bus((void *)hostdata->sync[c->target].script));
+ patch_abs_tci_data (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_target, c->target);
+ /* XXX - new pointer stuff */
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_saved_pointer, virt_to_bus(&cmd->saved_data_pointer));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_saved_residual, virt_to_bus(&cmd->saved_residual));
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_residual, virt_to_bus(&cmd->residual));
+
+ /* XXX - new start stuff */
+ patch_abs_32 (cmd->dsa, Ent_dsa_code_template / sizeof(u32),
+ dsa_temp_addr_dsa_value, virt_to_bus(&cmd->dsa_addr));
+
+}
+
+/*
+ * Function : run_process_issue_queue (void)
+ *
+ * Purpose : insure that the coroutine is running and will process our
+ * request. process_issue_queue_running is checked/set here (in an
+ * inline function) rather than in process_issue_queue itself to reduce
+ * the chances of stack overflow.
+ *
+ */
+
+static volatile int process_issue_queue_running = 0;
+
+static __inline__ void
+run_process_issue_queue(void) {
+ unsigned long flags;
+ save_flags (flags);
+ cli();
+ if (!process_issue_queue_running) {
+ process_issue_queue_running = 1;
+ process_issue_queue(flags);
+ /*
+ * process_issue_queue_running is cleared in process_issue_queue
+ * once it can't do more work, and process_issue_queue exits with
+ * interrupts disabled.
+ */
+ }
+ restore_flags (flags);
+}
+
+/*
+ * Function : static void abnormal_finished (struct NCR53c7x0_cmd *cmd, int
+ * result)
+ *
+ * Purpose : mark SCSI command as finished, OR'ing the host portion
+ * of the result word into the result field of the corresponding
+ * Scsi_Cmnd structure, and removing it from the internal queues.
+ *
+ * Inputs : cmd - command, result - entire result field
+ *
+ * Preconditions : the NCR chip should be in a halted state when
+ * abnormal_finished is run, since it modifies structures which
+ * the NCR expects to have exclusive access to.
+ */
+
+static void
+abnormal_finished (struct NCR53c7x0_cmd *cmd, int result) {
+ Scsi_Cmnd *c = cmd->cmd;
+ struct Scsi_Host *host = c->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ int left, found;
+ volatile struct NCR53c7x0_cmd * linux_search;
+ volatile struct NCR53c7x0_cmd * volatile *linux_prev;
+ volatile u32 *ncr_prev, *current, ncr_search;
+
+#if 0
+ printk ("scsi%d: abnormal finished\n", host->host_no);
+#endif
+
+ save_flags(flags);
+ cli();
+ found = 0;
+ /*
+ * Traverse the NCR issue array until we find a match or run out
+ * of instructions. Instructions in the NCR issue array are
+ * either JUMP or NOP instructions, which are 2 words in length.
+ */
+
+
+ for (found = 0, left = host->can_queue, current = hostdata->schedule;
+ left > 0; --left, current += 2)
+ {
+ if (issue_to_cmd (host, hostdata, (u32 *) current) == cmd)
+ {
+ current[0] = hostdata->NOP_insn;
+ current[1] = 0xdeadbeef;
+ ++found;
+ break;
+ }
+ }
+
+ /*
+ * Traverse the NCR reconnect list of DSA structures until we find
+ * a pointer to this dsa or have found too many command structures.
+ * We let prev point at the next field of the previous element or
+ * head of the list, so we don't do anything different for removing
+ * the head element.
+ */
+
+ for (left = host->can_queue,
+ ncr_search = hostdata->reconnect_dsa_head,
+ ncr_prev = &hostdata->reconnect_dsa_head;
+ left >= 0 && ncr_search &&
+ ((char*)bus_to_virt(ncr_search) + hostdata->dsa_start)
+ != (char *) cmd->dsa;
+ ncr_prev = (u32*) ((char*)bus_to_virt(ncr_search) +
+ hostdata->dsa_next), ncr_search = *ncr_prev, --left);
+
+ if (left < 0)
+ printk("scsi%d: loop detected in ncr reonncect list\n",
+ host->host_no);
+ else if (ncr_search)
+ if (found)
+ printk("scsi%d: scsi %ld in ncr issue array and reconnect lists\n",
+ host->host_no, c->pid);
+ else {
+ volatile u32 * next = (u32 *)
+ ((char *)bus_to_virt(ncr_search) + hostdata->dsa_next);
+ *ncr_prev = *next;
+/* If we're at the tail end of the issue queue, update that pointer too. */
+ found = 1;
+ }
+
+ /*
+ * Traverse the host running list until we find this command or discover
+ * we have too many elements, pointing linux_prev at the next field of the
+ * linux_previous element or head of the list, search at this element.
+ */
+
+ for (left = host->can_queue, linux_search = hostdata->running_list,
+ linux_prev = &hostdata->running_list;
+ left >= 0 && linux_search && linux_search != cmd;
+ linux_prev = &(linux_search->next),
+ linux_search = linux_search->next, --left);
+
+ if (left < 0)
+ printk ("scsi%d: loop detected in host running list for scsi pid %ld\n",
+ host->host_no, c->pid);
+ else if (linux_search) {
+ *linux_prev = linux_search->next;
+ --hostdata->busy[c->target][c->lun];
+ }
+
+ /* Return the NCR command structure to the free list */
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
+ c->host_scribble = NULL;
+
+ /* And return */
+ c->result = result;
+ c->scsi_done(c);
+
+ restore_flags(flags);
+ run_process_issue_queue();
+}
+
+/*
+ * Function : static void intr_break (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handler for breakpoint interrupts from a SCSI script
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ * cmd - pointer to the command (if any) dsa was pointing
+ * to.
+ *
+ */
+
+static void
+intr_break (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_break *bp;
+#if 0
+ Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+#endif
+ u32 *dsp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Find the break point corresponding to this address, and
+ * dump the appropriate debugging information to standard
+ * output.
+ */
+ save_flags(flags);
+ cli();
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ for (bp = hostdata->breakpoints; bp && bp->address != dsp;
+ bp = bp->next);
+ if (!bp)
+ panic("scsi%d : break point interrupt from %p with no breakpoint!",
+ host->host_no, dsp);
+
+ /*
+ * Configure the NCR chip for manual start mode, so that we can
+ * point the DSP register at the instruction that follows the
+ * INT int_debug_break instruction.
+ */
+
+ NCR53c7x0_write8 (hostdata->dmode,
+ NCR53c7x0_read8(hostdata->dmode)|DMODE_MAN);
+
+ /*
+ * And update the DSP register, using the size of the old
+ * instruction in bytes.
+ */
+
+ restore_flags(flags);
+}
+/*
+ * Function : static void print_synchronous (const char *prefix,
+ * const unsigned char *msg)
+ *
+ * Purpose : print a pretty, user and machine parsable representation
+ * of a SDTR message, including the "real" parameters, data
+ * clock so we can tell transfer rate at a glance.
+ *
+ * Inputs ; prefix - text to prepend, msg - SDTR message (5 bytes)
+ */
+
+static void
+print_synchronous (const char *prefix, const unsigned char *msg) {
+ if (msg[4]) {
+ int Hz = 1000000000 / (msg[3] * 4);
+ int integer = Hz / 1000000;
+ int fraction = (Hz - (integer * 1000000)) / 10000;
+ printk ("%speriod %dns offset %d %d.%02dMHz %s SCSI%s\n",
+ prefix, (int) msg[3] * 4, (int) msg[4], integer, fraction,
+ (((msg[3] * 4) < 200) ? "FAST" : "synchronous"),
+ (((msg[3] * 4) < 200) ? "-II" : ""));
+ } else
+ printk ("%sasynchronous SCSI\n", prefix);
+}
+
+/*
+ * Function : static void set_synchronous (struct Scsi_Host *host,
+ * int target, int sxfer, int scntl3, int now_connected)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and
+ * target with the given register values; in the indirect
+ * select operand, reselection script, and chip registers.
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ * sxfer and scntl3 - NCR registers. now_connected - if non-zero,
+ * we should reprogram the registers now too.
+ */
+
+static void
+set_synchronous (struct Scsi_Host *host, int target, int sxfer, int scntl3,
+ int now_connected) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 *script;
+ NCR53c7x0_local_setup(host);
+
+ /* These are eight bit registers */
+ sxfer &= 0xff;
+ scntl3 &= 0xff;
+
+ hostdata->sync[target].sxfer_sanity = sxfer;
+ hostdata->sync[target].scntl3_sanity = scntl3;
+
+/*
+ * HARD CODED : synchronous script is EIGHT words long. This
+ * must agree with 53c7.8xx.h
+ */
+
+ if ((hostdata->chip != 700) && (hostdata->chip != 70066)) {
+ hostdata->sync[target].select_indirect = (scntl3 << 24) |
+ (target << 16) | (sxfer << 8);
+
+ script = (u32 *) hostdata->sync[target].script;
+
+ /* XXX - add NCR53c7x0 code to reprogram SCF bits if we want to */
+ if ((hostdata->chip / 100) == 8) {
+ script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+ DCMD_RWRI_OP_MOVE) << 24) |
+ (SCNTL3_REG_800 << 16) | (scntl3 << 8);
+ script[1] = 0;
+ script += 2;
+ }
+
+ script[0] = ((DCMD_TYPE_RWRI | DCMD_RWRI_OPC_MODIFY |
+ DCMD_RWRI_OP_MOVE) << 24) |
+ (SXFER_REG << 16) | (sxfer << 8);
+ script[1] = 0;
+ script += 2;
+
+#ifdef DEBUG_SYNC_INTR
+ if (hostdata->options & OPTION_DEBUG_DISCONNECT) {
+ script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_INT) << 24) | DBC_TCI_TRUE;
+ script[1] = DEBUG_SYNC_INTR;
+ script += 2;
+ }
+#endif
+
+ script[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_RETURN) << 24) | DBC_TCI_TRUE;
+ script[1] = 0;
+ script += 2;
+ }
+
+ if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS)
+ printk ("scsi%d : target %d sync parameters are sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, target, sxfer, scntl3);
+
+ if (now_connected) {
+ if ((hostdata->chip / 100) == 8)
+ NCR53c7x0_write8(SCNTL3_REG_800, scntl3);
+ NCR53c7x0_write8(SXFER_REG, sxfer);
+ }
+}
+
+
+/*
+ * Function : static int asynchronous (struct Scsi_Host *host, int target)
+ *
+ * Purpose : reprogram between the selected SCSI Host adapter and target
+ * (assumed to be currently connected) for asynchronous transfers.
+ *
+ * Inputs : host - SCSI host structure, target - numeric target ID.
+ *
+ * Preconditions : the NCR chip should be in one of the halted states
+ */
+
+static void
+asynchronous (struct Scsi_Host *host, int target) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+ set_synchronous (host, target, /* no offset */ 0, hostdata->saved_scntl3,
+ 1);
+ printk ("scsi%d : setting target %d to asynchronous SCSI\n",
+ host->host_no, target);
+}
+
+/*
+ * XXX - do we want to go out of our way (ie, add extra code to selection
+ * in the NCR53c710/NCR53c720 script) to reprogram the synchronous
+ * conversion bits, or can we be content in just setting the
+ * sxfer bits?
+ */
+
+/* Table for NCR53c8xx synchronous values */
+static const struct {
+ int div; /* Total clock divisor * 10 */
+ unsigned char scf; /* */
+ unsigned char tp; /* 4 + tp = xferp divisor */
+} syncs[] = {
+/* div scf tp div scf tp div scf tp */
+ { 40, 1, 0}, { 50, 1, 1}, { 60, 1, 2},
+ { 70, 1, 3}, { 75, 2, 1}, { 80, 1, 4},
+ { 90, 1, 5}, { 100, 1, 6}, { 105, 2, 3},
+ { 110, 1, 7}, { 120, 2, 4}, { 135, 2, 5},
+ { 140, 3, 3}, { 150, 2, 6}, { 160, 3, 4},
+ { 165, 2, 7}, { 180, 3, 5}, { 200, 3, 6},
+ { 210, 4, 3}, { 220, 3, 7}, { 240, 4, 4},
+ { 270, 4, 5}, { 300, 4, 6}, { 330, 4, 7}
+};
+
+/*
+ * Function : static void synchronous (struct Scsi_Host *host, int target,
+ * char *msg)
+ *
+ * Purpose : reprogram transfers between the selected SCSI initiator and
+ * target for synchronous SCSI transfers such that the synchronous
+ * offset is less than that requested and period at least as long
+ * as that requested. Also modify *msg such that it contains
+ * an appropriate response.
+ *
+ * Inputs : host - NCR53c7,8xx SCSI host, target - number SCSI target id,
+ * msg - synchronous transfer request.
+ */
+
+
+static void
+synchronous (struct Scsi_Host *host, int target, char *msg) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int desire, divisor, i, limit;
+ unsigned char scntl3, sxfer;
+/* The diagnostic message fits on one line, even with max. width integers */
+ char buf[80];
+
+/* Desired transfer clock in Hz */
+ desire = 1000000000L / (msg[3] * 4);
+/* Scale the available SCSI clock by 10 so we get tenths */
+ divisor = (hostdata->scsi_clock * 10) / desire;
+
+/* NCR chips can handle at most an offset of 8 */
+ if (msg[4] > 8)
+ msg[4] = 8;
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : optimal synchronous divisor of %d.%01d\n",
+ host->host_no, divisor / 10, divisor % 10);
+
+ limit = (sizeof(syncs) / sizeof(syncs[0]) -1);
+ for (i = 0; (i < limit) && (divisor > syncs[i].div); ++i);
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : selected synchronous divisor of %d.%01d\n",
+ host->host_no, syncs[i].div / 10, syncs[i].div % 10);
+
+ msg[3] = ((1000000000L / hostdata->scsi_clock) * syncs[i].div / 10 / 4);
+
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk("scsi%d : selected synchronous period of %dns\n", host->host_no,
+ msg[3] * 4);
+
+ scntl3 = (hostdata->chip / 100 == 8) ? ((hostdata->saved_scntl3 &
+ ~SCNTL3_800_SCF_MASK) | (syncs[i].scf << SCNTL3_800_SCF_SHIFT)) : 0;
+ sxfer = (msg[4] << SXFER_MO_SHIFT) | ((syncs[i].tp) << SXFER_TP_SHIFT);
+ if (hostdata->options & OPTION_DEBUG_SDTR)
+ printk ("scsi%d : sxfer=0x%x scntl3=0x%x\n",
+ host->host_no, (int) sxfer, (int) scntl3);
+ set_synchronous (host, target, sxfer, scntl3, 1);
+ sprintf (buf, "scsi%d : setting target %d to ", host->host_no, target);
+ print_synchronous (buf, msg);
+}
+
+/*
+ * Function : static int NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handler for INT generated instructions for the
+ * NCR53c810/820 SCSI SCRIPT
+ *
+ * Inputs : host - pointer to this host adapter's structure,
+ * cmd - pointer to the command (if any) dsa was pointing
+ * to.
+ *
+ */
+
+static int
+NCR53c8x0_dstat_sir_intr (struct Scsi_Host *host, struct
+ NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ int print;
+ Scsi_Cmnd *c = cmd ? cmd->cmd : NULL;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 dsps,*dsp; /* Argument of the INT instruction */
+ NCR53c7x0_local_setup(host);
+ dsps = NCR53c7x0_read32(DSPS_REG);
+ dsp = (u32 *) bus_to_virt(NCR53c7x0_read32(DSP_REG));
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : DSPS = 0x%x\n", host->host_no, dsps);
+
+ switch (dsps) {
+ case A_int_msg_1:
+ print = 1;
+ switch (hostdata->msg_buf[0]) {
+ /*
+ * Unless we've initiated synchronous negotiation, I don't
+ * think that this should happen.
+ */
+ case MESSAGE_REJECT:
+ hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ if (cmd && (cmd->flags & CMD_FLAG_SDTR)) {
+ printk ("scsi%d : target %d rejected SDTR\n", host->host_no,
+ c->target);
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ asynchronous (host, c->target);
+ print = 0;
+ }
+ break;
+ case INITIATE_RECOVERY:
+ printk ("scsi%d : extended contingent allegiance not supported yet, rejecting\n",
+ host->host_no);
+ /* Fall through to default */
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ break;
+ default:
+ printk ("scsi%d : unsupported message, resjecting\n",
+ host->host_no);
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ }
+ if (print) {
+ printk ("scsi%d : received message", host->host_no);
+ if (c)
+ printk (" from target %d lun %d ", c->target, c->lun);
+ print_msg ((unsigned char *) hostdata->msg_buf);
+ printk("\n");
+ }
+
+ return SPECIFIC_INT_NOTHING;
+
+
+ case A_int_msg_sdtr:
+/*
+ * At this point, hostdata->msg_buf contains
+ * 0 EXTENDED MESSAGE
+ * 1 length
+ * 2 SDTR
+ * 3 period * 4ns
+ * 4 offset
+ */
+
+ if (cmd) {
+ char buf[80];
+ sprintf (buf, "scsi%d : target %d %s ", host->host_no, c->target,
+ (cmd->flags & CMD_FLAG_SDTR) ? "accepting" : "requesting");
+ print_synchronous (buf, (unsigned char *) hostdata->msg_buf);
+
+ /*
+ * Initiator initiated, won't happen unless synchronous
+ * transfers are enabled. If we get a SDTR message in
+ * response to our SDTR, we should program our parameters
+ * such that
+ * offset <= requested offset
+ * period >= requested period
+ */
+ if (cmd->flags & CMD_FLAG_SDTR) {
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ if (hostdata->msg_buf[4])
+ synchronous (host, c->target, (unsigned char *)
+ hostdata->msg_buf);
+ else
+ asynchronous (host, c->target);
+ hostdata->dsp = hostdata->script + hostdata->E_accept_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ } else {
+ if (hostdata->options & OPTION_SYNCHRONOUS) {
+ cmd->flags |= CMD_FLAG_DID_SDTR;
+ synchronous (host, c->target, (unsigned char *)
+ hostdata->msg_buf);
+ } else {
+ hostdata->msg_buf[4] = 0; /* 0 offset = async */
+ asynchronous (host, c->target);
+ }
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 0, 5);
+ patch_dsa_32 (cmd->dsa, dsa_msgout_other, 1, (u32)
+ virt_to_bus ((void *)&hostdata->msg_buf));
+ hostdata->dsp = hostdata->script +
+ hostdata->E_respond_message / sizeof(u32);
+ hostdata->dsp_changed = 1;
+ }
+ return SPECIFIC_INT_NOTHING;
+ }
+ /* Fall through to abort if we couldn't find a cmd, and
+ therefore a dsa structure to twiddle */
+ case A_int_msg_wdtr:
+ hostdata->dsp = hostdata->script + hostdata->E_reject_message /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_err_unexpected_phase:
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : unexpected phase\n", host->host_no);
+ return SPECIFIC_INT_ABORT;
+ case A_int_err_selected:
+ printk ("scsi%d : selected by target %d\n", host->host_no,
+ (int) NCR53c7x0_read8(SDID_REG_800) &7);
+ hostdata->dsp = hostdata->script + hostdata->E_target_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_err_unexpected_reselect:
+ printk ("scsi%d : unexpected reselect by target %d lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & 7,
+ hostdata->reselected_identify & 7);
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+/*
+ * Since contingent allegiance conditions are cleared by the next
+ * command issued to a target, we must issue a REQUEST SENSE
+ * command after receiving a CHECK CONDITION status, before
+ * another command is issued.
+ *
+ * Since this NCR53c7x0_cmd will be freed after use, we don't
+ * care if we step on the various fields, so modify a few things.
+ */
+ case A_int_err_check_condition:
+#if 0
+ if (hostdata->options & OPTION_DEBUG_INTR)
+#endif
+ printk ("scsi%d : CHECK CONDITION\n", host->host_no);
+ if (!c) {
+ printk("scsi%d : CHECK CONDITION with no SCSI command\n",
+ host->host_no);
+ return SPECIFIC_INT_PANIC;
+ }
+
+ /*
+ * FIXME : this uses the normal one-byte selection message.
+ * We may want to renegotiate for synchronous & WIDE transfers
+ * since these could be the crux of our problem.
+ *
+ hostdata->NOP_insn* FIXME : once SCSI-II tagged queuing is implemented, we'll
+ * have to set this up so that the rest of the DSA
+ * agrees with this being an untagged queue'd command.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_msgout, 0, 1);
+
+ /*
+ * Modify the table indirect for COMMAND OUT phase, since
+ * Request Sense is a six byte command.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_cmdout, 0, 6);
+
+ c->cmnd[0] = REQUEST_SENSE;
+ c->cmnd[1] &= 0xe0; /* Zero all but LUN */
+ c->cmnd[2] = 0;
+ c->cmnd[3] = 0;
+ c->cmnd[4] = sizeof(c->sense_buffer);
+ c->cmnd[5] = 0;
+
+ /*
+ * Disable dataout phase, and program datain to transfer to the
+ * sense buffer, and add a jump to other_transfer after the
+ * command so overflow/underrun conditions are detected.
+ */
+
+ patch_dsa_32 (cmd->dsa, dsa_dataout, 0,
+ virt_to_bus(hostdata->script) + hostdata->E_other_transfer);
+ patch_dsa_32 (cmd->dsa, dsa_datain, 0,
+ virt_to_bus(cmd->data_transfer_start));
+ cmd->data_transfer_start[0] = (((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I |
+ DCMD_BMI_IO)) << 24) | sizeof(c->sense_buffer);
+ cmd->data_transfer_start[1] = (u32) virt_to_bus(c->sense_buffer);
+
+ cmd->data_transfer_start[2] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE;
+ cmd->data_transfer_start[3] = (u32) virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+
+ /*
+ * Currently, this command is flagged as completed, ie
+ * it has valid status and message data. Reflag it as
+ * incomplete. Q - need to do something so that original
+ * status, etc are used.
+ */
+
+ cmd->cmd->result = 0xffff;
+
+ /*
+ * Restart command as a REQUEST SENSE.
+ */
+ hostdata->dsp = (u32 *) hostdata->script + hostdata->E_select /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ return SPECIFIC_INT_NOTHING;
+ case A_int_debug_break:
+ return SPECIFIC_INT_BREAK;
+ case A_int_norm_aborted:
+ hostdata->dsp = (u32 *) hostdata->schedule;
+ hostdata->dsp_changed = 1;
+ if (cmd)
+ abnormal_finished (cmd, DID_ERROR << 16);
+ return SPECIFIC_INT_NOTHING;
+ case A_int_test_1:
+ case A_int_test_2:
+ hostdata->idle = 1;
+ hostdata->test_completed = (dsps - A_int_test_1) / 0x00010000 + 1;
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk("scsi%d : test%d complete\n", host->host_no,
+ hostdata->test_completed);
+ return SPECIFIC_INT_NOTHING;
+#ifdef A_int_debug_reselected_ok
+ case A_int_debug_reselected_ok:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ u32 *dsa;
+ dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG));
+
+ printk("scsi%d : reselected_ok (DSA = 0x%x (virt 0x%p)\n",
+ host->host_no, NCR53c7x0_read32(DSA_REG), dsa);
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt(cmd->saved_data_pointer));
+ print_insn (host, hostdata->script + Ent_reselected_ok /
+ sizeof(u32), "", 1);
+ printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG),
+ NCR53c7x0_read8(SCNTL3_REG_800));
+ if (c) {
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script, "", 1);
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script + 2, "", 1);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_reselect_check
+ case A_int_debug_reselect_check:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ u32 *dsa;
+#if 0
+ u32 *code;
+#endif
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG));
+ printk("scsi%d : reselected_check_next (DSA = 0x%lx (virt 0x%p))\n",
+ host->host_no, virt_to_bus(dsa), dsa);
+ if (dsa) {
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer));
+#if 0
+ printk("scsi%d : template code :\n", host->host_no);
+ for (code = dsa + (Ent_dsa_code_check_reselect - Ent_dsa_zero)
+ / sizeof(u32); code < (dsa + Ent_dsa_zero / sizeof(u32));
+ code += print_insn (host, code, "", 1));
+#endif
+ }
+ print_insn (host, hostdata->script + Ent_reselected_ok /
+ sizeof(u32), "", 1);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_schedule
+ case A_int_debug_dsa_schedule:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ u32 *dsa;
+ /*
+ * Note - this dsa is not based on location relative to
+ * the command structure, but to location relative to the
+ * DSA register
+ */
+ dsa = (u32 *) bus_to_virt (NCR53c7x0_read32(DSA_REG));
+ printk("scsi%d : dsa_schedule (old DSA = 0x%lx (virt 0x%p))\n",
+ host->host_no, virt_to_bus(dsa), dsa);
+ if (dsa)
+ printk("scsi%d : resume address is 0x%x (virt 0x%p)\n"
+ " (temp was 0x%x (virt 0x%p))\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer),
+ NCR53c7x0_read32 (TEMP_REG),
+ bus_to_virt (NCR53c7x0_read32(TEMP_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_scheduled
+ case A_int_debug_scheduled:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : new I/O 0x%x (virt 0x%p) scheduled\n",
+ host->host_no, NCR53c7x0_read32(DSA_REG),
+ bus_to_virt(NCR53c7x0_read32(DSA_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_idle
+ case A_int_debug_idle:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : idle\n", host->host_no);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_cmd
+ case A_int_debug_cmd:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : command sent\n");
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_dsa_loaded
+ case A_int_debug_dsa_loaded:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ printk("scsi%d : DSA loaded with 0x%x (virt 0x%p)\n", host->host_no,
+ NCR53c7x0_read32(DSA_REG),
+ bus_to_virt(NCR53c7x0_read32(DSA_REG)));
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_reselected
+ case A_int_debug_reselected:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk("scsi%d : reselected by target %d lun %d\n",
+ host->host_no, (int) NCR53c7x0_read8(SDID_REG_800) & ~0x80,
+ (int) hostdata->reselected_identify & 7);
+ print_queues(host);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_disconnect_msg
+ case A_int_debug_disconnect_msg:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR)) {
+ if (c)
+ printk("scsi%d : target %d lun %d disconnecting\n",
+ host->host_no, c->target, c->lun);
+ else
+ printk("scsi%d : unknown target disconnecting\n",
+ host->host_no);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_disconnected
+ case A_int_debug_disconnected:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk ("scsi%d : disconnected, new queues are\n",
+ host->host_no);
+ print_queues(host);
+#if 0
+ printk ("scsi%d : sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, NCR53c7x0_read8(SXFER_REG),
+ NCR53c7x0_read8(SCNTL3_REG_800));
+#endif
+ if (c) {
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script, "", 1);
+ print_insn (host, (u32 *)
+ hostdata->sync[c->target].script + 2, "", 1);
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_panic
+ case A_int_debug_panic:
+ printk("scsi%d : int_debug_panic received\n", host->host_no);
+ print_lots (host);
+ return SPECIFIC_INT_PANIC;
+#endif
+#ifdef A_int_debug_saved
+ case A_int_debug_saved:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ printk ("scsi%d : saved data pointer 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer,
+ bus_to_virt (cmd->saved_data_pointer));
+ print_progress (c);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_restored
+ case A_int_debug_restored:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT)) {
+ if (cmd) {
+ int size;
+ printk ("scsi%d : restored data pointer 0x%x (virt 0x%p)\n",
+ host->host_no, cmd->saved_data_pointer, bus_to_virt (
+ cmd->saved_data_pointer));
+ size = print_insn (host, (u32 *)
+ bus_to_virt(cmd->saved_data_pointer), "", 1);
+ size = print_insn (host, (u32 *)
+ bus_to_virt(cmd->saved_data_pointer) + size, "", 1);
+ print_progress (c);
+ }
+#if 0
+ printk ("scsi%d : datapath residual %d\n",
+ host->host_no, datapath_residual (host)) ;
+#endif
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_sync
+ case A_int_debug_sync:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) {
+ unsigned char sxfer = NCR53c7x0_read8 (SXFER_REG),
+ scntl3 = NCR53c7x0_read8 (SCNTL3_REG_800);
+ if (c) {
+ if (sxfer != hostdata->sync[c->target].sxfer_sanity ||
+ scntl3 != hostdata->sync[c->target].scntl3_sanity) {
+ printk ("scsi%d : sync sanity check failed sxfer=0x%x, scntl3=0x%x",
+ host->host_no, sxfer, scntl3);
+ NCR53c7x0_write8 (SXFER_REG, sxfer);
+ NCR53c7x0_write8 (SCNTL3_REG_800, scntl3);
+ }
+ } else
+ printk ("scsi%d : unknown command sxfer=0x%x, scntl3=0x%x\n",
+ host->host_no, (int) sxfer, (int) scntl3);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+#ifdef A_int_debug_datain
+ case A_int_debug_datain:
+ if (hostdata->options & (OPTION_DEBUG_SCRIPT|OPTION_DEBUG_INTR|
+ OPTION_DEBUG_DISCONNECT|OPTION_DEBUG_SDTR)) {
+ int size;
+ printk ("scsi%d : In do_datain (%s) sxfer=0x%x, scntl3=0x%x\n"
+ " datapath residual=%d\n",
+ host->host_no, sbcl_to_phase (NCR53c7x0_read8 (SBCL_REG)),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ (int) NCR53c7x0_read8(SCNTL3_REG_800),
+ datapath_residual (host)) ;
+ print_insn (host, dsp, "", 1);
+ size = print_insn (host, (u32 *) bus_to_virt(dsp[1]), "", 1);
+ print_insn (host, (u32 *) bus_to_virt(dsp[1]) + size, "", 1);
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+/*
+ * FIXME : for 7xx support, we need to read SDID_REG_700 and handle
+ * the comparison as bitfielded, not binary.
+ */
+#ifdef A_int_debug_check_dsa
+ case A_int_debug_check_dsa:
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ int sdid = NCR53c7x0_read8 (SDID_REG_800) & 15;
+ char *where = dsp - NCR53c7x0_insn_size(NCR53c7x0_read8
+ (DCMD_REG)) == hostdata->script +
+ Ent_select_check_dsa / sizeof(u32) ?
+ "selection" : "reselection";
+ if (c && sdid != c->target) {
+ printk ("scsi%d : SDID target %d != DSA target %d at %s\n",
+ host->host_no, sdid, c->target, where);
+ print_lots(host);
+ dump_events (host, 20);
+ return SPECIFIC_INT_PANIC;
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+#endif
+ default:
+ if ((dsps & 0xff000000) == 0x03000000) {
+ printk ("scsi%d : misc debug interrupt 0x%x\n",
+ host->host_no, dsps);
+ return SPECIFIC_INT_RESTART;
+ } else if ((dsps & 0xff000000) == 0x05000000) {
+ if (hostdata->events) {
+ struct NCR53c7x0_event *event;
+ ++hostdata->event_index;
+ if (hostdata->event_index >= hostdata->event_size)
+ hostdata->event_index = 0;
+ event = (struct NCR53c7x0_event *) hostdata->events +
+ hostdata->event_index;
+ event->event = (enum ncr_event) dsps;
+ event->dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ /* FIXME : this needs to change for the '7xx family */
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON)
+ event->target = NCR53c7x0_read8(SSID_REG_800);
+ else
+ event->target = 255;
+
+ if (event->event == EVENT_RESELECT)
+ event->lun = hostdata->reselected_identify & 0xf;
+ else if (c)
+ event->lun = c->lun;
+ else
+ event->lun = 255;
+ do_gettimeofday(&(event->time));
+ if (c) {
+ event->pid = c->pid;
+ memcpy ((void *) event->cmnd, (void *) c->cmnd,
+ sizeof (event->cmnd));
+ } else {
+ event->pid = -1;
+ }
+ }
+ return SPECIFIC_INT_RESTART;
+ }
+
+ printk ("scsi%d : unknown user interrupt 0x%x\n",
+ host->host_no, (unsigned) dsps);
+ return SPECIFIC_INT_PANIC;
+ }
+}
+
+/*
+ * XXX - the stock NCR assembler won't output the scriptu.h file,
+ * which undefine's all #define'd CPP symbols from the script.h
+ * file, which will create problems if you use multiple scripts
+ * with the same symbol names.
+ *
+ * If you insist on using NCR's assembler, you could generate
+ * scriptu.h from script.h using something like
+ *
+ * grep #define script.h | \
+ * sed 's/#define[ ][ ]*\([_a-zA-Z][_a-zA-Z0-9]*\).*$/#undefine \1/' \
+ * > scriptu.h
+ */
+
+#include "53c8xx_u.h"
+
+/* XXX - add alternate script handling code here */
+
+
+#ifdef NCR_DEBUG
+/*
+ * Debugging without a debugger is no fun. So, I've provided
+ * a debugging interface in the NCR53c7x0 driver. To avoid
+ * kernel cruft, there's just enough here to act as an interface
+ * to a user level debugger (aka, GDB).
+ *
+ *
+ * The following restrictions apply to debugger commands :
+ * 1. The command must be terminated by a newline.
+ * 2. Command length must be less than 80 bytes including the
+ * newline.
+ * 3. The entire command must be written with one system call.
+ */
+
+static const char debugger_help =
+"bc <addr> - clear breakpoint\n"
+"bl - list breakpoints\n"
+"bs <addr> - set breakpoint\n"
+"g - start\n"
+"h - halt\n"
+"? - this message\n"
+"i - info\n"
+"mp <addr> <size> - print memory\n"
+"ms <addr> <size> <value> - store memory\n"
+"rp <num> <size> - print register\n"
+"rs <num> <size> <value> - store register\n"
+"s - single step\n"
+"tb - begin trace \n"
+"te - end trace\n";
+
+/*
+ * Whenever we change a break point, we should probably
+ * set the NCR up so that it is in a single step mode.
+ */
+
+static int debugger_fn_bc (struct Scsi_Host *host, struct debugger_token *token,
+ u32 args[]) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ instance->hostdata;
+ struct NCR53c7x0_break *bp, **prev;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ for (bp = (struct NCR53c7x0_break *) instance->breakpoints,
+ prev = (struct NCR53c7x0_break **) &instance->breakpoints;
+ bp; prev = (struct NCR53c7x0_break **) &(bp->next),
+ bp = (struct NCR53c7x0_break *) bp->next);
+
+ if (!bp) {
+ restore_flags(flags);
+ return -EIO;
+ }
+
+ /*
+ * XXX - we need to insure that the processor is halted
+ * here in order to prevent a race condition.
+ */
+
+ memcpy ((void *) bp->addr, (void *) bp->old, sizeof(bp->old));
+ if (prev)
+ *prev = bp->next;
+
+ restore_flags(flags);
+ return 0;
+}
+
+
+static int
+debugger_fn_bl (struct Scsi_Host *host, struct debugger_token *token,
+ u32 args[]) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_break *bp;
+ char buf[80];
+ size_t len;
+ unsigned long flags;
+ /*
+ * XXX - we need to insure that the processor is halted
+ * here in order to prevent a race condition. So, if the
+ * processor isn't halted, print an error message and continue.
+ */
+
+ sprintf (buf, "scsi%d : bp : warning : processor not halted\b",
+ host->host_no);
+ debugger_kernel_write (host, buf, strlen(buf));
+
+ save_flags(flags);
+ cli();
+ for (bp = (struct NCR53c7x0_break *) host->breakpoints;
+ bp; bp = (struct NCR53c7x0_break *) bp->next); {
+ sprintf (buf, "scsi%d : bp : success : at %08x, replaces %08x %08x",
+ bp->addr, bp->old[0], bp->old[1]);
+ len = strlen(buf);
+ if ((bp->old[0] & (DCMD_TYPE_MASK << 24)) ==
+ (DCMD_TYPE_MMI << 24)) {
+ sprintf(buf + len, "%08x\n", * (u32 *) bp->addr);
+ } else {
+ sprintf(buf + len, "\n");
+ }
+ len = strlen(buf);
+ debugger_kernel_write (host, buf, len);
+ }
+ restore_flags(flags);
+ return 0;
+}
+
+static int
+debugger_fn_bs (struct Scsi_Host *host, struct debugger_token *token,
+ u32 args[]) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_break *bp;
+ char buf[80];
+ size_t len;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+
+ if (hostdata->state != STATE_HALTED) {
+ sprintf (buf, "scsi%d : bs : failure : NCR not halted\n", host->host_no);
+ debugger_kernel_write (host, buf, strlen(buf));
+ restore_flags(flags);
+ return -1;
+ }
+
+ if (!(bp = kmalloc (sizeof (struct NCR53c7x0_break)))) {
+ printk ("scsi%d : kmalloc(%d) of breakpoint structure failed, try again\n",
+ host->host_no, sizeof(struct NCR53c7x0_break));
+ restore_flags(flags);
+ return -1;
+ }
+
+ bp->address = (u32 *) args[0];
+ memcpy ((void *) bp->old_instruction, (void *) bp->address, 8);
+ bp->old_size = (((bp->old_instruction[0] >> 24) & DCMD_TYPE_MASK) ==
+ DCMD_TYPE_MMI ? 3 : 2;
+ bp->next = hostdata->breakpoints;
+ hostdata->breakpoints = bp->next;
+ memcpy ((void *) bp->address, (void *) hostdata->E_debug_break, 8);
+
+ restore_flags(flags);
+ return 0;
+}
+
+#define TOKEN(name,nargs) {#name, nargs, debugger_fn_##name}
+static const struct debugger_token {
+ char *name;
+ int numargs;
+ int (*fn)(struct debugger_token *token, u32 args[]);
+} debugger_tokens[] = {
+ TOKEN(bc,1), TOKEN(bl,0), TOKEN(bs,1), TOKEN(g,0), TOKEN(halt,0),
+ {DT_help, "?", 0} , TOKEN(h,0), TOKEN(i,0), TOKEN(mp,2),
+ TOKEN(ms,3), TOKEN(rp,2), TOKEN(rs,2), TOKEN(s,0), TOKEN(tb,0), TOKEN(te,0)
+};
+
+#define NDT sizeof(debugger_tokens / sizeof(struct debugger_token))
+
+static struct Scsi_Host * inode_to_host (struct inode *inode) {
+ int dev;
+ struct Scsi_Host *tmp;
+ for (dev = MINOR(inode->rdev), host = first_host;
+ (host->hostt == the_template); --dev, host = host->next)
+ if (!dev) return host;
+ return NULL;
+}
+
+
+static int
+debugger_user_write (struct inode *inode,struct file *filp,
+ char *buf,int count) {
+ struct Scsi_Host *host; /* This SCSI host */
+ struct NCR53c7x0_hostadata *hostdata;
+ char input_buf[80], /* Kernel space copy of buf */
+ *ptr; /* Pointer to argument list */
+ u32 args[3]; /* Arguments */
+ int i, j, error, len;
+
+ if (!(host = inode_to_host(inode)))
+ return -ENXIO;
+
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+
+ if (error = verify_area(VERIFY_READ,buf,count))
+ return error;
+
+ if (count > 80)
+ return -EIO;
+
+ memcpy_from_fs(input_buf, buf, count);
+
+ if (input_buf[count - 1] != '\n')
+ return -EIO;
+
+ input_buf[count - 1]=0;
+
+ for (i = 0; i < NDT; ++i) {
+ len = strlen (debugger_tokens[i].name);
+ if (!strncmp(input_buf, debugger_tokens[i].name, len))
+ break;
+ };
+
+ if (i == NDT)
+ return -EIO;
+
+ for (ptr = input_buf + len, j = 0; j < debugger_tokens[i].nargs && *ptr;) {
+ if (*ptr == ' ' || *ptr == '\t') {
+ ++ptr;
+ } else if (isdigit(*ptr)) {
+ args[j++] = simple_strtoul (ptr, &ptr, 0);
+ } else {
+ return -EIO;
+ }
+ }
+
+ if (j != debugger_tokens[i].nargs)
+ return -EIO;
+
+ return count;
+}
+
+static int
+debugger_user_read (struct inode *inode,struct file *filp,
+ char *buf,int count) {
+ struct Scsi_Host *instance;
+
+}
+
+static int
+debugger_kernel_write (struct Scsi_Host *host, char *buf, size_t
+ buflen) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int copy, left;
+ unsigned long flags;
+ save_flags(flags);
+ cli();
+ while (buflen) {
+ left = (hostdata->debug_buf + hostdata->debug_size - 1) -
+ hostdata->debug_write;
+ copy = (buflen <= left) ? buflen : left;
+ memcpy (hostdata->debug_write, buf, copy);
+ buf += copy;
+ buflen -= copy;
+ hostdata->debug_count += copy;
+ if ((hostdata->debug_write += copy) ==
+ (hostdata->debug_buf + hostdata->debug_size))
+ hosdata->debug_write = hostdata->debug_buf;
+ }
+ restore_flags(flags);
+}
+
+#endif /* def NCRDEBUG */
+
+/*
+ * Function : static void NCR538xx_soft_reset (struct Scsi_Host *host)
+ *
+ * Purpose : perform a soft reset of the NCR53c8xx chip
+ *
+ * Inputs : host - pointer to this host adapter's structure
+ *
+ * Preconditions : NCR53c7x0_init must have been called for this
+ * host.
+ *
+ */
+
+static void
+NCR53c8x0_soft_reset (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+
+
+ /*
+ * Do a soft reset of the chip so that everything is
+ * reinitialized to the power-on state.
+ *
+ * Basically follow the procedure outlined in the NCR53c700
+ * data manual under Chapter Six, How to Use, Steps Necessary to
+ * Start SCRIPTS, with the exception of actually starting the
+ * script and setting up the synchronous transfer gunk.
+ */
+
+ NCR53c7x0_write8(ISTAT_REG_800, ISTAT_10_SRST);
+ NCR53c7x0_write8(ISTAT_REG_800, 0);
+ NCR53c7x0_write8(hostdata->dmode, hostdata->saved_dmode & ~DMODE_MAN);
+
+
+ /*
+ * Respond to reselection by targets and use our _initiator_ SCSI ID
+ * for arbitration. If notyet, also respond to SCSI selection.
+ *
+ * XXX - Note : we must reprogram this when reselecting as
+ * a target.
+ */
+
+#ifdef notyet
+ NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE|SCID_800_SRE);
+#else
+ NCR53c7x0_write8(SCID_REG, (host->this_id & 7)|SCID_800_RRE);
+#endif
+ NCR53c7x0_write8(RESPID_REG_800, hostdata->this_id_mask);
+
+ /*
+ * Use a maximum (1.6) second handshake to handshake timeout,
+ * and SCSI recommended .5s selection timeout.
+ */
+
+ /*
+ * The new gcc won't recognize preprocessing directives
+ * within macro args.
+ */
+#if 0
+ NCR53c7x0_write8(STIME0_REG_800,
+ ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK)
+ | ((15 << STIME0_800_HTH_SHIFT) & STIME0_800_HTH_MASK));
+#else
+/* Disable HTH interrupt */
+ NCR53c7x0_write8(STIME0_REG_800,
+ ((selection_timeout << STIME0_800_SEL_SHIFT) & STIME0_800_SEL_MASK));
+#endif
+
+
+ /*
+ * Enable active negation for happy synchronous transfers.
+ */
+
+ NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE);
+
+ /*
+ * Enable all interrupts, except parity which we only want when
+ * the user requests it.
+ */
+
+ NCR53c7x0_write8(DIEN_REG, DIEN_800_MDPE | DIEN_800_BF |
+ DIEN_ABRT | DIEN_SSI | DIEN_SIR | DIEN_800_IID);
+
+
+ NCR53c7x0_write8(SIEN0_REG_800, ((hostdata->options & OPTION_PARITY) ?
+ SIEN_PAR : 0) | SIEN_RST | SIEN_UDC | SIEN_SGE | SIEN_MA);
+ NCR53c7x0_write8(SIEN1_REG_800, SIEN1_800_STO | SIEN1_800_HTH);
+
+ /*
+ * Use saved clock frequency divisor and scripts loaded in 16 bit
+ * mode flags from the saved dcntl.
+ */
+
+ NCR53c7x0_write8(DCNTL_REG, hostdata->saved_dcntl);
+ NCR53c7x0_write8(CTEST4_REG_800, hostdata->saved_ctest4);
+
+ /* Enable active negation */
+ NCR53c7x0_write8(STEST3_REG_800, STEST3_800_TE);
+}
+
+/*
+ * Function static struct NCR53c7x0_cmd *allocate_cmd (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Return the first free NCR53c7x0_cmd structure (which are
+ * reused in a LIFO maner to minimize cache thrashing).
+ *
+ * Side effects : If we haven't yet scheduled allocation of NCR53c7x0_cmd
+ * structures for this device, do so. Attempt to complete all scheduled
+ * allocations using kmalloc(), putting NCR53c7x0_cmd structures on
+ * the free list. Teach programmers not to drink and hack.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure allocated on behalf of cmd;
+ * NULL on failure.
+ */
+
+static struct NCR53c7x0_cmd *
+allocate_cmd (Scsi_Cmnd *cmd) {
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ void *real; /* Real address */
+ int size; /* Size of *tmp */
+ struct NCR53c7x0_cmd *tmp;
+ unsigned long flags;
+
+ if (hostdata->options & OPTION_DEBUG_ALLOCATION)
+ printk ("scsi%d : num_cmds = %d, can_queue = %d\n"
+ " target = %d, lun = %d, %s\n",
+ host->host_no, hostdata->num_cmds, host->can_queue,
+ cmd->target, cmd->lun, (hostdata->cmd_allocated[cmd->target] &
+ (1 << cmd->lun)) ? "allready allocated" : "not allocated");
+
+/*
+ * If we have not yet reserved commands for this I_T_L nexus, and
+ * the device exists (as indicated by permanant Scsi_Cmnd structures
+ * being allocated under 1.3.x, or being outside of scan_scsis in
+ * 1.2.x), do so now.
+ */
+ if (!(hostdata->cmd_allocated[cmd->target] & (1 << cmd->lun)) &&
+#ifdef LINUX_1_2
+ !in_scan_scsis
+#else
+ cmd->device && cmd->device->has_cmdblocks
+#endif
+ ) {
+ if ((hostdata->extra_allocate + hostdata->num_cmds) < host->can_queue)
+ hostdata->extra_allocate += host->cmd_per_lun;
+ hostdata->cmd_allocated[cmd->target] |= (1 << cmd->lun);
+ }
+
+ for (; hostdata->extra_allocate > 0 ; --hostdata->extra_allocate,
+ ++hostdata->num_cmds) {
+ /* historically, kmalloc has returned unaligned addresses; pad so we
+ have enough room to ROUNDUP */
+ size = hostdata->max_cmd_size + sizeof (void *);
+/* FIXME: for ISA bus '7xx chips, we need to or GFP_DMA in here */
+ real = kmalloc (size, GFP_ATOMIC);
+ if (!real) {
+ if (hostdata->options & OPTION_DEBUG_ALLOCATION)
+ printk ("scsi%d : kmalloc(%d) failed\n",
+ host->host_no, size);
+ break;
+ }
+ tmp = ROUNDUP(real, void *);
+ tmp->real = real;
+ tmp->size = size;
+#ifdef LINUX_1_2
+ tmp->free = ((void (*)(void *, int)) kfree_s);
+#else
+ tmp->free = ((void (*)(void *, int)) kfree);
+#endif
+ save_flags (flags);
+ cli();
+ tmp->next = hostdata->free;
+ hostdata->free = tmp;
+ restore_flags (flags);
+ }
+ save_flags(flags);
+ cli();
+ tmp = (struct NCR53c7x0_cmd *) hostdata->free;
+ if (tmp) {
+ hostdata->free = tmp->next;
+ }
+ restore_flags(flags);
+ if (!tmp)
+ printk ("scsi%d : can't allocate command for target %d lun %d\n",
+ host->host_no, cmd->target, cmd->lun);
+ return tmp;
+}
+
+/*
+ * Function static struct NCR53c7x0_cmd *create_cmd (Scsi_Cmnd *cmd)
+ *
+ *
+ * Purpose : allocate a NCR53c7x0_cmd structure, initialize it based on the
+ * Scsi_Cmnd structure passed in cmd, including dsa and Linux field
+ * initialization, and dsa code relocation.
+ *
+ * Inputs : cmd - SCSI command
+ *
+ * Returns : NCR53c7x0_cmd structure corresponding to cmd,
+ * NULL on failure.
+ */
+
+static struct NCR53c7x0_cmd *
+create_cmd (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_cmd *tmp; /* NCR53c7x0_cmd structure for this command */
+ int datain, /* Number of instructions per phase */
+ dataout;
+ int data_transfer_instructions, /* Count of dynamic instructions */
+ i; /* Counter */
+ u32 *cmd_datain, /* Address of datain/dataout code */
+ *cmd_dataout; /* Incremented as we assemble */
+#ifdef notyet
+ unsigned char *msgptr; /* Current byte in select message */
+ int msglen; /* Length of whole select message */
+#endif
+ unsigned long flags;
+ NCR53c7x0_local_setup(cmd->host);
+
+ if (!(tmp = allocate_cmd (cmd)))
+ return NULL;
+
+
+ /*
+ * Decide whether we need to generate commands for DATA IN,
+ * DATA OUT, neither, or both based on the SCSI command
+ */
+
+ switch (cmd->cmnd[0]) {
+ /* These commands do DATA IN */
+ case INQUIRY:
+ case MODE_SENSE:
+ case READ_6:
+ case READ_10:
+ case READ_CAPACITY:
+ case REQUEST_SENSE:
+ datain = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+ dataout = 0;
+ break;
+ /* These commands do DATA OUT */
+ case MODE_SELECT:
+ case WRITE_6:
+ case WRITE_10:
+#if 0
+ printk("scsi%d : command is ", host->host_no);
+ print_command(cmd->cmnd);
+#endif
+#if 0
+ printk ("scsi%d : %d scatter/gather segments\n", host->host_no,
+ cmd->use_sg);
+#endif
+ datain = 0;
+ dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+#if 0
+ hostdata->options |= OPTION_DEBUG_INTR;
+#endif
+ break;
+ /*
+ * These commands do no data transfer, we should force an
+ * interrupt if a data phase is attempted on them.
+ */
+ case START_STOP:
+ case TEST_UNIT_READY:
+ datain = dataout = 0;
+ break;
+ /*
+ * We don't know about these commands, so generate code to handle
+ * both DATA IN and DATA OUT phases.
+ */
+ default:
+ datain = dataout = 2 * (cmd->use_sg ? cmd->use_sg : 1) + 3;
+ }
+
+ /*
+ * New code : so that active pointers work correctly irregardless
+ * of where the saved data pointer is at, we want to immediately
+ * enter the dynamic code after selection, and on a non-data
+ * phase perform a CALL to the non-data phase handler, with
+ * returns back to this address.
+ *
+ * If a phase mismatch is encountered in the middle of a
+ * Block MOVE instruction, we want to _leave_ that instruction
+ * unchanged as the current case is, modify a temporary buffer,
+ * and point the active pointer (TEMP) at that.
+ *
+ * Furthermore, we want to implement a saved data pointer,
+ * set by the SAVE_DATA_POINTERs message.
+ *
+ * So, the data transfer segments will change to
+ * CALL data_transfer, WHEN NOT data phase
+ * MOVE x, x, WHEN data phase
+ * ( repeat )
+ * JUMP other_transfer
+ */
+
+ data_transfer_instructions = datain + dataout;
+
+ /*
+ * When we perform a request sense, we overwrite various things,
+ * including the data transfer code. Make sure we have enough
+ * space to do that.
+ */
+
+ if (data_transfer_instructions < 2)
+ data_transfer_instructions = 2;
+
+
+ /*
+ * The saved data pointer is set up so that a RESTORE POINTERS message
+ * will start the data transfer over at the beggining.
+ */
+
+ tmp->saved_data_pointer = virt_to_bus (hostdata->script) +
+ hostdata->E_data_transfer;
+
+ /*
+ * Initialize Linux specific fields.
+ */
+
+ tmp->cmd = cmd;
+ tmp->next = NULL;
+ tmp->flags = 0;
+ tmp->dsa_next_addr = virt_to_bus(tmp->dsa) + hostdata->dsa_next -
+ hostdata->dsa_start;
+ tmp->dsa_addr = virt_to_bus(tmp->dsa) - hostdata->dsa_start;
+
+ /*
+ * Calculate addresses of dynamic code to fill in DSA
+ */
+
+ tmp->data_transfer_start = tmp->dsa + (hostdata->dsa_end -
+ hostdata->dsa_start) / sizeof(u32);
+ tmp->data_transfer_end = tmp->data_transfer_start +
+ 2 * data_transfer_instructions;
+
+ cmd_datain = datain ? tmp->data_transfer_start : NULL;
+ cmd_dataout = dataout ? (datain ? cmd_datain + 2 * datain : tmp->
+ data_transfer_start) : NULL;
+
+ /*
+ * Fill in the NCR53c7x0_cmd structure as follows
+ * dsa, with fixed up DSA code
+ * datain code
+ * dataout code
+ */
+
+ /* Copy template code into dsa and perform all necessary fixups */
+ if (hostdata->dsa_fixup)
+ hostdata->dsa_fixup(tmp);
+
+ patch_dsa_32(tmp->dsa, dsa_next, 0, 0);
+ patch_dsa_32(tmp->dsa, dsa_cmnd, 0, virt_to_bus(cmd));
+
+ if (hostdata->options & OPTION_DEBUG_SYNCHRONOUS)
+ if (hostdata->sync[cmd->target].select_indirect !=
+ ((hostdata->sync[cmd->target].scntl3_sanity << 24) |
+ (cmd->target << 16) |
+ (hostdata->sync[cmd->target].sxfer_sanity << 8))) {
+ printk ("scsi%d : sanity check failed select_indirect=0x%x\n",
+ host->host_no, hostdata->sync[cmd->target].select_indirect);
+ FATAL(host);
+
+ }
+
+ patch_dsa_32(tmp->dsa, dsa_select, 0, hostdata->sync[cmd->target].
+ select_indirect);
+ /*
+ * Right now, we'll do the WIDE and SYNCHRONOUS negotiations on
+ * different commands; although it should be trivial to do them
+ * both at the same time.
+ */
+ if (hostdata->initiate_wdtr & (1 << cmd->target)) {
+ memcpy ((void *) (tmp->select + 1), (void *) wdtr_message,
+ sizeof(wdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(wdtr_message));
+ save_flags(flags);
+ cli();
+ hostdata->initiate_wdtr &= ~(1 << cmd->target);
+ restore_flags(flags);
+ } else if (hostdata->initiate_sdtr & (1 << cmd->target)) {
+ memcpy ((void *) (tmp->select + 1), (void *) sdtr_message,
+ sizeof(sdtr_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(sdtr_message));
+ tmp->flags |= CMD_FLAG_SDTR;
+ save_flags(flags);
+ cli();
+ hostdata->initiate_sdtr &= ~(1 << cmd->target);
+ restore_flags(flags);
+
+ }
+#if 1
+ else if (!(hostdata->talked_to & (1 << cmd->target)) &&
+ !(hostdata->options & OPTION_NO_ASYNC)) {
+ memcpy ((void *) (tmp->select + 1), (void *) async_message,
+ sizeof(async_message));
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1 + sizeof(async_message));
+ tmp->flags |= CMD_FLAG_SDTR;
+ }
+#endif
+ else
+ patch_dsa_32(tmp->dsa, dsa_msgout, 0, 1);
+ hostdata->talked_to |= (1 << cmd->target);
+ tmp->select[0] = (hostdata->options & OPTION_DISCONNECT) ?
+ IDENTIFY (1, cmd->lun) : IDENTIFY (0, cmd->lun);
+ patch_dsa_32(tmp->dsa, dsa_msgout, 1, virt_to_bus(tmp->select));
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 0, cmd->cmd_len);
+ patch_dsa_32(tmp->dsa, dsa_cmdout, 1, virt_to_bus(cmd->cmnd));
+ patch_dsa_32(tmp->dsa, dsa_dataout, 0, cmd_dataout ?
+ virt_to_bus (cmd_dataout)
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ patch_dsa_32(tmp->dsa, dsa_datain, 0, cmd_datain ?
+ virt_to_bus (cmd_datain)
+ : virt_to_bus (hostdata->script) + hostdata->E_other_transfer);
+ /*
+ * XXX - need to make endian aware, should use separate variables
+ * for both status and message bytes.
+ */
+ patch_dsa_32(tmp->dsa, dsa_msgin, 0, 1);
+/*
+ * FIXME : these only works for little endian. We probably want to
+ * provide message and status fields in the NCR53c7x0_cmd
+ * structure, and assign them to cmd->result when we're done.
+ */
+ patch_dsa_32(tmp->dsa, dsa_msgin, 1, virt_to_bus(&cmd->result) + 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_status, 1, virt_to_bus(&cmd->result));
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 0, 1);
+ patch_dsa_32(tmp->dsa, dsa_msgout_other, 1,
+ virt_to_bus(&(hostdata->NCR53c7xx_msg_nop)));
+
+ /*
+ * Generate code for zero or more of the DATA IN, DATA OUT phases
+ * in the format
+ *
+ * CALL data_transfer, WHEN NOT phase
+ * MOVE first buffer length, first buffer address, WHEN phase
+ * ...
+ * MOVE last buffer length, last buffer address, WHEN phase
+ * JUMP other_transfer
+ */
+
+/*
+ * See if we're getting to data transfer by generating an unconditional
+ * interrupt.
+ */
+#if 0
+ if (datain) {
+ cmd_datain[0] = 0x98080000;
+ cmd_datain[1] = 0x03ffd00d;
+ cmd_datain += 2;
+ }
+#endif
+
+/*
+ * XXX - I'm undecided whether all of this nonsense is faster
+ * in the long run, or whether I should just go and implement a loop
+ * on the NCR chip using table indirect mode?
+ *
+ * In any case, this is how it _must_ be done for 53c700/700-66 chips,
+ * so this stays even when we come up with something better.
+ *
+ * When we're limited to 1 simultaneous command, no overlapping processing,
+ * we're seeing 630K/sec, with 7% CPU usage on a slow Syquest 45M
+ * drive.
+ *
+ * Not bad, not good. We'll see.
+ */
+
+ for (i = 0; cmd->use_sg ? (i < cmd->use_sg) : !i; cmd_datain += 4,
+ cmd_dataout += 4, ++i) {
+ u32 buf = cmd->use_sg ?
+ virt_to_bus(((struct scatterlist *)cmd->buffer)[i].address) :
+ virt_to_bus(cmd->request_buffer);
+ u32 count = cmd->use_sg ?
+ ((struct scatterlist *)cmd->buffer)[i].length :
+ cmd->request_bufflen;
+
+ if (datain) {
+ /* CALL other_in, WHEN NOT DATA_IN */
+ cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ DCMD_TCI_IO) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd_datain[1] = virt_to_bus (hostdata->script) +
+ hostdata->E_other_in;
+ /* MOVE count, buf, WHEN DATA_IN */
+ cmd_datain[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I | DCMD_BMI_IO)
+ << 24) | count;
+ cmd_datain[3] = buf;
+#if 0
+ print_insn (host, cmd_datain, "dynamic ", 1);
+ print_insn (host, cmd_datain + 2, "dynamic ", 1);
+#endif
+ }
+ if (dataout) {
+ /* CALL other_out, WHEN NOT DATA_OUT */
+ cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_out;
+ /* MOVE count, buf, WHEN DATA+OUT */
+ cmd_dataout[2] = ((DCMD_TYPE_BMI | DCMD_BMI_OP_MOVE_I) << 24)
+ | count;
+ cmd_dataout[3] = buf;
+#if 0
+ print_insn (host, cmd_dataout, "dynamic ", 1);
+ print_insn (host, cmd_dataout + 2, "dynamic ", 1);
+#endif
+ }
+ }
+
+ /*
+ * Install JUMP instructions after the data transfer routines to return
+ * control to the do_other_transfer routines.
+ */
+
+
+ if (datain) {
+ cmd_datain[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ cmd_datain[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+#if 0
+ print_insn (host, cmd_datain, "dynamic jump ", 1);
+#endif
+ cmd_datain += 2;
+ }
+#if 0
+ if (datain) {
+ cmd_datain[0] = 0x98080000;
+ cmd_datain[1] = 0x03ffdeed;
+ cmd_datain += 2;
+ }
+#endif
+ if (dataout) {
+ cmd_dataout[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ cmd_dataout[1] = virt_to_bus(hostdata->script) +
+ hostdata->E_other_transfer;
+#if 0
+ print_insn (host, cmd_dataout, "dynamic jump ", 1);
+#endif
+ cmd_dataout += 2;
+ }
+ return tmp;
+}
+
+/*
+ * Function : int NCR53c7xx_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance driver issue_queue, with major
+ * twiddling done to the host specific fields of cmd. If the
+ * process_issue_queue corouting isn't running, it is restarted.
+ *
+ * NOTE : we use the host_scribble field of the Scsi_Cmnd structure to
+ * hold our own data, and pervert the ptr field of the SCp field
+ * to create a linked list.
+ */
+
+int
+NCR53c7xx_queue_command (Scsi_Cmnd *cmd, void (* done)(Scsi_Cmnd *)) {
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ unsigned long flags;
+ Scsi_Cmnd *tmp;
+
+ cmd->scsi_done = done;
+ cmd->host_scribble = NULL;
+ cmd->SCp.ptr = NULL;
+ cmd->SCp.buffer = NULL;
+
+ save_flags(flags);
+ cli();
+ if ((hostdata->options & (OPTION_DEBUG_INIT_ONLY|OPTION_DEBUG_PROBE_ONLY))
+ || ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+ !(hostdata->debug_lun_limit[cmd->target] & (1 << cmd->lun)))
+#ifdef LINUX_1_2
+ || cmd->target > 7
+#else
+ || cmd->target > host->max_id
+#endif
+ || cmd->target == host->this_id
+ || hostdata->state == STATE_DISABLED) {
+ printk("scsi%d : disabled or bad target %d lun %d\n", host->host_no,
+ cmd->target, cmd->lun);
+ cmd->result = (DID_BAD_TARGET << 16);
+ } else if ((hostdata->options & OPTION_DEBUG_NCOMMANDS_LIMIT) &&
+ (hostdata->debug_count_limit == 0)) {
+ printk("scsi%d : maximum commands exceeded\n", host->host_no);
+ cmd->result = (DID_BAD_TARGET << 16);
+ cmd->result = (DID_BAD_TARGET << 16);
+ } else if (hostdata->options & OPTION_DEBUG_READ_ONLY) {
+ switch (cmd->cmnd[0]) {
+ case WRITE_6:
+ case WRITE_10:
+ printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
+ host->host_no);
+ cmd->result = (DID_BAD_TARGET << 16);
+ }
+ } else {
+ if ((hostdata->options & OPTION_DEBUG_TARGET_LIMIT) &&
+ hostdata->debug_count_limit != -1)
+ --hostdata->debug_count_limit;
+ restore_flags (flags);
+ cmd->result = 0xffff; /* The NCR will overwrite message
+ and status with valid data */
+ cmd->host_scribble = (unsigned char *) tmp = create_cmd (cmd);
+ }
+ cli();
+ /*
+ * REQUEST SENSE commands are inserted at the head of the queue
+ * so that we do not clear the contingent allegience condition
+ * they may be looking at.
+ */
+
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ cmd->SCp.ptr = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->SCp.ptr;
+ tmp = (Scsi_Cmnd *) tmp->SCp.ptr);
+ tmp->SCp.ptr = (unsigned char *) cmd;
+ }
+ restore_flags (flags);
+ run_process_issue_queue();
+ return 0;
+}
+
+/*
+ * Function : void to_schedule_list (struct Scsi_Host *host,
+ * struct NCR53c7x0_hostdata * hostdata, Scsi_Cmnd *cmd)
+ *
+ * Purpose : takes a SCSI command which was just removed from the
+ * issue queue, and deals with it by inserting it in the first
+ * free slot in the schedule list or by terminating it immediately.
+ *
+ * Inputs :
+ * host - SCSI host adater; hostdata - hostdata structure for
+ * this adapter; cmd - a pointer to the command; should have
+ * the host_scribble field initialized to point to a valid
+ *
+ * Side effects :
+ * cmd is added to the per instance schedule list, with minor
+ * twiddling done to the host specific fields of cmd.
+ *
+ */
+
+static __inline__ void
+to_schedule_list (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ Scsi_Cmnd *tmp = cmd->cmd;
+ unsigned long flags;
+ /* dsa start is negative, so subtraction is used */
+ volatile u32 *current;
+
+ int i;
+ NCR53c7x0_local_setup(host);
+#if 0
+ printk("scsi%d : new dsa is 0x%lx (virt 0x%p)\n", host->host_no,
+ virt_to_bus(dsa), dsa);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Work arround race condition : if an interrupt fired and we
+ * got disabled forget about this command.
+ */
+
+ if (hostdata->state == STATE_DISABLED) {
+ printk("scsi%d : driver disabled\n", host->host_no);
+ tmp->result = (DID_BAD_TARGET << 16);
+ cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ hostdata->free = cmd;
+ tmp->scsi_done(tmp);
+ restore_flags (flags);
+ return;
+ }
+
+ for (i = host->can_queue, current = hostdata->schedule;
+ i > 0 && current[0] != hostdata->NOP_insn;
+ --i, current += 2 /* JUMP instructions are two words */);
+
+ if (i > 0) {
+ ++hostdata->busy[tmp->target][tmp->lun];
+ cmd->next = hostdata->running_list;
+ hostdata->running_list = cmd;
+
+ /* Restore this instruction to a NOP once the command starts */
+ cmd->dsa [(hostdata->dsa_jump_dest - hostdata->dsa_start) /
+ sizeof(u32)] = (u32) virt_to_bus ((void *)current);
+ /* Replace the current jump operand. */
+ current[1] =
+ virt_to_bus ((void *) cmd->dsa) + hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template;
+ /* Replace the NOP instruction with a JUMP */
+ current[0] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP) << 24) |
+ DBC_TCI_TRUE;
+ } else {
+ printk ("scsi%d: no free slot\n", host->host_no);
+ disable(host);
+ tmp->result = (DID_ERROR << 16);
+ cmd->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ hostdata->free = cmd;
+ tmp->scsi_done(tmp);
+ restore_flags (flags);
+ return;
+ }
+
+ /*
+ * If the NCR chip is in an idle state, start it running the scheduler
+ * immediately. Otherwise, signal the chip to jump to schedule as
+ * soon as it is idle.
+ */
+ if (hostdata->idle) {
+ hostdata->idle = 0;
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus ((void *)hostdata->schedule));
+ } else {
+ NCR53c7x0_write8(hostdata->istat, ISTAT_10_SIGP);
+ }
+
+ restore_flags(flags);
+}
+
+/*
+ * Function : busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata
+ * *hostdata, Scsi_Cmnd *cmd)
+ *
+ * Purpose : decide if we can pass the given SCSI command on to the
+ * device in question or not.
+ *
+ * Returns : non-zero when we're busy, 0 when we aren't.
+ */
+
+static __inline__ int
+busyp (struct Scsi_Host *host, struct NCR53c7x0_hostdata *hostdata,
+ Scsi_Cmnd *cmd) {
+ /* FIXME : in the future, this needs to accomodate SCSI-II tagged
+ queuing, and we may be able to play with fairness here a bit.
+ */
+ return hostdata->busy[cmd->target][cmd->lun];
+}
+
+/*
+ * Function : process_issue_queue (void)
+ *
+ * Purpose : transfer commands from the issue queue to NCR start queue
+ * of each NCR53c7/8xx in the system, avoiding kernel stack
+ * overflows when the scsi_done() function is invoked recursively.
+ *
+ * NOTE : process_issue_queue exits with interrupts *disabled*, so the
+ * caller must renable them if it desires.
+ *
+ * NOTE : process_issue_queue should be called from both
+ * NCR53c7x0_queue_command() and from the interrupt handler
+ * after command completion in case NCR53c7x0_queue_command()
+ * isn't invoked again but we've freed up resources that are
+ * needed.
+ */
+
+static void
+process_issue_queue (unsigned long flags) {
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *host;
+ struct NCR53c7x0_hostdata *hostdata;
+ int done;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set process_issue_queue_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ */
+
+ do {
+ cli(); /* Freeze request queues */
+ done = 1;
+ for (host = first_host; host && host->hostt == the_template;
+ host = host->next) {
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+ cli();
+ if (hostdata->issue_queue) {
+ if (hostdata->state == STATE_DISABLED) {
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) tmp->SCp.ptr;
+ tmp->result = (DID_BAD_TARGET << 16);
+ if (tmp->host_scribble) {
+ ((struct NCR53c7x0_cmd *)tmp->host_scribble)->next =
+ hostdata->free;
+ hostdata->free =
+ (struct NCR53c7x0_cmd *)tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ }
+ tmp->scsi_done (tmp);
+ done = 0;
+ } else
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
+ tmp->SCp.ptr)
+ if (!tmp->host_scribble ||
+ !busyp (host, hostdata, tmp)) {
+ if (prev)
+ prev->SCp.ptr = tmp->SCp.ptr;
+ else
+ hostdata->issue_queue = (Scsi_Cmnd *)
+ tmp->SCp.ptr;
+ tmp->SCp.ptr = NULL;
+ if (tmp->host_scribble) {
+ if (hostdata->options & OPTION_DEBUG_QUEUES)
+ printk ("scsi%d : moving command for target %d lun %d to start list\n",
+ host->host_no, tmp->target, tmp->lun);
+
+
+ to_schedule_list (host, hostdata,
+ (struct NCR53c7x0_cmd *)
+ tmp->host_scribble);
+ } else {
+ if (((tmp->result & 0xff) == 0xff) ||
+ ((tmp->result & 0xff00) == 0xff00)) {
+ printk ("scsi%d : danger Will Robinson!\n",
+ host->host_no);
+ tmp->result = DID_ERROR << 16;
+ disable (host);
+ }
+ tmp->scsi_done(tmp);
+ }
+ done = 0;
+ } /* if target/lun is not busy */
+ } /* if hostdata->issue_queue */
+ if (!done)
+ restore_flags (flags);
+ } /* for host */
+ } while (!done);
+ process_issue_queue_running = 0;
+}
+
+/*
+ * Function : static void intr_scsi (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all SCSI interrupts, indicated by the setting
+ * of the SIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_scsi (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ unsigned char sstat0_sist0, sist1, /* Registers */
+ fatal; /* Did a fatal interrupt
+ occur ? */
+
+ int is_8xx_chip;
+ NCR53c7x0_local_setup(host);
+
+ fatal = 0;
+
+ is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100;
+ if (is_8xx_chip) {
+ sstat0_sist0 = NCR53c7x0_read8(SIST0_REG_800);
+ udelay(1);
+ sist1 = NCR53c7x0_read8(SIST1_REG_800);
+ } else {
+ sstat0_sist0 = NCR53c7x0_read8(SSTAT0_REG);
+ sist1 = 0;
+ }
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : SIST0 0x%0x, SIST1 0x%0x\n", host->host_no,
+ sstat0_sist0, sist1);
+
+ /* 250ms selection timeout */
+ if ((is_8xx_chip && (sist1 & SIST1_800_STO)) ||
+ (!is_8xx_chip && (sstat0_sist0 & SSTAT0_700_STO))) {
+ fatal = 1;
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ printk ("scsi%d : Selection Timeout\n", host->host_no);
+ if (cmd) {
+ printk("scsi%d : target %d, lun %d, command ",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
+ print_command (cmd->cmd->cmnd);
+ printk("scsi%d : dsp = 0x%x (virt 0x%p)\n", host->host_no,
+ NCR53c7x0_read32(DSP_REG),
+ bus_to_virt(NCR53c7x0_read32(DSP_REG)));
+ } else {
+ printk("scsi%d : no command\n", host->host_no);
+ }
+ }
+/*
+ * XXX - question : how do we want to handle the Illegal Instruction
+ * interrupt, which may occur before or after the Selection Timeout
+ * interrupt?
+ */
+
+ if (1) {
+ hostdata->idle = 1;
+ hostdata->expecting_sto = 0;
+
+ if (hostdata->test_running) {
+ hostdata->test_running = 0;
+ hostdata->test_completed = 3;
+ } else if (cmd) {
+ abnormal_finished(cmd, DID_BAD_TARGET << 16);
+ }
+#if 0
+ hostdata->intrs = 0;
+#endif
+ }
+ }
+
+/*
+ * FIXME : in theory, we can also get a UDC when a STO occurs.
+ */
+ if (sstat0_sist0 & SSTAT0_UDC) {
+ fatal = 1;
+ if (cmd) {
+ printk("scsi%d : target %d lun %d unexpected disconnect\n",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
+ print_lots (host);
+ abnormal_finished(cmd, DID_ERROR << 16);
+ } else
+ printk("scsi%d : unexpected disconnect (no command)\n",
+ host->host_no);
+
+ hostdata->dsp = (u32 *) hostdata->schedule;
+ hostdata->dsp_changed = 1;
+ }
+
+ /* SCSI PARITY error */
+ if (sstat0_sist0 & SSTAT0_PAR) {
+ fatal = 1;
+ if (cmd && cmd->cmd) {
+ printk("scsi%d : target %d lun %d parity error.\n",
+ host->host_no, cmd->cmd->target, cmd->cmd->lun);
+ abnormal_finished (cmd, DID_PARITY << 16);
+ } else
+ printk("scsi%d : parity error\n", host->host_no);
+ /* Should send message out, parity error */
+
+ /* XXX - Reduce synchronous transfer rate! */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ /* SCSI GROSS error */
+ }
+
+ if (sstat0_sist0 & SSTAT0_SGE) {
+ fatal = 1;
+ printk("scsi%d : gross error\n", host->host_no);
+ /* Reset SCSI offset */
+ if ((hostdata->chip / 100) == 8) {
+ NCR53c7x0_write8 (STEST2_REG_800, STEST2_800_ROF);
+ }
+
+ /*
+ * A SCSI gross error may occur when we have
+ *
+ * - A synchronous offset which causes the SCSI FIFO to be overwritten.
+ *
+ * - A REQ which causes the maxmimum synchronous offset programmed in
+ * the SXFER register to be exceeded.
+ *
+ * - A phase change with an outstanding synchronous offset.
+ *
+ * - Residual data in the synchronous data FIFO, with a transfer
+ * other than a synchronous receive is started.$#
+ */
+
+
+ /* XXX Should deduce synchronous transfer rate! */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ /* Phase mismatch */
+ }
+
+ if (sstat0_sist0 & SSTAT0_MA) {
+ fatal = 1;
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : SSTAT0_MA\n", host->host_no);
+ intr_phase_mismatch (host, cmd);
+ }
+
+#if 0
+ if (sstat0_sist0 & SIST0_800_RSL)
+ printk ("scsi%d : Oh no Mr. Bill!\n", host->host_no);
+#endif
+
+/*
+ * If a fatal SCSI interrupt occurs, we must insure that the DMA and
+ * SCSI FIFOs were flushed.
+ */
+
+ if (fatal) {
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+/* XXX - code check for 700/800 chips */
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ printk ("scsi%d: Flushing DMA FIFO\n",
+ host->host_no);
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+ DSTAT_DFE));
+ } else {
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+ }
+ hostdata->dstat |= DSTAT_DFE;
+ }
+ }
+}
+
+/*
+ * Function : static void NCR53c7x0_intr (int irq, struct pt_regs * regs)
+ *
+ * Purpose : handle NCR53c7x0 interrupts for all NCR devices sharing
+ * the same IRQ line.
+ *
+ * Inputs : Since we're using the SA_INTERRUPT interrupt handler
+ * semantics, irq indicates the interrupt which invoked
+ * this handler.
+ */
+
+static void
+NCR53c7x0_intr (int irq, struct pt_regs * regs) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host; /* Host we are looking at */
+ unsigned char istat; /* Values of interrupt regs */
+ struct NCR53c7x0_hostdata *hostdata; /* host->hostdata */
+ struct NCR53c7x0_cmd *cmd, /* command which halted */
+ **cmd_prev_ptr;
+ u32 *dsa; /* DSA */
+ int done = 1; /* Indicates when handler
+ should terminate */
+ int interrupted = 0; /* This HA generated
+ an interrupt */
+ int have_intfly; /* Don't print warning
+ messages when we stack
+ INTFLYs */
+ unsigned long flags;
+
+#ifdef NCR_DEBUG
+ char buf[80]; /* Debugging sprintf buffer */
+ size_t buflen; /* Length of same */
+#endif
+
+ do {
+ done = 1;
+ for (host = first_host; host; host = host->next)
+ if (host->hostt == the_template && host->irq == irq) {
+ NCR53c7x0_local_setup(host);
+
+ hostdata = (struct NCR53c7x0_hostdata *) host->hostdata;
+ hostdata->dsp_changed = 0;
+ interrupted = 0;
+ have_intfly = 0;
+
+ do {
+ int is_8xx_chip;
+
+ hostdata->dstat_valid = 0;
+ interrupted = 0;
+ /*
+ * Only read istat once, since reading it again will unstack
+ * interrupts?
+ */
+ istat = NCR53c7x0_read8(hostdata->istat);
+
+ /*
+ * INTFLY interrupts are used by the NCR53c720, NCR53c810,
+ * and NCR53c820 to signify completion of a command. Since
+ * the SCSI processor continues running, we can't just look
+ * at the contents of the DSA register and continue running.
+ */
+/* XXX - this is too big, offends my sense of aesthetics, and should
+ move to intr_intfly() */
+ is_8xx_chip = ((unsigned) (hostdata->chip - 800)) < 100;
+ if ((hostdata->options & OPTION_INTFLY) &&
+ (is_8xx_chip && (istat & ISTAT_800_INTF))) {
+ char search_found = 0; /* Got at least one ? */
+ done = 0;
+ interrupted = 1;
+
+ /*
+ * Clear the INTF bit by writing a one.
+ * This reset operation is self-clearing.
+ */
+ NCR53c7x0_write8(hostdata->istat, istat|ISTAT_800_INTF);
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : INTFLY\n", host->host_no);
+
+ /*
+ * Traverse our list of running commands, and look
+ * for those with valid (non-0xff ff) status and message
+ * bytes encoded in the result which signify command
+ * completion.
+ */
+
+
+ save_flags(flags);
+ cli();
+restart:
+ for (cmd_prev_ptr = (struct NCR53c7x0_cmd **)
+ &(hostdata->running_list), cmd =
+ (struct NCR53c7x0_cmd *) hostdata->running_list; cmd ;
+ cmd_prev_ptr = (struct NCR53c7x0_cmd **) &(cmd->next),
+ cmd = (struct NCR53c7x0_cmd *) cmd->next) {
+ Scsi_Cmnd *tmp;
+
+ if (!cmd) {
+ printk("scsi%d : very weird.\n", host->host_no);
+ break;
+ }
+
+ if (!(tmp = cmd->cmd)) {
+ printk("scsi%d : weird. NCR53c7x0_cmd has no Scsi_Cmnd\n",
+ host->host_no);
+ continue;
+ }
+#if 0
+ printk ("scsi%d : looking at result of 0x%x\n",
+ host->host_no, cmd->cmd->result);
+#endif
+
+ if (((tmp->result & 0xff) == 0xff) ||
+ ((tmp->result & 0xff00) == 0xff00))
+ continue;
+
+ search_found = 1;
+
+ /* Important - remove from list _before_ done is called */
+ if (cmd_prev_ptr)
+ *cmd_prev_ptr = (struct NCR53c7x0_cmd *) cmd->next;
+
+ --hostdata->busy[tmp->target][tmp->lun];
+ cmd->next = hostdata->free;
+ hostdata->free = cmd;
+
+ tmp->host_scribble = NULL;
+
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ printk ("scsi%d : command complete : pid %lu, id %d,lun %d result 0x%x ",
+ host->host_no, tmp->pid, tmp->target, tmp->lun, tmp->result);
+ print_command (tmp->cmnd);
+ }
+
+#if 0
+ hostdata->options &= ~OPTION_DEBUG_INTR;
+#endif
+ tmp->scsi_done(tmp);
+ goto restart;
+
+ }
+ restore_flags(flags);
+
+ /*
+ * I think that we're stacking INTFLY interrupts; taking care of
+ * all the finished commands on the first one, and then getting
+ * worried when we see the next one. The magic with have_intfly
+ * should tell if this is the case..
+ */
+
+ if (!search_found && !have_intfly) {
+ printk ("scsi%d : WARNING : INTFLY with no completed commands.\n",
+ host->host_no);
+ } else if (!have_intfly) {
+ have_intfly = 1;
+ run_process_issue_queue();
+ }
+ }
+
+ if (istat & (ISTAT_SIP|ISTAT_DIP)) {
+ done = 0;
+ interrupted = 1;
+ hostdata->state = STATE_HALTED;
+
+ if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK)
+ printk ("scsi%d : SCSI FIFO not empty\n",
+ host->host_no);
+
+ /*
+ * NCR53c700 and NCR53c700-66 change the current SCSI
+ * process, hostdata->current, in the Linux driver so
+ * cmd = hostdata->current.
+ *
+ * With other chips, we must look through the commands
+ * executing and find the command structure which
+ * corresponds to the DSA register.
+ */
+
+ if (hostdata->options & OPTION_700) {
+ cmd = (struct NCR53c7x0_cmd *) hostdata->current;
+ } else {
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ for (cmd = (struct NCR53c7x0_cmd *)
+ hostdata->running_list; cmd &&
+ (dsa + (hostdata->dsa_start / sizeof(u32))) !=
+ cmd->dsa;
+ cmd = (struct NCR53c7x0_cmd *)(cmd->next));
+ }
+ if (hostdata->options & OPTION_DEBUG_INTR) {
+ if (cmd) {
+ printk("scsi%d : interrupt for pid %lu, id %d, lun %d ",
+ host->host_no, cmd->cmd->pid, (int) cmd->cmd->target,
+ (int) cmd->cmd->lun);
+ print_command (cmd->cmd->cmnd);
+ } else {
+ printk("scsi%d : no active command\n", host->host_no);
+ }
+ }
+
+ if (istat & ISTAT_SIP) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ISTAT_SIP\n", host->host_no);
+ intr_scsi (host, cmd);
+ }
+
+ if (istat & ISTAT_DIP) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ISTAT_DIP\n", host->host_no);
+ intr_dma (host, cmd);
+ }
+
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ /* XXX - code check for 700/800 chips */
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ printk ("scsi%d : DMA FIFO not empty\n", host->host_no);
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ printk ("scsi%d: Flushing DMA FIFO\n",
+ host->host_no);
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+ DSTAT_DFE));
+ } else
+ {
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+ }
+ hostdata->dstat |= DSTAT_DFE;
+ }
+ }
+ } while (interrupted);
+
+
+
+ if (hostdata->intrs != -1)
+ hostdata->intrs++;
+#if 0
+ if (hostdata->intrs > 40) {
+ printk("scsi%d : too many interrupts, halting", host->host_no);
+ disable(host);
+ }
+#endif
+
+ if (!hostdata->idle && hostdata->state == STATE_HALTED) {
+ if (!hostdata->dsp_changed) {
+ hostdata->dsp = (u32 *)
+ bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ }
+
+#if 0
+ printk("scsi%d : new dsp is 0x%lx (virt 0x%p)\n",
+ host->host_no, virt_to_bus(hostdata->dsp), hostdata->dsp);
+#endif
+
+ hostdata->state = STATE_RUNNING;
+ NCR53c7x0_write32 (DSP_REG, virt_to_bus(hostdata->dsp));
+ }
+ }
+ } while (!done);
+}
+
+
+/*
+ * Function : static int abort_connected (struct Scsi_Host *host)
+ *
+ * Purpose : Assuming that the NCR SCSI processor is currently
+ * halted, break the currently established nexus. Clean
+ * up of the NCR53c7x0_cmd and Scsi_Cmnd structures should
+ * be done on receipt of the abort interrupt.
+ *
+ * Inputs : host - SCSI host
+ *
+ */
+
+static int
+abort_connected (struct Scsi_Host *host) {
+#ifdef NEW_ABORT
+ NCR53c7x0_local_declare();
+#endif
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+/* FIXME : this probably should change for production kernels; at the
+ least, counter sould move to a per-host structure. */
+ static int counter = 5;
+#ifdef NEW_ABORT
+ int sstat, phase, offset;
+ u32 *script;
+ NCR53c7x0_local_setup(host);
+#endif
+
+ if (--counter <= 0) {
+ disable(host);
+ return 0;
+ }
+
+ printk ("scsi%d : DANGER : abort_connected() called \n",
+ host->host_no);
+
+#ifdef NEW_ABORT
+
+/*
+ * New strategy : Rather than using a generic abort routine,
+ * we'll specifically try to source or sink the appropriate
+ * amount of data for the phase we're currently in (taking into
+ * account the current synchronous offset)
+ */
+
+ sstat = (NCR53c8x0_read8 ((chip / 100) == 8 ? SSTAT1_REG : SSTAT2_REG);
+ offset = OFFSET (sstat & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
+ phase = sstat & SSTAT2_PHASE_MASK;
+
+/*
+ * SET ATN
+ * MOVE source_or_sink, WHEN CURRENT PHASE
+ * < repeat for each outstanding byte >
+ * JUMP send_abort_message
+ */
+
+ script = hostdata->abort_script = kmalloc (
+ 8 /* instruction size */ * (
+ 1 /* set ATN */ +
+ (!offset ? 1 : offset) /* One transfer per outstanding byte */ +
+ 1 /* send abort message */),
+ GFP_ATOMIC);
+
+
+#else /* def NEW_ABORT */
+ hostdata->dsp = hostdata->script + hostdata->E_initiator_abort /
+ sizeof(u32);
+#endif /* def NEW_ABORT */
+ hostdata->dsp_changed = 1;
+
+/* XXX - need to flag the command as aborted after the abort_connected
+ code runs
+ */
+ return 0;
+}
+
+/*
+ * Function : static int datapath_residual (Scsi_Host *host)
+ *
+ * Purpose : return residual data count of what's in the chip.
+ *
+ * Inputs : host - SCSI host
+ */
+
+static int
+datapath_residual (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int count, synchronous, sstat;
+ NCR53c7x0_local_setup(host);
+ /* COMPAT : the 700 and 700-66 need to use DFIFO_00_BO_MASK */
+ count = ((NCR53c7x0_read8 (DFIFO_REG) & DFIFO_10_BO_MASK) -
+ (NCR53c7x0_read32 (DBC_REG) & DFIFO_10_BO_MASK)) & DFIFO_10_BO_MASK;
+ synchronous = NCR53c7x0_read8 (SXFER_REG) & SXFER_MO_MASK;
+ /* COMPAT : DDIR is elsewhere on non-'8xx chips. */
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ /* Receive */
+ if (synchronous)
+ count += (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT;
+ else
+ if (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT0_REG : SSTAT1_REG) & SSTAT1_ILF)
+ ++count;
+ } else {
+ /* Send */
+ sstat = ((hostdata->chip / 100) == 8) ? NCR53c7x0_read8 (SSTAT0_REG) :
+ NCR53c7x0_read8 (SSTAT1_REG);
+ if (sstat & SSTAT1_OLF)
+ ++count;
+ if (synchronous && (sstat & SSTAT1_ORF))
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : static const char * sbcl_to_phase (int sbcl)_
+ *
+ * Purpose : Convert SBCL register to user-parsable phase representation
+ *
+ * Inputs : sbcl - value of sbcl register
+ */
+
+
+static const char *
+sbcl_to_phase (int sbcl) {
+ switch (sbcl & SBCL_PHASE_MASK) {
+ case SBCL_PHASE_DATAIN:
+ return "DATAIN";
+ case SBCL_PHASE_DATAOUT:
+ return "DATAOUT";
+ case SBCL_PHASE_MSGIN:
+ return "MSGIN";
+ case SBCL_PHASE_MSGOUT:
+ return "MSGOUT";
+ case SBCL_PHASE_CMDOUT:
+ return "CMDOUT";
+ case SBCL_PHASE_STATIN:
+ return "STATUSIN";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Function : static const char * sstat2_to_phase (int sstat)_
+ *
+ * Purpose : Convert SSTAT2 register to user-parsable phase representation
+ *
+ * Inputs : sstat - value of sstat register
+ */
+
+
+static const char *
+sstat2_to_phase (int sstat) {
+ switch (sstat & SSTAT2_PHASE_MASK) {
+ case SSTAT2_PHASE_DATAIN:
+ return "DATAIN";
+ case SSTAT2_PHASE_DATAOUT:
+ return "DATAOUT";
+ case SSTAT2_PHASE_MSGIN:
+ return "MSGIN";
+ case SSTAT2_PHASE_MSGOUT:
+ return "MSGOUT";
+ case SSTAT2_PHASE_CMDOUT:
+ return "CMDOUT";
+ case SSTAT2_PHASE_STATIN:
+ return "STATUSIN";
+ default:
+ return "unknown";
+ }
+}
+
+/*
+ * Function : static void intr_phase_mismatch (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : Handle phase mismatch interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ *
+ * Side effects : The abort_connected() routine is called or the NCR chip
+ * is restarted, jumping to the command_complete entry point, or
+ * patching the address and transfer count of the current instruction
+ * and calling the msg_in entry point as appropriate.
+ */
+
+static void
+intr_phase_mismatch (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ u32 dbc_dcmd, *dsp, *dsp_next;
+ unsigned char dcmd, sbcl;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int residual;
+ enum {ACTION_ABORT, ACTION_ABORT_PRINT, ACTION_CONTINUE} action =
+ ACTION_ABORT_PRINT;
+ const char *where = NULL;
+ NCR53c7x0_local_setup(host);
+
+ /*
+ * Corrective action is based on where in the SCSI SCRIPT(tm) the error
+ * occurred, as well as which SCSI phase we are currently in.
+ */
+ dsp_next = bus_to_virt(NCR53c7x0_read32(DSP_REG));
+
+ /*
+ * Fetch the current instruction, and remove the operands for easier
+ * interpretation.
+ */
+ dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+ dcmd = (dbc_dcmd & 0xff000000) >> 24;
+ /*
+ * Like other processors, the NCR adjusts the instruction pointer before
+ * instruction decode. Set the DSP address back to what it should
+ * be for this instruction based on its size (2 or 3 32 bit words).
+ */
+ dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+
+
+ /*
+ * Read new SCSI phase from the SBCL lines. Since all of our code uses
+ * a WHEN conditional instead of an IF conditional, we don't need to
+ * wait for a new REQ.
+ */
+ sbcl = NCR53c7x0_read8(SBCL_REG) & SBCL_PHASE_MASK;
+
+ if (!cmd) {
+ action = ACTION_ABORT_PRINT;
+ where = "no current command";
+ /*
+ * The way my SCSI SCRIPTS(tm) are architected, recoverable phase
+ * mismatches should only occur where we're doing a multi-byte
+ * BMI instruction. Specifically, this means
+ *
+ * - select messages (a SCSI-I target may ignore additional messages
+ * after the IDENTIFY; any target may reject a SDTR or WDTR)
+ *
+ * - command out (targets may send a message to signal an error
+ * condition, or go into STATUSIN after they've decided
+ * they don't like the command.
+ *
+ * - reply_message (targets may reject a multi-byte message in the
+ * middle)
+ *
+ * - data transfer routines (command completion with buffer space
+ * left, disconnect message, or error message)
+ */
+ } else if (((dsp >= cmd->data_transfer_start &&
+ dsp < cmd->data_transfer_end)) || dsp == (cmd->residual + 2)) {
+ if ((dcmd & (DCMD_TYPE_MASK|DCMD_BMI_OP_MASK|DCMD_BMI_INDIRECT|
+ DCMD_BMI_MSG|DCMD_BMI_CD)) == (DCMD_TYPE_BMI|
+ DCMD_BMI_OP_MOVE_I)) {
+ residual = datapath_residual (host);
+ if (hostdata->options & OPTION_DEBUG_DISCONNECT)
+ printk ("scsi%d : handling residual transfer (+ %d bytes from DMA FIFO)\n",
+ host->host_no, residual);
+
+ /*
+ * The first instruction is a CALL to the alternate handler for
+ * this data transfer phase, so we can do calls to
+ * munge_msg_restart as we would if control were passed
+ * from normal dynamic code.
+ */
+ if (dsp != cmd->residual + 2) {
+ cmd->residual[0] = ((DCMD_TYPE_TCI | DCMD_TCI_OP_CALL |
+ ((dcmd & DCMD_BMI_IO) ? DCMD_TCI_IO : 0)) << 24) |
+ DBC_TCI_WAIT_FOR_VALID | DBC_TCI_COMPARE_PHASE;
+ cmd->residual[1] = virt_to_bus(hostdata->script)
+ + ((dcmd & DCMD_BMI_IO)
+ ? hostdata->E_other_in : hostdata->E_other_out);
+ }
+
+ /*
+ * The second instruction is the a data transfer block
+ * move instruction, reflecting the pointer and count at the
+ * time of the phase mismatch.
+ */
+ cmd->residual[2] = dbc_dcmd + residual;
+ cmd->residual[3] = NCR53c7x0_read32(DNAD_REG) - residual;
+
+ /*
+ * The third and final instruction is a jump to the instruction
+ * which follows the instruction which had to be 'split'
+ */
+ if (dsp != cmd->residual + 2) {
+ cmd->residual[4] = ((DCMD_TYPE_TCI|DCMD_TCI_OP_JUMP)
+ << 24) | DBC_TCI_TRUE;
+ cmd->residual[5] = virt_to_bus(dsp_next);
+ }
+
+ /*
+ * For the sake of simplicity, transfer control to the
+ * conditional CALL at the start of the residual buffer.
+ */
+ hostdata->dsp = cmd->residual;
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ } else {
+ where = "non-BMI dynamic DSA code";
+ action = ACTION_ABORT_PRINT;
+ }
+ } else if (dsp == (hostdata->script + hostdata->E_select_msgout / 4)) {
+ /* Release ATN */
+ NCR53c7x0_write8 (SOCL_REG, 0);
+ switch (sbcl) {
+ /*
+ * Some devices (SQ555 come to mind) grab the IDENTIFY message
+ * sent on selection, and decide to go into COMMAND OUT phase
+ * rather than accepting the rest of the messages or rejecting
+ * them. Handle these devices gracefully.
+ */
+ case SBCL_PHASE_CMDOUT:
+ hostdata->dsp = dsp + 2 /* two _words_ */;
+ hostdata->dsp_changed = 1;
+ printk ("scsi%d : target %d ignored SDTR and went into COMMAND OUT\n",
+ host->host_no, cmd->cmd->target);
+ cmd->flags &= ~CMD_FLAG_SDTR;
+ action = ACTION_CONTINUE;
+ break;
+ case SBCL_PHASE_MSGIN:
+ hostdata->dsp = hostdata->script + hostdata->E_msg_in /
+ sizeof(u32);
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ break;
+ default:
+ where="select message out";
+ action = ACTION_ABORT_PRINT;
+ }
+ /*
+ * Some SCSI devices will interpret a command as they read the bytes
+ * off the SCSI bus, and may decide that the command is Bogus before
+ * they've read the entire commad off the bus.
+ */
+ } else if (dsp == hostdata->script + hostdata->E_cmdout_cmdout / sizeof
+ (u32)) {
+ hostdata->dsp = hostdata->script + hostdata->E_data_transfer /
+ sizeof (u32);
+ hostdata->dsp_changed = 1;
+ action = ACTION_CONTINUE;
+ /* FIXME : we need to handle message reject, etc. within msg_respond. */
+#ifdef notyet
+ } else if (dsp == hostdata->script + hostdata->E_reply_message) {
+ switch (sbcl) {
+ /* Any other phase mismatches abort the currently executing command. */
+#endif
+ } else {
+ where = "unknown location";
+ action = ACTION_ABORT_PRINT;
+ }
+
+ /* Flush DMA FIFO */
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+ if (!(hostdata->dstat & DSTAT_DFE)) {
+ if (NCR53c7x0_read8 (CTEST2_REG_800) & CTEST2_800_DDIR) {
+ printk ("scsi%d: Flushing DMA FIFO\n",
+ host->host_no);
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_FLF);
+ /* FIXME : what about stacked DMA interrupts? */
+ while (!((hostdata->dstat = NCR53c7x0_read8(DSTAT_REG)) &
+ DSTAT_DFE));
+ } else {
+ NCR53c7x0_write8 (CTEST3_REG_800, CTEST3_800_CLF);
+ while (NCR53c7x0_read8 (CTEST3_REG_800) & CTEST3_800_CLF);
+ }
+ hostdata->dstat |= DSTAT_DFE;
+ }
+
+ switch (action) {
+ case ACTION_ABORT_PRINT:
+ printk("scsi%d : %s : unexpected phase %s.\n",
+ host->host_no, where ? where : "unknown location",
+ sbcl_to_phase(sbcl));
+ print_lots (host);
+ /* Fall through to ACTION_ABORT */
+ case ACTION_ABORT:
+ abort_connected (host);
+ break;
+ case ACTION_CONTINUE:
+ break;
+ }
+
+#if 0
+ if (hostdata->dsp_changed) {
+ printk("scsi%d: new dsp 0x%p\n", host->host_no, hostdata->dsp);
+ print_insn (host, hostdata->dsp, "", 1);
+ }
+#endif
+
+}
+
+/*
+ * Function : static void intr_bf (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle BUS FAULT interrupts
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_bf (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 *dsp,
+ *next_dsp, /* Current dsp */
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ unsigned short pci_status;
+ int tmp;
+ unsigned long flags;
+ char *reason = NULL;
+ /* Default behavior is for a silent error, with a retry until we've
+ exhausted retries. */
+ enum {MAYBE, ALWAYS, NEVER} retry = MAYBE;
+ int report = 0;
+ NCR53c7x0_local_setup(host);
+
+ dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+ next_dsp = bus_to_virt (NCR53c7x0_read32(DSP_REG));
+ dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* FIXME - check chip type */
+ dsa = bus_to_virt (NCR53c7x0_read32(DSA_REG));
+
+ /*
+ * Bus faults can be caused by either a Bad Address or
+ * Target Abort. We should check the Received Target Abort
+ * bit of the PCI status register and Master Abort Bit.
+ *
+ * - Master Abort bit indicates that no device claimed
+ * the address with DEVSEL within five clocks
+ *
+ * - Target Abort bit indicates that a target claimed it,
+ * but changed its mind once it saw the byte enables.
+ *
+ */
+
+ if ((hostdata->chip / 100) == 8) {
+ save_flags (flags);
+ cli();
+ tmp = pcibios_read_config_word (hostdata->pci_bus,
+ hostdata->pci_device_fn, PCI_STATUS, &pci_status);
+ restore_flags (flags);
+ if (tmp == PCIBIOS_SUCCESSFUL) {
+ if (pci_status & PCI_STATUS_REC_TARGET_ABORT) {
+ reason = "PCI target abort";
+ pci_status &= ~PCI_STATUS_REC_TARGET_ABORT;
+ } else if (pci_status & PCI_STATUS_REC_MASTER_ABORT) {
+ reason = "No device asserted PCI DEVSEL within five bus clocks";
+ pci_status &= ~PCI_STATUS_REC_MASTER_ABORT;
+ } else if (pci_status & PCI_STATUS_PARITY) {
+ report = 1;
+ pci_status &= ~PCI_STATUS_PARITY;
+ }
+ } else {
+ printk ("scsi%d : couldn't read status register : %s\n",
+ host->host_no, pcibios_strerror (tmp));
+ retry = NEVER;
+ }
+ }
+
+#ifndef notyet
+ report = 1;
+#endif
+ if (report && reason) {
+ printk(KERN_ALERT "scsi%d : BUS FAULT reason = %s\n",
+ host->host_no, reason ? reason : "unknown");
+ print_lots (host);
+ }
+
+#ifndef notyet
+ retry = NEVER;
+#endif
+
+ /*
+ * TODO : we should attempt to recover from any spurious bus
+ * faults. After X retries, we should figure that things are
+ * sufficiently wedged, and call NCR53c7xx_reset.
+ *
+ * This code should only get executed once we've decided that we
+ * cannot retry.
+ */
+
+ if (retry == NEVER) {
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG\n");
+ FATAL (host);
+ }
+}
+
+/*
+ * Function : static void intr_dma (struct Scsi_Host *host,
+ * struct NCR53c7x0_cmd *cmd)
+ *
+ * Purpose : handle all DMA interrupts, indicated by the setting
+ * of the DIP bit in the ISTAT register.
+ *
+ * Inputs : host, cmd - host and NCR command causing the interrupt, cmd
+ * may be NULL.
+ */
+
+static void
+intr_dma (struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned char dstat; /* DSTAT */
+ u32 *dsp,
+ *next_dsp, /* Current dsp */
+ *dsa,
+ dbc_dcmd; /* DCMD (high eight bits) + DBC */
+ int tmp;
+ unsigned long flags;
+ NCR53c7x0_local_setup(host);
+
+ if (!hostdata->dstat_valid) {
+ hostdata->dstat = NCR53c7x0_read8(DSTAT_REG);
+ hostdata->dstat_valid = 1;
+ }
+
+ dstat = hostdata->dstat;
+
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk("scsi%d : DSTAT=0x%x\n", host->host_no, (int) dstat);
+
+ dbc_dcmd = NCR53c7x0_read32 (DBC_REG);
+ next_dsp = bus_to_virt(NCR53c7x0_read32(DSP_REG));
+ dsp = next_dsp - NCR53c7x0_insn_size ((dbc_dcmd >> 24) & 0xff);
+/* XXX - check chip type */
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+
+ /*
+ * DSTAT_ABRT is the aborted interrupt. This is set whenever the
+ * SCSI chip is aborted.
+ *
+ * With NCR53c700 and NCR53c700-66 style chips, we should only
+ * get this when the chip is currently running the accept
+ * reselect/select code and we have set the abort bit in the
+ * ISTAT register.
+ *
+ */
+
+ if (dstat & DSTAT_ABRT) {
+#if 0
+ /* XXX - add code here to deal with normal abort */
+ if ((hostdata->options & OPTION_700) && (hostdata->state ==
+ STATE_ABORTING)) {
+ } else
+#endif
+ {
+ printk(KERN_ALERT "scsi%d : unexpected abort interrupt at\n"
+ " ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "s ", 1);
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_SSI is the single step interrupt. Should be generated
+ * whenever we have single stepped or are tracing.
+ */
+
+ if (dstat & DSTAT_SSI) {
+ if (hostdata->options & OPTION_DEBUG_TRACE) {
+ } else if (hostdata->options & OPTION_DEBUG_SINGLE) {
+ print_insn (host, dsp, "s ", 0);
+ save_flags(flags);
+ cli();
+/* XXX - should we do this, or can we get away with writing dsp? */
+
+ NCR53c7x0_write8 (DCNTL_REG, (NCR53c7x0_read8(DCNTL_REG) &
+ ~DCNTL_SSM) | DCNTL_STD);
+ restore_flags(flags);
+ } else {
+ printk(KERN_ALERT "scsi%d : unexpected single step interrupt at\n"
+ " ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG\n");
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_IID / DSTAT_OPC (same bit, same meaning, only the name
+ * is different) is generated whenever an illegal instruction is
+ * encountered.
+ *
+ * XXX - we may want to emulate INTFLY here, so we can use
+ * the same SCSI SCRIPT (tm) for NCR53c710 through NCR53c810
+ * chips.
+ */
+
+ if (dstat & DSTAT_OPC) {
+ /*
+ * Ascertain if this IID interrupts occurred before or after a STO
+ * interrupt. Since the interrupt handling code now leaves
+ * DSP unmodified until _after_ all stacked interrupts have been
+ * processed, reading the DSP returns the original DSP register.
+ * This means that if dsp lies between the select code, and
+ * message out following the selection code (where the IID interrupt
+ * would have to have occurred by due to the implicit wait for REQ),
+ * we have an IID interrupt resulting from a STO condition and
+ * can ignore it.
+ */
+
+ if (((dsp >= (hostdata->script + hostdata->E_select / sizeof(u32))) &&
+ (dsp <= (hostdata->script + hostdata->E_select_msgout /
+ sizeof(u32) + 8))) || (hostdata->test_running == 2)) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : ignoring DSTAT_IID for SSTAT_STO\n",
+ host->host_no);
+ if (hostdata->expecting_iid) {
+ hostdata->expecting_iid = 0;
+ hostdata->idle = 1;
+ if (hostdata->test_running == 2) {
+ hostdata->test_running = 0;
+ hostdata->test_completed = 3;
+ } else if (cmd)
+ abnormal_finished (cmd, DID_BAD_TARGET << 16);
+ } else {
+ hostdata->expecting_sto = 1;
+ }
+ /*
+ * We can't guarantee we'll be able to execute the WAIT DISCONNECT
+ * instruction within the 3.4us of bus free and arbitration delay
+ * that a target can RESELECT in and assert REQ after we've dropped
+ * ACK. If this happens, we'll get an illegal instruction interrupt.
+ * Doing away with the WAIT DISCONNECT instructions broke everything,
+ * so instead I'll settle for moving one WAIT DISCONNECT a few
+ * instructions closer to the CLEAR ACK before it to minimize the
+ * chances of this happening, and handle it if it occurs anyway.
+ *
+ * Simply continue with what we were doing, and control should
+ * be transfered to the schedule routine which will ultimately
+ * pass control onto the reselection or selection (not yet)
+ * code.
+ */
+ } else if (dbc_dcmd == 0x48000000 && (NCR53c7x0_read8 (SBCL_REG) &
+ SBCL_REQ)) {
+ if (!(hostdata->options & OPTION_NO_PRINT_RACE))
+ {
+ printk("scsi%d: REQ before WAIT DISCONNECT IID\n",
+ host->host_no);
+ hostdata->options |= OPTION_NO_PRINT_RACE;
+ }
+ } else {
+ printk(KERN_ALERT "scsi%d : illegal instruction\n", host->host_no);
+ print_lots (host);
+ printk(KERN_ALERT " mail drew@PoohSticks.ORG with ALL\n"
+ " boot messages and diagnostic output\n");
+ FATAL (host);
+ }
+ }
+
+ /*
+ * DSTAT_BF are bus fault errors
+ */
+
+ if (dstat & DSTAT_800_BF) {
+ intr_bf (host, cmd);
+ }
+
+
+ /*
+ * DSTAT_SIR interrupts are generated by the execution of
+ * the INT instruction. Since the exact values available
+ * are determined entirely by the SCSI script running,
+ * and are local to a particular script, a unique handler
+ * is called for each script.
+ */
+
+ if (dstat & DSTAT_SIR) {
+ if (hostdata->options & OPTION_DEBUG_INTR)
+ printk ("scsi%d : DSTAT_SIR\n", host->host_no);
+ switch ((tmp = hostdata->dstat_sir_intr (host, cmd))) {
+ case SPECIFIC_INT_NOTHING:
+ case SPECIFIC_INT_RESTART:
+ break;
+ case SPECIFIC_INT_ABORT:
+ abort_connected(host);
+ break;
+ case SPECIFIC_INT_PANIC:
+ printk(KERN_ALERT "scsi%d : failure at ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT " dstat_sir_intr() returned SPECIFIC_INT_PANIC\n");
+ FATAL (host);
+ break;
+ case SPECIFIC_INT_BREAK:
+ intr_break (host, cmd);
+ break;
+ default:
+ printk(KERN_ALERT "scsi%d : failure at ", host->host_no);
+ print_insn (host, dsp, KERN_ALERT "", 1);
+ printk(KERN_ALERT" dstat_sir_intr() returned unknown value %d\n",
+ tmp);
+ FATAL (host);
+ }
+ }
+
+ if ((hostdata->chip / 100) == 8 && (dstat & DSTAT_800_MDPE)) {
+ printk(KERN_ALERT "scsi%d : Master Data Parity Error\n",
+ host->host_no);
+ FATAL (host);
+ }
+}
+
+/*
+ * Function : static int print_insn (struct Scsi_Host *host,
+ * u32 *insn, int kernel)
+ *
+ * Purpose : print numeric representation of the instruction pointed
+ * to by insn to the debugging or kernel message buffer
+ * as appropriate.
+ *
+ * If desired, a user level program can interpret this
+ * information.
+ *
+ * Inputs : host, insn - host, pointer to instruction, prefix -
+ * string to prepend, kernel - use printk instead of debugging buffer.
+ *
+ * Returns : size, in u32s, of instruction printed.
+ */
+
+/*
+ * FIXME: should change kernel parameter so that it takes an ENUM
+ * specifying severity - either KERN_ALERT or KERN_PANIC so
+ * all panic messages are output with the same severity.
+ */
+
+static int
+print_insn (struct Scsi_Host *host, const u32 *insn,
+ const char *prefix, int kernel) {
+ char buf[160], /* Temporary buffer and pointer. ICKY
+ arbitrary length. */
+
+
+ *tmp;
+ unsigned char dcmd; /* dcmd register for *insn */
+ int size;
+
+ /*
+ * Check to see if the instruction pointer is not bogus before
+ * indirecting through it; avoiding red-zone at start of
+ * memory.
+ *
+ * FIXME: icky magic needs to happen here on non-intel boxes which
+ * don't have kernel memory mapped in like this. Might be reasonable
+ * to use vverify()?
+ */
+
+ if (MAP_NR(insn) < 1 || MAP_NR(insn + 8) > MAP_NR(high_memory) ||
+ ((((dcmd = (insn[0] >> 24) & 0xff) & DCMD_TYPE_MMI) == DCMD_TYPE_MMI) &&
+ MAP_NR(insn + 12) > MAP_NR(high_memory))) {
+ size = 0;
+ sprintf (buf, "%s%p: address out of range\n",
+ prefix, insn);
+ } else {
+/*
+ * FIXME : (void *) cast in virt_to_bus should be unecessary, because
+ * it should take const void * as argument.
+ */
+ sprintf(buf, "%s0x%lx (virt 0x%p) : 0x%08x 0x%08x (virt 0x%p)",
+ (prefix ? prefix : ""), virt_to_bus((void *) insn), insn,
+ insn[0], insn[1], bus_to_virt (insn[1]));
+ tmp = buf + strlen(buf);
+ if ((dcmd & DCMD_TYPE_MASK) == DCMD_TYPE_MMI) {
+ sprintf (tmp, " 0x%08x (virt 0x%p)\n", insn[2],
+ bus_to_virt(insn[2]));
+ size = 3;
+ } else {
+ sprintf (tmp, "\n");
+ size = 2;
+ }
+ }
+
+ if (kernel)
+ printk ("%s", buf);
+#ifdef NCR_DEBUG
+ else {
+ size_t len = strlen(buf);
+ debugger_kernel_write(host, buf, len);
+ }
+#endif
+ return size;
+}
+
+/*
+ * Function : static const char *ncr_state (int state)
+ *
+ * Purpose : convert state (probably from hostdata->state) to a string
+ *
+ * Inputs : state
+ *
+ * Returns : char * representation of state, "unknown" on error.
+ */
+
+static const char *
+ncr_state (int state) {
+ switch (state) {
+ case STATE_HALTED: return "halted";
+ case STATE_WAITING: return "waiting";
+ case STATE_RUNNING: return "running";
+ case STATE_ABORTING: return "aborting";
+ case STATE_DISABLED: return "disabled";
+ default: return "unknown";
+ }
+}
+
+/*
+ * Function : int NCR53c7xx_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : Abort an errant SCSI command, doing all necessary
+ * cleanup of the issue_queue, running_list, shared Linux/NCR
+ * dsa issue and reconnect queues.
+ *
+ * Inputs : cmd - command to abort, code - entire result field
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+int
+NCR53c7xx_abort (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata = host ? (struct NCR53c7x0_hostdata *)
+ host->hostdata : NULL;
+ unsigned long flags;
+ struct NCR53c7x0_cmd *curr, **prev;
+ Scsi_Cmnd *me, **last;
+#if 0
+ static long cache_pid = -1;
+#endif
+
+
+ if (!host) {
+ printk ("Bogus SCSI command pid %ld; no host structure\n",
+ cmd->pid);
+ return SCSI_ABORT_ERROR;
+ } else if (!hostdata) {
+ printk ("Bogus SCSI host %d; no hostdata\n", host->host_no);
+ return SCSI_ABORT_ERROR;
+ }
+ NCR53c7x0_local_setup(host);
+
+/*
+ * CHECK : I don't think that reading ISTAT will unstack any interrupts,
+ * since we need to write the INTF bit to clear it, and SCSI/DMA
+ * interrupts don't clear until we read SSTAT/SIST and DSTAT registers.
+ *
+ * See that this is the case.
+ *
+ * I suspect that several of our failures may be coming from a new fatal
+ * interrupt (possibly due to a phase mismatch) happening after we've left
+ * the interrupt handler, but before the PIC has had the interrupt condition
+ * cleared.
+ */
+
+ if (NCR53c7x0_read8(hostdata->istat) &
+ (ISTAT_DIP|ISTAT_SIP|
+ (hostdata->chip / 100 == 8 ? ISTAT_800_INTF : 0))) {
+ printk ("scsi%d : dropped interrupt for command %ld\n", host->host_no,
+ cmd->pid);
+ NCR53c7x0_intr (host->irq, NULL);
+ return SCSI_ABORT_BUSY;
+ }
+
+ save_flags(flags);
+ cli();
+#if 0
+ if (cache_pid == cmd->pid)
+ panic ("scsi%d : bloody fetus %d\n", host->host_no, cmd->pid);
+ else
+ cache_pid = cmd->pid;
+#endif
+
+
+/*
+ * The command could be hiding in the issue_queue. This would be very
+ * nice, as commands can't be moved from the high level driver's issue queue
+ * into the shared queue until an interrupt routine is serviced, and this
+ * moving is atomic.
+ *
+ * If this is the case, we don't have to worry about anything - we simply
+ * pull the command out of the old queue, and call it aborted.
+ */
+
+ for (me = (Scsi_Cmnd *) hostdata->issue_queue,
+ last = (Scsi_Cmnd **) &(hostdata->issue_queue);
+ me && me != cmd; last = (Scsi_Cmnd **)&(me->SCp.ptr),
+ me = (Scsi_Cmnd *)me->SCp.ptr);
+
+ if (me) {
+ *last = (Scsi_Cmnd *) me->SCp.ptr;
+ if (me->host_scribble) {
+ ((struct NCR53c7x0_cmd *)me->host_scribble)->next = hostdata->free;
+ hostdata->free = (struct NCR53c7x0_cmd *) me->host_scribble;
+ me->host_scribble = NULL;
+ }
+ cmd->result = DID_ABORT << 16;
+ cmd->scsi_done(cmd);
+ printk ("scsi%d : found command %ld in Linux issue queue\n",
+ host->host_no, me->pid);
+ restore_flags(flags);
+ run_process_issue_queue();
+ return SCSI_ABORT_SUCCESS;
+ }
+
+/*
+ * That failing, the command could be in our list of already executing
+ * commands. If this is the case, drastic measures are called for.
+ */
+
+ for (curr = (struct NCR53c7x0_cmd *) hostdata->running_list,
+ prev = (struct NCR53c7x0_cmd **) &(hostdata->running_list);
+ curr && curr->cmd != cmd; prev = (struct NCR53c7x0_cmd **)
+ &(curr->next), curr = (struct NCR53c7x0_cmd *) curr->next);
+
+ if (curr) {
+ if ((cmd->result & 0xff) != 0xff && (cmd->result & 0xff00) != 0xff00) {
+ if (prev)
+ *prev = (struct NCR53c7x0_cmd *) curr->next;
+ curr->next = (struct NCR53c7x0_cmd *) hostdata->free;
+ cmd->host_scribble = NULL;
+ hostdata->free = curr;
+ cmd->scsi_done(cmd);
+ printk ("scsi%d : found finished command %ld in running list\n",
+ host->host_no, cmd->pid);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ } else {
+ printk ("scsi%d : DANGER : command running, can not abort.\n",
+ cmd->host->host_no);
+ restore_flags(flags);
+ return SCSI_ABORT_BUSY;
+ }
+ }
+
+/*
+ * And if we couldn't find it in any of our queues, it must have been
+ * a dropped interrupt.
+ */
+
+ curr = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (curr) {
+ curr->next = hostdata->free;
+ hostdata->free = curr;
+ cmd->host_scribble = NULL;
+ }
+
+ if (((cmd->result & 0xff00) == 0xff00) ||
+ ((cmd->result & 0xff) == 0xff)) {
+ printk ("scsi%d : did this command ever run?\n", host->host_no);
+ cmd->result = DID_ABORT << 16;
+ } else {
+ printk ("scsi%d : probably lost INTFLY, normal completion\n",
+ host->host_no);
+/*
+ * FIXME : We need to add an additional flag which indicates if a
+ * command was ever counted as BUSY, so if we end up here we can
+ * decrement the busy count if and only if it is necessary.
+ */
+ --hostdata->busy[cmd->target][cmd->lun];
+ }
+ restore_flags(flags);
+ cmd->scsi_done(cmd);
+
+/*
+ * We need to run process_issue_queue since termination of this command
+ * may allow another queued command to execute first?
+ */
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+/*
+ * Function : int NCR53c7xx_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : perform a hard reset of the SCSI bus and NCR
+ * chip.
+ *
+ * Inputs : cmd - command which caused the SCSI RESET
+ *
+ * Returns : 0 on success.
+ */
+
+int
+NCR53c7xx_reset (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ int found = 0;
+ struct NCR53c7x0_cmd * c;
+ Scsi_Cmnd *tmp;
+ /*
+ * When we call scsi_done(), it's going to wake up anything sleeping on the
+ * resources which were in use by the aborted commands, and we'll start to
+ * get new commands.
+ *
+ * We can't let this happen until after we've re-initialized the driver
+ * structures, and can't reinitilize those structures until after we've
+ * dealt with their contents.
+ *
+ * So, we need to find all of the commands which were running, stick
+ * them on a linked list of completed commands (we'll use the host_scribble
+ * pointer), do our reinitialization, and then call the done function for
+ * each command.
+ */
+ Scsi_Cmnd *nuke_list = NULL;
+ struct Scsi_Host *host = cmd->host;
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+
+ NCR53c7x0_local_setup(host);
+ save_flags(flags);
+ cli();
+ ncr_halt (host);
+ print_lots (host);
+ dump_events (host, 30);
+ ncr_scsi_reset (host);
+ for (tmp = nuke_list = return_outstanding_commands (host, 1 /* free */,
+ 0 /* issue */ ); tmp; tmp = (Scsi_Cmnd *) tmp->SCp.buffer)
+ if (tmp == cmd) {
+ found = 1;
+ break;
+ }
+
+ /*
+ * If we didn't find the command which caused this reset in our running
+ * list, then we've lost it. See that it terminates normally anyway.
+ */
+ if (!found) {
+ c = (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ if (c) {
+ cmd->host_scribble = NULL;
+ c->next = hostdata->free;
+ hostdata->free = c;
+ } else
+ printk ("scsi%d: lost command %ld\n", host->host_no, cmd->pid);
+ cmd->SCp.buffer = (struct scatterlist *) nuke_list;
+ nuke_list = cmd;
+ }
+
+ NCR53c7x0_driver_init (host);
+ hostdata->soft_reset (host);
+ if (hostdata->resets == 0)
+ disable(host);
+ else if (hostdata->resets != -1)
+ --hostdata->resets;
+ sti();
+ for (; nuke_list; nuke_list = tmp) {
+ tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer;
+ nuke_list->result = DID_RESET << 16;
+ nuke_list->scsi_done (nuke_list);
+ }
+ restore_flags(flags);
+ return SCSI_RESET_SUCCESS;
+}
+
+/*
+ * The NCR SDMS bios follows Annex A of the SCSI-CAM draft, and
+ * therefore shares the scsicam_bios_param function.
+ */
+
+/*
+ * Function : int insn_to_offset (Scsi_Cmnd *cmd, u32 *insn)
+ *
+ * Purpose : convert instructions stored at NCR pointer into data
+ * pointer offset.
+ *
+ * Inputs : cmd - SCSI command; insn - pointer to instruction. Either current
+ * DSP, or saved data pointer.
+ *
+ * Returns : offset on success, -1 on failure.
+ */
+
+
+static int
+insn_to_offset (Scsi_Cmnd *cmd, u32 *insn) {
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) cmd->host->hostdata;
+ struct NCR53c7x0_cmd *ncmd =
+ (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ int offset = 0, buffers;
+ struct scatterlist *segment;
+ char *ptr;
+ int found = 0;
+
+/*
+ * With the current code implementation, if the insn is inside dynamically
+ * generated code, the data pointer will be the instruction preceeding
+ * the next transfer segment.
+ */
+
+ if (!check_address ((unsigned long) ncmd, sizeof (struct NCR53c7x0_cmd)) &&
+ ((insn >= ncmd->data_transfer_start &&
+ insn < ncmd->data_transfer_end) ||
+ (insn >= ncmd->residual &&
+ insn < (ncmd->residual +
+ sizeof(ncmd->residual))))) {
+ ptr = bus_to_virt(insn[3]);
+
+ if ((buffers = cmd->use_sg)) {
+ for (offset = 0,
+ segment = (struct scatterlist *) cmd->buffer;
+ buffers && !((found = ((ptr >= segment->address) &&
+ (ptr < (segment->address + segment->length)))));
+ --buffers, offset += segment->length, ++segment)
+#if 0
+ printk("scsi%d: comparing 0x%p to 0x%p\n",
+ cmd->host->host_no, saved, segment->address);
+#else
+ ;
+#endif
+ offset += ptr - segment->address;
+ } else {
+ found = 1;
+ offset = ptr - (char *) (cmd->request_buffer);
+ }
+ } else if ((insn >= hostdata->script +
+ hostdata->E_data_transfer / sizeof(u32)) &&
+ (insn <= hostdata->script +
+ hostdata->E_end_data_transfer / sizeof(u32))) {
+ found = 1;
+ offset = 0;
+ }
+ return found ? offset : -1;
+}
+
+
+
+/*
+ * Function : void print_progress (Scsi_Cmnd *cmd)
+ *
+ * Purpose : print the current location of the saved data pointer
+ *
+ * Inputs : cmd - command we are interested in
+ *
+ */
+
+static void
+print_progress (Scsi_Cmnd *cmd) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_cmd *ncmd =
+ (struct NCR53c7x0_cmd *) cmd->host_scribble;
+ int offset, i;
+ char *where;
+ u32 *ptr;
+ NCR53c7x0_local_setup (cmd->host);
+ for (i = 0; i < 2; ++i) {
+ if (check_address ((unsigned long) ncmd,
+ sizeof (struct NCR53c7x0_cmd)) == -1)
+ continue;
+ if (!i) {
+ where = "saved";
+ ptr = bus_to_virt(ncmd->saved_data_pointer);
+ } else {
+ where = "active";
+ ptr = bus_to_virt (NCR53c7x0_read32 (DSP_REG) -
+ NCR53c7x0_insn_size (NCR53c7x0_read8 (DCMD_REG)) *
+ sizeof(u32));
+ }
+ offset = insn_to_offset (cmd, ptr);
+
+ if (offset != -1)
+ printk ("scsi%d : %s data pointer at offset %d\n",
+ cmd->host->host_no, where, offset);
+ else {
+ int size;
+ printk ("scsi%d : can't determine %s data pointer offset\n",
+ cmd->host->host_no, where);
+ if (ncmd) {
+ size = print_insn (cmd->host,
+ bus_to_virt(ncmd->saved_data_pointer), "", 1);
+ print_insn (cmd->host,
+ bus_to_virt(ncmd->saved_data_pointer) + size * sizeof(u32),
+ "", 1);
+ }
+ }
+ }
+}
+
+
+static void
+print_dsa (struct Scsi_Host *host, u32 *dsa, const char *prefix) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int i, len;
+ char *ptr;
+ Scsi_Cmnd *cmd;
+
+ if (check_address ((unsigned long) dsa, hostdata->dsa_end -
+ hostdata->dsa_start) == -1) {
+ printk("scsi%d : bad dsa virt 0x%p\n", host->host_no, dsa);
+ return;
+ }
+ printk("%sscsi%d : dsa at phys 0x%lx (virt 0x%p)\n"
+ " + %d : dsa_msgout length = %u, data = 0x%x (virt 0x%p)\n" ,
+ prefix ? prefix : "",
+ host->host_no, virt_to_bus (dsa), dsa, hostdata->dsa_msgout,
+ dsa[hostdata->dsa_msgout / sizeof(u32)],
+ dsa[hostdata->dsa_msgout / sizeof(u32) + 1],
+ bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]));
+
+ /*
+ * Only print messages if they're sane in length so we don't
+ * blow the kernel printk buffer on something which won't buy us
+ * anything.
+ */
+
+ if (dsa[hostdata->dsa_msgout / sizeof(u32)] <
+ sizeof (hostdata->free->select))
+ for (i = dsa[hostdata->dsa_msgout / sizeof(u32)],
+ ptr = bus_to_virt (dsa[hostdata->dsa_msgout / sizeof(u32) + 1]);
+ i > 0 && !check_address ((unsigned long) ptr, 1);
+ ptr += len, i -= len) {
+ printk(" ");
+ len = print_msg (ptr);
+ printk("\n");
+ if (!len)
+ break;
+ }
+
+ printk(" + %d : select_indirect = 0x%x\n",
+ hostdata->dsa_select, dsa[hostdata->dsa_select / sizeof(u32)]);
+ cmd = (Scsi_Cmnd *) bus_to_virt(dsa[hostdata->dsa_cmnd / sizeof(u32)]);
+ printk(" + %d : dsa_cmnd = 0x%x ", hostdata->dsa_cmnd,
+ (u32) virt_to_bus(cmd));
+ if (cmd) {
+ printk(" result = 0x%x, target = %d, lun = %d, cmd = ",
+ cmd->result, cmd->target, cmd->lun);
+ print_command(cmd->cmnd);
+ } else
+ printk("\n");
+ printk(" + %d : dsa_next = 0x%x\n", hostdata->dsa_next,
+ dsa[hostdata->dsa_next / sizeof(u32)]);
+ if (cmd) {
+ printk("scsi%d target %d : sxfer_sanity = 0x%x, scntl3_sanity = 0x%x\n"
+ " script : ",
+ host->host_no, cmd->target,
+ hostdata->sync[cmd->target].sxfer_sanity,
+ hostdata->sync[cmd->target].scntl3_sanity);
+ for (i = 0; i < (sizeof(hostdata->sync[cmd->target].script) / 4); ++i)
+ printk ("0x%x ", hostdata->sync[cmd->target].script[i]);
+ printk ("\n");
+ print_progress (cmd);
+ }
+}
+/*
+ * Function : void print_queues (Scsi_Host *host)
+ *
+ * Purpose : print the contents of the NCR issue and reconnect queues
+ *
+ * Inputs : host - SCSI host we are interested in
+ *
+ */
+
+static void
+print_queues (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ u32 *dsa, *next_dsa;
+ volatile u32 *current;
+ int left;
+ Scsi_Cmnd *cmd, *next_cmd;
+ unsigned long flags;
+
+ printk ("scsi%d : issue queue\n", host->host_no);
+
+ for (left = host->can_queue, cmd = (Scsi_Cmnd *) hostdata->issue_queue;
+ left >= 0 && cmd;
+ cmd = next_cmd) {
+ next_cmd = (Scsi_Cmnd *) cmd->SCp.ptr;
+ save_flags(flags);
+ cli();
+ if (cmd->host_scribble) {
+ if (check_address ((unsigned long) (cmd->host_scribble),
+ sizeof (cmd->host_scribble)) == -1)
+ printk ("scsi%d: scsi pid %ld bad pointer to NCR53c7x0_cmd\n",
+ host->host_no, cmd->pid);
+ /* print_dsa does sanity check on address, no need to check */
+ else
+ print_dsa (host, ((struct NCR53c7x0_cmd *) cmd->host_scribble)
+ -> dsa, "");
+ } else
+ printk ("scsi%d : scsi pid %ld for target %d lun %d has no NCR53c7x0_cmd\n",
+ host->host_no, cmd->pid, cmd->target, cmd->lun);
+ restore_flags(flags);
+ }
+
+ if (left <= 0) {
+ printk ("scsi%d : loop detected in issue queue\n",
+ host->host_no);
+ }
+
+ /*
+ * Traverse the NCR reconnect and start DSA structures, printing out
+ * each element until we hit the end or detect a loop. Currently,
+ * the reconnect structure is a linked list; and the start structure
+ * is an array. Eventually, the reconnect structure will become a
+ * list as well, since this simplifies the code.
+ */
+
+ printk ("scsi%d : schedule dsa array :\n", host->host_no);
+ for (left = host->can_queue, current = hostdata->schedule;
+ left > 0; current += 2, --left)
+ if (current[0] != hostdata->NOP_insn)
+/* FIXME : convert pointer to dsa_begin to pointer to dsa. */
+ print_dsa (host, bus_to_virt (current[1] -
+ (hostdata->E_dsa_code_begin -
+ hostdata->E_dsa_code_template)), "");
+ printk ("scsi%d : end schedule dsa array\n", host->host_no);
+
+ printk ("scsi%d : reconnect_dsa_head :\n", host->host_no);
+
+ for (left = host->can_queue,
+ dsa = bus_to_virt (hostdata->reconnect_dsa_head);
+ left >= 0 && dsa;
+ dsa = next_dsa) {
+ save_flags (flags);
+ cli();
+ if (check_address ((unsigned long) dsa, sizeof(dsa)) == -1) {
+ printk ("scsi%d: bad DSA pointer 0x%p", host->host_no,
+ dsa);
+ next_dsa = NULL;
+ }
+ else
+ {
+ next_dsa = bus_to_virt(dsa[hostdata->dsa_next / sizeof(u32)]);
+ print_dsa (host, dsa, "");
+ }
+ restore_flags(flags);
+ }
+ printk ("scsi%d : end reconnect_dsa_head\n", host->host_no);
+ if (left < 0)
+ printk("scsi%d: possible loop in ncr reconnect list\n",
+ host->host_no);
+}
+
+static void
+print_lots (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ u32 *dsp_next, *dsp, *dsa, dbc_dcmd;
+ unsigned char dcmd, sbcl;
+ int i, size;
+ NCR53c7x0_local_setup(host);
+
+ if ((dsp_next = bus_to_virt(NCR53c7x0_read32 (DSP_REG)))) {
+ dbc_dcmd = NCR53c7x0_read32(DBC_REG);
+ dcmd = (dbc_dcmd & 0xff000000) >> 24;
+ dsp = dsp_next - NCR53c7x0_insn_size(dcmd);
+ dsa = bus_to_virt(NCR53c7x0_read32(DSA_REG));
+ sbcl = NCR53c7x0_read8 (SBCL_REG);
+
+
+ printk ("scsi%d : DCMD|DBC=0x%x, DNAD=0x%x (virt 0x%p)\n"
+ " DSA=0x%lx (virt 0x%p)\n"
+ " DSPS=0x%x, TEMP=0x%x (virt 0x%p), DMODE=0x%x\n"
+ " SXFER=0x%x, SCNTL3=0x%x\n"
+ " %s%s%sphase=%s, %d bytes in SCSI FIFO\n"
+ " STEST0=0x%x\n",
+ host->host_no, dbc_dcmd, NCR53c7x0_read32(DNAD_REG),
+ bus_to_virt(NCR53c7x0_read32(DNAD_REG)),
+ virt_to_bus(dsa), dsa,
+ NCR53c7x0_read32(DSPS_REG), NCR53c7x0_read32(TEMP_REG),
+ bus_to_virt (NCR53c7x0_read32(TEMP_REG)),
+ (int) NCR53c7x0_read8(hostdata->dmode),
+ (int) NCR53c7x0_read8(SXFER_REG),
+ (int) NCR53c7x0_read8(SCNTL3_REG_800),
+ (sbcl & SBCL_BSY) ? "BSY " : "",
+ (sbcl & SBCL_SEL) ? "SEL " : "",
+ (sbcl & SBCL_REQ) ? "REQ " : "",
+ sstat2_to_phase(NCR53c7x0_read8 (((hostdata->chip / 100) == 8) ?
+ SSTAT1_REG : SSTAT2_REG)),
+ (NCR53c7x0_read8 ((hostdata->chip / 100) == 8 ?
+ SSTAT1_REG : SSTAT2_REG) & SSTAT2_FF_MASK) >> SSTAT2_FF_SHIFT,
+ NCR53c7x0_read8 (STEST0_REG_800));
+ printk ("scsi%d : DSP 0x%lx (virt 0x%p) ->\n", host->host_no,
+ virt_to_bus(dsp), dsp);
+ for (i = 6; i > 0; --i, dsp += size)
+ size = print_insn (host, dsp, "", 1);
+ if (NCR53c7x0_read8 (SCNTL1_REG) & SCNTL1_CON) {
+ printk ("scsi%d : connected (SDID=0x%x, SSID=0x%x)\n",
+ host->host_no, NCR53c7x0_read8 (SDID_REG_800),
+ NCR53c7x0_read8 (SSID_REG_800));
+ print_dsa (host, dsa, "");
+ }
+
+#if 1
+ print_queues (host);
+#endif
+ }
+}
+
+/*
+ * Function : static int shutdown (struct Scsi_Host *host)
+ *
+ * Purpose : does a clean (we hope) shutdown of the NCR SCSI
+ * chip. Use prior to dumping core, unloading the NCR driver,
+ *
+ * Returns : 0 on success
+ */
+static int
+shutdown (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ NCR53c7x0_local_setup(host);
+ save_flags (flags);
+ cli();
+/* Get in a state where we can reset the SCSI bus */
+ ncr_halt (host);
+ ncr_scsi_reset (host);
+ hostdata->soft_reset(host);
+
+ disable (host);
+ restore_flags (flags);
+ return 0;
+}
+
+/*
+ * Function : void ncr_scsi_reset (struct Scsi_Host *host)
+ *
+ * Purpose : reset the SCSI bus.
+ */
+
+static void
+ncr_scsi_reset (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ int sien = 0;
+ NCR53c7x0_local_setup(host);
+ save_flags (flags);
+ cli();
+ if ((hostdata->chip / 100) == 8) {
+ sien = NCR53c7x0_read8(SIEN0_REG_800);
+ NCR53c7x0_write8(SIEN0_REG_800, sien & ~SIEN_RST);
+ }
+ NCR53c7x0_write8(SCNTL1_REG, SCNTL1_RST);
+ udelay(25); /* Minimum amount of time to assert RST */
+ NCR53c7x0_write8(SCNTL1_REG, 0);
+ if ((hostdata->chip / 100) == 8) {
+ NCR53c7x0_write8(SIEN0_REG_800, sien);
+ }
+ restore_flags (flags);
+}
+
+/*
+ * Function : void hard_reset (struct Scsi_Host *host)
+ *
+ */
+
+static void
+hard_reset (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ save_flags (flags);
+ cli();
+ ncr_scsi_reset(host);
+ NCR53c7x0_driver_init (host);
+ if (hostdata->soft_reset)
+ hostdata->soft_reset (host);
+ restore_flags(flags);
+}
+
+
+/*
+ * Function : Scsi_Cmnd *return_outstanding_commands (struct Scsi_Host *host,
+ * int free, int issue)
+ *
+ * Purpose : return a linked list (using the SCp.buffer field as next,
+ * so we don't perturb hostdata. We don't use a field of the
+ * NCR53c7x0_cmd structure since we may not have allocated one
+ * for the command causing the reset.) of Scsi_Cmnd structures that
+ * had propogated bellow the Linux issue queue level. If free is set,
+ * free the NCR53c7x0_cmd structures which are associated with
+ * the Scsi_Cmnd structures, and clean up any internal
+ * NCR lists that the commands were on. If issue is set,
+ * also return commands in the issue queue.
+ *
+ * Returns : linked list of commands
+ *
+ * NOTE : the caller should insure that the NCR chip is halted
+ * if the free flag is set.
+ */
+
+static Scsi_Cmnd *
+return_outstanding_commands (struct Scsi_Host *host, int free, int issue) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_cmd *c;
+ int i;
+ u32 *current;
+ Scsi_Cmnd *list = NULL, *tmp;
+ for (c = (struct NCR53c7x0_cmd *) hostdata->running_list; c;
+ c = (struct NCR53c7x0_cmd *) c->next) {
+ if (c->cmd->SCp.buffer) {
+ printk ("scsi%d : loop detected in running list!\n", host->host_no);
+ break;
+ } else {
+ printk ("The sti() implicit in a printk() prevents hangs\n");
+ break;
+ }
+
+ c->cmd->SCp.buffer = (struct scatterlist *) list;
+ list = c->cmd;
+ if (free) {
+ c->next = hostdata->free;
+ hostdata->free = c;
+ }
+ }
+
+ if (free) {
+ for (i = 0, current = (u32 *) hostdata->schedule;
+ i < host->can_queue; ++i, current += 2) {
+ current[0] = hostdata->NOP_insn;
+ current[1] = 0xdeadbeef;
+ }
+ hostdata->current = NULL;
+ }
+
+ if (issue) {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp; tmp = tmp->next) {
+ if (tmp->SCp.buffer) {
+ printk ("scsi%d : loop detected in issue queue!\n",
+ host->host_no);
+ break;
+ }
+ tmp->SCp.buffer = (struct scatterlist *) list;
+ list = tmp;
+ }
+ if (free)
+ hostdata->issue_queue = NULL;
+
+ }
+ return list;
+}
+
+/*
+ * Function : static int disable (struct Scsi_Host *host)
+ *
+ * Purpose : disables the given NCR host, causing all commands
+ * to return a driver error. Call this so we can unload the
+ * module during development and try again. Eventually,
+ * we should be able to find clean workarrounds for these
+ * problems.
+ *
+ * Inputs : host - hostadapter to twiddle
+ *
+ * Returns : 0 on success.
+ */
+
+static int
+disable (struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ unsigned long flags;
+ Scsi_Cmnd *nuke_list, *tmp;
+ save_flags(flags);
+ cli();
+ if (hostdata->state != STATE_HALTED)
+ ncr_halt (host);
+ nuke_list = return_outstanding_commands (host, 1 /* free */, 1 /* issue */);
+ hard_reset (host);
+ hostdata->state = STATE_DISABLED;
+ restore_flags(flags);
+ printk ("scsi%d : nuking commands\n", host->host_no);
+ for (; nuke_list; nuke_list = tmp) {
+ tmp = (Scsi_Cmnd *) nuke_list->SCp.buffer;
+ nuke_list->result = DID_ERROR << 16;
+ nuke_list->scsi_done(nuke_list);
+ }
+ printk ("scsi%d : done. \n", host->host_no);
+ printk (KERN_ALERT "scsi%d : disabled. Unload and reload\n",
+ host->host_no);
+ return 0;
+}
+
+/*
+ * Function : static int ncr_halt (struct Scsi_Host *host)
+ *
+ * Purpose : halts the SCSI SCRIPTS(tm) processor on the NCR chip
+ *
+ * Inputs : host - SCSI chip to halt
+ *
+ * Returns : 0 on success
+ */
+
+static int
+ncr_halt (struct Scsi_Host *host) {
+ NCR53c7x0_local_declare();
+ unsigned long flags;
+ unsigned char istat, tmp;
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ int stage;
+ NCR53c7x0_local_setup(host);
+
+ save_flags(flags);
+ cli();
+ /* Stage 0 : eat all interrupts
+ Stage 1 : set ABORT
+ Stage 2 : eat all but abort interrupts
+ Stage 3 : eat all interrupts
+ */
+ for (stage = 0;;) {
+ if (stage == 1) {
+ NCR53c7x0_write8(hostdata->istat, ISTAT_ABRT);
+ ++stage;
+ }
+ istat = NCR53c7x0_read8 (hostdata->istat);
+ if (istat & ISTAT_SIP) {
+ if ((hostdata->chip / 100) == 8) {
+ tmp = NCR53c7x0_read8(SIST0_REG_800);
+ udelay(1);
+ tmp = NCR53c7x0_read8(SIST1_REG_800);
+ } else {
+ tmp = NCR53c7x0_read8(SSTAT0_REG);
+ }
+ } else if (istat & ISTAT_DIP) {
+ tmp = NCR53c7x0_read8(DSTAT_REG);
+ if (stage == 2) {
+ if (tmp & DSTAT_ABRT) {
+ NCR53c7x0_write8(hostdata->istat, 0);
+ ++stage;
+ } else {
+ printk(KERN_ALERT "scsi%d : could not halt NCR chip\n",
+ host->host_no);
+ disable (host);
+ }
+ }
+ }
+ if (!(istat & (ISTAT_SIP|ISTAT_DIP)))
+ if (stage == 0)
+ ++stage;
+ else if (stage == 3)
+ break;
+ }
+ hostdata->state = STATE_HALTED;
+ restore_flags(flags);
+#if 0
+ print_lots (host);
+#endif
+ return 0;
+}
+
+/*
+ * Function: event_name (int event)
+ *
+ * Purpose: map event enum into user-readable strings.
+ */
+
+static const char *
+event_name (int event) {
+ switch (event) {
+ case EVENT_NONE: return "none";
+ case EVENT_ISSUE_QUEUE: return "to issue queue";
+ case EVENT_START_QUEUE: return "to start queue";
+ case EVENT_SELECT: return "selected";
+ case EVENT_DISCONNECT: return "disconnected";
+ case EVENT_RESELECT: return "reselected";
+ case EVENT_COMPLETE: return "completed";
+ case EVENT_IDLE: return "idle";
+ case EVENT_SELECT_FAILED: return "select failed";
+ case EVENT_BEFORE_SELECT: return "before select";
+ case EVENT_RESELECT_FAILED: return "reselect failed";
+ default: return "unknown";
+ }
+}
+
+/*
+ * Function : void dump_events (struct Scsi_Host *host, count)
+ *
+ * Purpose : print last count events which have occurred.
+ */
+static void
+dump_events (struct Scsi_Host *host, int count) {
+ struct NCR53c7x0_hostdata *hostdata = (struct NCR53c7x0_hostdata *)
+ host->hostdata;
+ struct NCR53c7x0_event event;
+ int i;
+ unsigned long flags;
+ if (hostdata->events) {
+ if (count > hostdata->event_size)
+ count = hostdata->event_size;
+ for (i = hostdata->event_index; count > 0;
+ i = (i ? i - 1 : hostdata->event_size -1), --count) {
+ save_flags(flags);
+/*
+ * By copying the event we're currently examinging with interrupts
+ * disabled, we can do multiple printk(), etc. operations and
+ * still be guaranteed that they're happening on the same
+ * event structure.
+ */
+ cli();
+#if 0
+ event = hostdata->events[i];
+#else
+ memcpy ((void *) &event, (void *) &(hostdata->events[i]),
+ sizeof(event));
+#endif
+
+ restore_flags(flags);
+ printk ("scsi%d : %s event %d at %ld secs %ld usecs target %d lun %d\n",
+ host->host_no, event_name (event.event), count,
+ (long) event.time.tv_sec, (long) event.time.tv_usec,
+ event.target, event.lun);
+ if (event.dsa)
+ printk (" event for dsa 0x%lx (virt 0x%p)\n",
+ virt_to_bus(event.dsa), event.dsa);
+ if (event.pid != -1) {
+ printk (" event for pid %ld ", event.pid);
+ print_command (event.cmnd);
+ }
+ }
+ }
+}
+
+/*
+ * Function: check_address
+ *
+ * Purpose: Check to see if a possibly corrupt pointer will fault the
+ * kernel.
+ *
+ * Inputs: addr - address; size - size of area
+ *
+ * Returns: 0 if area is OK, -1 on error.
+ *
+ * NOTES: should be implemented in terms of vverify on kernels
+ * that have it.
+ */
+
+static int
+check_address (unsigned long addr, int size) {
+ return (MAP_NR(addr) < 1 || MAP_NR(addr + size) > MAP_NR(high_memory) ?
+ -1 : 0);
+}
+
+#ifdef MODULE
+int
+NCR53c7x0_release(struct Scsi_Host *host) {
+ struct NCR53c7x0_hostdata *hostdata =
+ (struct NCR53c7x0_hostdata *) host->hostdata;
+ struct NCR53c7x0_cmd *cmd, *tmp;
+ shutdown (host);
+ if (host->irq != IRQ_NONE)
+ {
+ int irq_count;
+ struct Scsi_Host *tmp;
+ for (irq_count = 0, tmp = first_host; tmp; tmp = tmp->next)
+ if (tmp->hostt == the_template && tmp->irq == host->irq)
+ ++irq_count;
+ if (irq_count == 1)
+ free_irq(host->irq);
+ }
+ if (host->dma_channel != DMA_NONE)
+ free_dma(host->dma_channel);
+ if (host->io_port)
+ release_region(host->io_port, host->n_io_port);
+
+ for (cmd = (struct NCR53c7x0_cmd *) hostdata->free; cmd; cmd = tmp,
+ --hostdata->num_cmds) {
+ tmp = (struct NCR53c7x0_cmd *) cmd->next;
+ /*
+ * If we're going to loop, try to stop it to get a more accurate
+ * count of the leaked commands.
+ */
+ cmd->next = NULL;
+ if (cmd->free)
+ cmd->free ((void *) cmd->real, cmd->size);
+ }
+ if (hostdata->num_cmds)
+ printk ("scsi%d : leaked %d NCR53c7x0_cmd structures\n",
+ host->host_no, hostdata->num_cmds);
+ if (hostdata->events)
+ vfree ((void *)hostdata->events);
+ return 1;
+}
+Scsi_Host_Template driver_template = NCR53c7xx;
+#include "scsi_module.c"
+#endif /* def MODULE */
diff --git a/i386/i386at/gpl/linux/scsi/53c7,8xx.h b/i386/i386at/gpl/linux/scsi/53c7,8xx.h
new file mode 100644
index 00000000..f1dfc4de
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/53c7,8xx.h
@@ -0,0 +1,1584 @@
+/*
+ * NCR 53c{7,8}0x0 driver, header file
+ *
+ * Sponsored by
+ * iX Multiuser Multitasking Magazine
+ * Hannover, Germany
+ * hm@ix.de
+ *
+ * Copyright 1993, 1994, 1995 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@PoohSticks.ORG
+ * +1 (303) 786-7975
+ *
+ * TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+ *
+ * PRE-ALPHA
+ *
+ * For more information, please consult
+ *
+ * NCR 53C700/53C700-66
+ * SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR 53C810
+ * PCI-SCSI I/O Processor
+ * Data Manual
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * +1 (719) 578-3400
+ *
+ * Toll free literature number
+ * +1 (800) 334-5454
+ *
+ */
+
+#ifndef NCR53c7x0_H
+#define NCR53c7x0_H
+#if !defined(LINUX_1_2) && !defined(LINUX_1_3)
+#include <linux/version.h>
+#if LINUX_VERSION_CODE > 65536 + 3 * 256
+#define LINUX_1_3
+#else
+#define LINUX_1_2
+#endif
+#endif
+
+/*
+ * Prevent name space pollution in hosts.c, and only provide the
+ * define we need to get the NCR53c7x0 driver into the host template
+ * array.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+#include <linux/scsicam.h>
+
+extern int NCR53c7xx_abort(Scsi_Cmnd *);
+extern int NCR53c7xx_detect(Scsi_Host_Template *tpnt);
+extern int NCR53c7xx_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+extern int NCR53c7xx_reset(Scsi_Cmnd *);
+#ifdef MODULE
+extern int NCR53c7xx_release(struct Scsi_Host *);
+#else
+#define NCR53c7xx_release NULL
+#endif
+
+#ifdef LINUX_1_2
+#define NCR53c7xx {NULL, NULL, "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\
+ NULL, /* info */ NULL, /* command, deprecated */ NULL, \
+ NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \
+ NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \
+ /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \
+ /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
+#else
+#define NCR53c7xx {NULL, NULL, NULL, NULL, \
+ "NCR53c{7,8}xx (rel 17)", NCR53c7xx_detect,\
+ NULL, /* info */ NULL, /* command, deprecated */ NULL, \
+ NCR53c7xx_queue_command, NCR53c7xx_abort, NCR53c7xx_reset, \
+ NULL /* slave attach */, scsicam_bios_param, /* can queue */ 24, \
+ /* id */ 7, 127 /* old SG_ALL */, /* cmd per lun */ 3, \
+ /* present */ 0, /* unchecked isa dma */ 0, DISABLE_CLUSTERING}
+#endif
+
+#endif /* defined(HOSTS_C) || defined(MODULE) */
+
+#ifndef HOSTS_C
+#ifdef LINUX_1_2
+/*
+ * Change virtual addresses to physical addresses and vv.
+ * These are trivial on the 1:1 Linux/i386 mapping (but if we ever
+ * make the kernel segment mapped at 0, we need to do translation
+ * on the i386 as well)
+ */
+extern inline unsigned long virt_to_phys(volatile void * address)
+{
+ return (unsigned long) address;
+}
+
+extern inline void * phys_to_virt(unsigned long address)
+{
+ return (void *) address;
+}
+
+/*
+ * IO bus memory addresses are also 1:1 with the physical address
+ */
+#define virt_to_bus virt_to_phys
+#define bus_to_virt phys_to_virt
+
+/*
+ * readX/writeX() are used to access memory mapped devices. On some
+ * architectures the memory mapped IO stuff needs to be accessed
+ * differently. On the x86 architecture, we just read/write the
+ * memory location directly.
+ */
+#define readb(addr) (*(volatile unsigned char *) (addr))
+#define readw(addr) (*(volatile unsigned short *) (addr))
+#define readl(addr) (*(volatile unsigned int *) (addr))
+
+#define writeb(b,addr) ((*(volatile unsigned char *) (addr)) = (b))
+#define writew(b,addr) ((*(volatile unsigned short *) (addr)) = (b))
+#define writel(b,addr) ((*(volatile unsigned int *) (addr)) = (b))
+
+#define mb()
+
+#endif /* def LINUX_1_2 */
+
+/* Register addresses, ordered numerically */
+
+/* SCSI control 0 rw, default = 0xc0 */
+#define SCNTL0_REG 0x00
+#define SCNTL0_ARB1 0x80 /* 0 0 = simple arbitration */
+#define SCNTL0_ARB2 0x40 /* 1 1 = full arbitration */
+#define SCNTL0_STRT 0x20 /* Start Sequence */
+#define SCNTL0_WATN 0x10 /* Select with ATN */
+#define SCNTL0_EPC 0x08 /* Enable parity checking */
+/* Bit 2 is reserved on 800 series chips */
+#define SCNTL0_EPG_700 0x04 /* Enable parity generation */
+#define SCNTL0_AAP 0x02 /* ATN/ on parity error */
+#define SCNTL0_TRG 0x01 /* Target mode */
+
+/* SCSI control 1 rw, default = 0x00 */
+
+#define SCNTL1_REG 0x01
+#define SCNTL1_EXC 0x80 /* Extra Clock Cycle of Data setup */
+#define SCNTL1_ADB 0x40 /* contents of SODL on bus */
+#define SCNTL1_ESR_700 0x20 /* Enable SIOP response to selection
+ and reselection */
+#define SCNTL1_DHP_800 0x20 /* Disable halt on parity error or ATN
+ target mode only */
+#define SCNTL1_CON 0x10 /* Connected */
+#define SCNTL1_RST 0x08 /* SCSI RST/ */
+#define SCNTL1_AESP 0x04 /* Force bad parity */
+#define SCNTL1_SND_700 0x02 /* Start SCSI send */
+#define SCNTL1_IARB_800 0x02 /* Immediate Arbitration, start
+ arbitration immediately after
+ busfree is detected */
+#define SCNTL1_RCV_700 0x01 /* Start SCSI receive */
+#define SCNTL1_SST_800 0x01 /* Start SCSI transfer */
+
+/* SCSI control 2 rw, */
+
+#define SCNTL2_REG_800 0x02
+#define SCNTL2_800_SDU 0x80 /* SCSI disconnect unexpected */
+
+/* SCSI control 3 rw */
+
+#define SCNTL3_REG_800 0x03
+#define SCNTL3_800_SCF_SHIFT 4
+#define SCNTL3_800_SCF_MASK 0x70
+#define SCNTL3_800_SCF2 0x40 /* Synchronous divisor */
+#define SCNTL3_800_SCF1 0x20 /* 0x00 = SCLK/3 */
+#define SCNTL3_800_SCF0 0x10 /* 0x10 = SCLK/1 */
+ /* 0x20 = SCLK/1.5
+ 0x30 = SCLK/2
+ 0x40 = SCLK/3 */
+
+#define SCNTL3_800_CCF_SHIFT 0
+#define SCNTL3_800_CCF_MASK 0x07
+#define SCNTL3_800_CCF2 0x04 /* 0x00 50.01 to 66 */
+#define SCNTL3_800_CCF1 0x02 /* 0x01 16.67 to 25 */
+#define SCNTL3_800_CCF0 0x01 /* 0x02 25.01 - 37.5
+ 0x03 37.51 - 50
+ 0x04 50.01 - 66 */
+
+/*
+ * SCSI destination ID rw - the appropriate bit is set for the selected
+ * target ID. This is written by the SCSI SCRIPTS processor.
+ * default = 0x00
+ */
+#define SDID_REG_700 0x02
+#define SDID_REG_800 0x06
+
+#define GP_REG_800 0x07 /* General purpose IO */
+#define GP_800_IO1 0x02
+#define GP_800_IO2 0x01
+
+
+/* SCSI interrupt enable rw, default = 0x00 */
+#define SIEN_REG_700 0x03
+#define SIEN0_REG_800 0x40
+#define SIEN_MA 0x80 /* Phase mismatch (ini) or ATN (tgt) */
+#define SIEN_FC 0x40 /* Function complete */
+#define SIEN_700_STO 0x20 /* Selection or reselection timeout */
+#define SIEN_800_SEL 0x20 /* Selected */
+#define SIEN_700_SEL 0x10 /* Selected or reselected */
+#define SIEN_800_RESEL 0x10 /* Reselected */
+#define SIEN_SGE 0x08 /* SCSI gross error */
+#define SIEN_UDC 0x04 /* Unexpected disconnect */
+#define SIEN_RST 0x02 /* SCSI RST/ received */
+#define SIEN_PAR 0x01 /* Parity error */
+
+/*
+ * SCSI chip ID rw
+ * NCR53c700 :
+ * When arbitrating, the highest bit is used, when reselection or selection
+ * occurs, the chip responds to all IDs for which a bit is set.
+ * default = 0x00
+ * NCR53c810 :
+ * Uses bit mapping
+ */
+#define SCID_REG 0x04
+/* Bit 7 is reserved on 800 series chips */
+#define SCID_800_RRE 0x40 /* Enable response to reselection */
+#define SCID_800_SRE 0x20 /* Enable response to selection */
+/* Bits four and three are reserved on 800 series chips */
+#define SCID_800_ENC_MASK 0x07 /* Encoded SCSI ID */
+
+/* SCSI transfer rw, default = 0x00 */
+#define SXFER_REG 0x05
+#define SXFER_DHP 0x80 /* Disable halt on parity */
+
+#define SXFER_TP2 0x40 /* Transfer period msb */
+#define SXFER_TP1 0x20
+#define SXFER_TP0 0x10 /* lsb */
+#define SXFER_TP_MASK 0x70
+/* FIXME : SXFER_TP_SHIFT == 5 is right for '8xx chips */
+#define SXFER_TP_SHIFT 5
+#define SXFER_TP_4 0x00 /* Divisors */
+#define SXFER_TP_5 0x10<<1
+#define SXFER_TP_6 0x20<<1
+#define SXFER_TP_7 0x30<<1
+#define SXFER_TP_8 0x40<<1
+#define SXFER_TP_9 0x50<<1
+#define SXFER_TP_10 0x60<<1
+#define SXFER_TP_11 0x70<<1
+
+#define SXFER_MO3 0x08 /* Max offset msb */
+#define SXFER_MO2 0x04
+#define SXFER_MO1 0x02
+#define SXFER_MO0 0x01 /* lsb */
+#define SXFER_MO_MASK 0x0f
+#define SXFER_MO_SHIFT 0
+
+/*
+ * SCSI output data latch rw
+ * The contents of this register are driven onto the SCSI bus when
+ * the Assert Data Bus bit of the SCNTL1 register is set and
+ * the CD, IO, and MSG bits of the SOCL register match the SCSI phase
+ */
+#define SODL_REG_700 0x06
+#define SODL_REG_800 0x54
+
+
+/*
+ * SCSI output control latch rw, default = 0
+ * Note that when the chip is being manually programmed as an initiator,
+ * the MSG, CD, and IO bits must be set correctly for the phase the target
+ * is driving the bus in. Otherwise no data transfer will occur due to
+ * phase mismatch.
+ */
+
+#define SBCL_REG 0x0b
+#define SBCL_REQ 0x80 /* REQ */
+#define SBCL_ACK 0x40 /* ACK */
+#define SBCL_BSY 0x20 /* BSY */
+#define SBCL_SEL 0x10 /* SEL */
+#define SBCL_ATN 0x08 /* ATN */
+#define SBCL_MSG 0x04 /* MSG */
+#define SBCL_CD 0x02 /* C/D */
+#define SBCL_IO 0x01 /* I/O */
+#define SBCL_PHASE_CMDOUT SBCL_CD
+#define SBCL_PHASE_DATAIN SBCL_IO
+#define SBCL_PHASE_DATAOUT 0
+#define SBCL_PHASE_MSGIN (SBCL_CD|SBCL_IO|SBCL_MSG)
+#define SBCL_PHASE_MSGOUT (SBCL_CD|SBCL_MSG)
+#define SBCL_PHASE_STATIN (SBCL_CD|SBCL_IO)
+#define SBCL_PHASE_MASK (SBCL_CD|SBCL_IO|SBCL_MSG)
+
+/*
+ * SCSI first byte received latch ro
+ * This register contains the first byte received during a block MOVE
+ * SCSI SCRIPTS instruction, including
+ *
+ * Initiator mode Target mode
+ * Message in Command
+ * Status Message out
+ * Data in Data out
+ *
+ * It also contains the selecting or reselecting device's ID and our
+ * ID.
+ *
+ * Note that this is the register the various IF conditionals can
+ * operate on.
+ */
+#define SFBR_REG 0x08
+
+/*
+ * SCSI input data latch ro
+ * In initiator mode, data is latched into this register on the rising
+ * edge of REQ/. In target mode, data is latched on the rising edge of
+ * ACK/
+ */
+#define SIDL_REG_700 0x09
+#define SIDL_REG_800 0x50
+
+/*
+ * SCSI bus data lines ro
+ * This register reflects the instantaneous status of the SCSI data
+ * lines. Note that SCNTL0 must be set to disable parity checking,
+ * otherwise reading this register will latch new parity.
+ */
+#define SBDL_REG_700 0x0a
+#define SBDL_REG_800 0x58
+
+#define SSID_REG_800 0x0a
+#define SSID_800_VAL 0x80 /* Exactly two bits asserted at sel */
+#define SSID_800_ENCID_MASK 0x07 /* Device which performed operation */
+
+
+/*
+ * SCSI bus control lines rw,
+ * instantaneous readout of control lines
+ */
+#define SOCL_REG 0x0b
+#define SOCL_REQ 0x80 /* REQ ro */
+#define SOCL_ACK 0x40 /* ACK ro */
+#define SOCL_BSY 0x20 /* BSY ro */
+#define SOCL_SEL 0x10 /* SEL ro */
+#define SOCL_ATN 0x08 /* ATN ro */
+#define SOCL_MSG 0x04 /* MSG ro */
+#define SOCL_CD 0x02 /* C/D ro */
+#define SOCL_IO 0x01 /* I/O ro */
+/*
+ * Synchronous SCSI Clock Control bits
+ * 0 - set by DCNTL
+ * 1 - SCLK / 1.0
+ * 2 - SCLK / 1.5
+ * 3 - SCLK / 2.0
+ */
+#define SBCL_SSCF1 0x02 /* wo, -66 only */
+#define SBCL_SSCF0 0x01 /* wo, -66 only */
+#define SBCL_SSCF_MASK 0x03
+
+/*
+ * XXX note : when reading the DSTAT and STAT registers to clear interrupts,
+ * insure that 10 clocks elapse between the two
+ */
+/* DMA status ro */
+#define DSTAT_REG 0x0c
+#define DSTAT_DFE 0x80 /* DMA FIFO empty */
+#define DSTAT_800_MDPE 0x40 /* Master Data Parity Error */
+#define DSTAT_800_BF 0x20 /* Bus Fault */
+#define DSTAT_ABRT 0x10 /* Aborted - set on error */
+#define DSTAT_SSI 0x08 /* SCRIPTS single step interrupt */
+#define DSTAT_SIR 0x04 /* SCRIPTS interrupt received -
+ set when INT instruction is
+ executed */
+#define DSTAT_WTD 0x02 /* Watchdog timeout detected */
+#define DSTAT_OPC 0x01 /* Illegal instruction */
+#define DSTAT_800_IID 0x01 /* Same thing, different name */
+
+
+/* NCR53c800 moves this stuff into SIST0 */
+#define SSTAT0_REG 0x0d /* SCSI status 0 ro */
+#define SIST0_REG_800 0x42
+#define SSTAT0_MA 0x80 /* ini : phase mismatch,
+ * tgt : ATN/ asserted
+ */
+#define SSTAT0_CMP 0x40 /* function complete */
+#define SSTAT0_700_STO 0x20 /* Selection or reselection timeout */
+#define SIST0_800_SEL 0x20 /* Selected */
+#define SSTAT0_700_SEL 0x10 /* Selected or reselected */
+#define SIST0_800_RSL 0x10 /* Reselected */
+#define SSTAT0_SGE 0x08 /* SCSI gross error */
+#define SSTAT0_UDC 0x04 /* Unexpected disconnect */
+#define SSTAT0_RST 0x02 /* SCSI RST/ received */
+#define SSTAT0_PAR 0x01 /* Parity error */
+
+/* And uses SSTAT0 for what was SSTAT1 */
+
+#define SSTAT1_REG 0x0e /* SCSI status 1 ro */
+#define SSTAT1_ILF 0x80 /* SIDL full */
+#define SSTAT1_ORF 0x40 /* SODR full */
+#define SSTAT1_OLF 0x20 /* SODL full */
+#define SSTAT1_AIP 0x10 /* Arbitration in progress */
+#define SSTAT1_LOA 0x08 /* Lost arbitration */
+#define SSTAT1_WOA 0x04 /* Won arbitration */
+#define SSTAT1_RST 0x02 /* Instant readout of RST/ */
+#define SSTAT1_SDP 0x01 /* Instant readout of SDP/ */
+
+#define SSTAT2_REG 0x0f /* SCSI status 2 ro */
+#define SSTAT2_FF3 0x80 /* number of bytes in synchronous */
+#define SSTAT2_FF2 0x40 /* data FIFO */
+#define SSTAT2_FF1 0x20
+#define SSTAT2_FF0 0x10
+#define SSTAT2_FF_MASK 0xf0
+#define SSTAT2_FF_SHIFT 4
+
+/*
+ * Latched signals, latched on the leading edge of REQ/ for initiators,
+ * ACK/ for targets.
+ */
+#define SSTAT2_SDP 0x08 /* SDP */
+#define SSTAT2_MSG 0x04 /* MSG */
+#define SSTAT2_CD 0x02 /* C/D */
+#define SSTAT2_IO 0x01 /* I/O */
+#define SSTAT2_PHASE_CMDOUT SSTAT2_CD
+#define SSTAT2_PHASE_DATAIN SSTAT2_IO
+#define SSTAT2_PHASE_DATAOUT 0
+#define SSTAT2_PHASE_MSGIN (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG)
+#define SSTAT2_PHASE_MSGOUT (SSTAT2_CD|SSTAT2_MSG)
+#define SSTAT2_PHASE_STATIN (SSTAT2_CD|SSTAT2_IO)
+#define SSTAT2_PHASE_MASK (SSTAT2_CD|SSTAT2_IO|SSTAT2_MSG)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHA_REG_00 0x10 /* through 0x13 Scratch A rw */
+/* NCR53c710 and higher */
+#define DSA_REG 0x10 /* DATA structure address */
+
+#define CTEST0_REG_700 0x14 /* Chip test 0 ro */
+#define CTEST0_REG_800 0x18 /* Chip test 0 rw, general purpose */
+/* 0x80 - 0x04 are reserved */
+#define CTEST0_700_RTRG 0x02 /* Real target mode */
+#define CTEST0_700_DDIR 0x01 /* Data direction, 1 =
+ * SCSI bus to host, 0 =
+ * host to SCSI.
+ */
+
+#define CTEST1_REG_700 0x15 /* Chip test 1 ro */
+#define CTEST1_REG_800 0x19 /* Chip test 1 ro */
+#define CTEST1_FMT3 0x80 /* Identify which byte lanes are empty */
+#define CTEST1_FMT2 0x40 /* in the DMA FIFO */
+#define CTEST1_FMT1 0x20
+#define CTEST1_FMT0 0x10
+
+#define CTEST1_FFL3 0x08 /* Identify which bytes lanes are full */
+#define CTEST1_FFL2 0x04 /* in the DMA FIFO */
+#define CTEST1_FFL1 0x02
+#define CTEST1_FFL0 0x01
+
+#define CTEST2_REG_700 0x16 /* Chip test 2 ro */
+#define CTEST2_REG_800 0x1a /* Chip test 2 ro */
+
+#define CTEST2_800_DDIR 0x80 /* 1 = SCSI->host */
+#define CTEST2_800_SIGP 0x40 /* A copy of SIGP in ISTAT.
+ Reading this register clears */
+#define CTEST2_800_CIO 0x20 /* Configured as IO */.
+#define CTEST2_800_CM 0x10 /* Configured as memory */
+
+/* 0x80 - 0x40 are reserved on 700 series chips */
+#define CTEST2_700_SOFF 0x20 /* SCSI Offset Compare,
+ * As an initiator, this bit is
+ * one when the synchronous offset
+ * is zero, as a target this bit
+ * is one when the synchronous
+ * offset is at the maximum
+ * defined in SXFER
+ */
+#define CTEST2_700_SFP 0x10 /* SCSI FIFO parity bit,
+ * reading CTEST3 unloads a byte
+ * from the FIFO and sets this
+ */
+#define CTEST2_700_DFP 0x08 /* DMA FIFO parity bit,
+ * reading CTEST6 unloads a byte
+ * from the FIFO and sets this
+ */
+#define CTEST2_TEOP 0x04 /* SCSI true end of process,
+ * indicates a totally finished
+ * transfer
+ */
+#define CTEST2_DREQ 0x02 /* Data request signal */
+/* 0x01 is reserved on 700 series chips */
+#define CTEST2_800_DACK 0x01
+
+/*
+ * Chip test 3 ro
+ * Unloads the bottom byte of the eight deep SCSI synchronous FIFO,
+ * check SSTAT2 FIFO full bits to determine size. Note that a GROSS
+ * error results if a read is attempted on this register. Also note
+ * that 16 and 32 bit reads of this register will cause corruption.
+ */
+#define CTEST3_REG_700 0x17
+/* Chip test 3 rw */
+#define CTEST3_REG_800 0x1b
+#define CTEST3_800_V3 0x80 /* Chip revision */
+#define CTEST3_800_V2 0x40
+#define CTEST3_800_V1 0x20
+#define CTEST3_800_V0 0x10
+#define CTEST3_800_FLF 0x08 /* Flush DMA FIFO */
+#define CTEST3_800_CLF 0x04 /* Clear DMA FIFO */
+#define CTEST3_800_FM 0x02 /* Fetch mode pin */
+/* bit 0 is reserved on 800 series chips */
+
+#define CTEST4_REG_700 0x18 /* Chip test 4 rw */
+#define CTEST4_REG_800 0x21 /* Chip test 4 rw */
+/* 0x80 is reserved on 700 series chips */
+#define CTEST4_800_BDIS 0x80 /* Burst mode disable */
+#define CTEST4_ZMOD 0x40 /* High impedance mode */
+#define CTEST4_SZM 0x20 /* SCSI bus high impedance */
+#define CTEST4_700_SLBE 0x10 /* SCSI loopback enabled */
+#define CTEST4_800_SRTM 0x10 /* Shadow Register Test Mode */
+#define CTEST4_700_SFWR 0x08 /* SCSI FIFO write enable,
+ * redirects writes from SODL
+ * to the SCSI FIFO.
+ */
+#define CTEST4_800_MPEE 0x08 /* Enable parity checking
+ during master cycles on PCI
+ bus */
+
+/*
+ * These bits send the contents of the CTEST6 register to the appropriate
+ * byte lane of the 32 bit DMA FIFO. Normal operation is zero, otherwise
+ * the high bit means the low two bits select the byte lane.
+ */
+#define CTEST4_FBL2 0x04
+#define CTEST4_FBL1 0x02
+#define CTEST4_FBL0 0x01
+#define CTEST4_FBL_MASK 0x07
+#define CTEST4_FBL_0 0x04 /* Select DMA FIFO byte lane 0 */
+#define CTEST4_FBL_1 0x05 /* Select DMA FIFO byte lane 1 */
+#define CTEST4_FBL_2 0x06 /* Select DMA FIFO byte lane 2 */
+#define CTEST4_FBL_3 0x07 /* Select DMA FIFO byte lane 3 */
+#define CTEST4_800_SAVE (CTEST4_800_BDIS)
+
+
+#define CTEST5_REG_700 0x19 /* Chip test 5 rw */
+#define CTEST5_REG_800 0x22 /* Chip test 5 rw */
+/*
+ * Clock Address Incrementor. When set, it increments the
+ * DNAD register to the next bus size boundary. It automatically
+ * resets itself when the operation is complete.
+ */
+#define CTEST5_ADCK 0x80
+/*
+ * Clock Byte Counter. When set, it decrements the DBC register to
+ * the next bus size boundary.
+ */
+#define CTEST5_BBCK 0x40
+/*
+ * Reset SCSI Offset. Setting this bit to 1 clears the current offset
+ * pointer in the SCSI synchronous offset counter (SSTAT). This bit
+ * is set to 1 if a SCSI Gross Error Condition occurs. The offset should
+ * be cleared when a synchronous transfer fails. When written, it is
+ * automatically cleared after the SCSI synchronous offset counter is
+ * reset.
+ */
+/* Bit 5 is reserved on 800 series chips */
+#define CTEST5_700_ROFF 0x20
+/*
+ * Master Control for Set or Reset pulses. When 1, causes the low
+ * four bits of register to set when set, 0 causes the low bits to
+ * clear when set.
+ */
+#define CTEST5_MASR 0x10
+#define CTEST5_DDIR 0x08 /* DMA direction */
+/*
+ * Bits 2-0 are reserved on 800 series chips
+ */
+#define CTEST5_700_EOP 0x04 /* End of process */
+#define CTEST5_700_DREQ 0x02 /* Data request */
+#define CTEST5_700_DACK 0x01 /* Data acknowledge */
+
+/*
+ * Chip test 6 rw - writing to this register writes to the byte
+ * lane in the DMA FIFO as determined by the FBL bits in the CTEST4
+ * register.
+ */
+#define CTEST6_REG_700 0x1a
+#define CTEST6_REG_800 0x23
+
+#define CTEST7_REG 0x1b /* Chip test 7 rw */
+/* 0x80 - 0x40 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define CTEST7_10_CDIS 0x80 /* Cache burst disable */
+#define CTEST7_10_SC1 0x40 /* Snoop control bits */
+#define CTEST7_10_SC0 0x20
+#define CTEST7_10_SC_MASK 0x60
+/* 0x20 is reserved on the NCR53c700 */
+#define CTEST7_0060_FM 0x20 /* Fetch mode */
+#define CTEST7_STD 0x10 /* Selection timeout disable */
+#define CTEST7_DFP 0x08 /* DMA FIFO parity bit for CTEST6 */
+#define CTEST7_EVP 0x04 /* 1 = host bus even parity, 0 = odd */
+#define CTEST7_10_TT1 0x02 /* Transfer type */
+#define CTEST7_00_DC 0x02 /* Set to drive DC low during instruction
+ fetch */
+#define CTEST7_DIFF 0x01 /* Differential mode */
+
+#define CTEST7_SAVE ( CTEST7_EVP | CTEST7_DIFF )
+
+
+#define TEMP_REG 0x1c /* through 0x1f Temporary stack rw */
+
+#define DFIFO_REG 0x20 /* DMA FIFO rw */
+/*
+ * 0x80 is reserved on the NCR53c710, the CLF and FLF bits have been
+ * moved into the CTEST8 register.
+ */
+#define DFIFO_00_FLF 0x80 /* Flush DMA FIFO to memory */
+#define DFIFO_00_CLF 0x40 /* Clear DMA and SCSI FIFOs */
+#define DFIFO_BO6 0x40
+#define DFIFO_BO5 0x20
+#define DFIFO_BO4 0x10
+#define DFIFO_BO3 0x08
+#define DFIFO_BO2 0x04
+#define DFIFO_BO1 0x02
+#define DFIFO_BO0 0x01
+#define DFIFO_10_BO_MASK 0x7f /* 7 bit counter */
+#define DFIFO_00_BO_MASK 0x3f /* 6 bit counter */
+
+/*
+ * Interrupt status rw
+ * Note that this is the only register which can be read while SCSI
+ * SCRIPTS are being executed.
+ */
+#define ISTAT_REG_700 0x21
+#define ISTAT_REG_800 0x14
+#define ISTAT_ABRT 0x80 /* Software abort, write
+ *1 to abort, wait for interrupt. */
+/* 0x40 and 0x20 are reserved on NCR53c700 and NCR53c700-66 chips */
+#define ISTAT_10_SRST 0x40 /* software reset */
+#define ISTAT_10_SIGP 0x20 /* signal script */
+/* 0x10 is reserved on NCR53c700 series chips */
+#define ISTAT_800_SEM 0x10 /* semaphore */
+#define ISTAT_CON 0x08 /* 1 when connected */
+#define ISTAT_800_INTF 0x04 /* Interrupt on the fly */
+#define ISTAT_700_PRE 0x04 /* Pointer register empty.
+ * Set to 1 when DSPS and DSP
+ * registers are empty in pipeline
+ * mode, always set otherwise.
+ */
+#define ISTAT_SIP 0x02 /* SCSI interrupt pending from
+ * SCSI portion of SIOP see
+ * SSTAT0
+ */
+#define ISTAT_DIP 0x01 /* DMA interrupt pending
+ * see DSTAT
+ */
+
+/* NCR53c700-66 and NCR53c710 only */
+#define CTEST8_REG 0x22 /* Chip test 8 rw */
+#define CTEST8_0066_EAS 0x80 /* Enable alternate SCSI clock,
+ * ie read from SCLK/ rather than CLK/
+ */
+#define CTEST8_0066_EFM 0x40 /* Enable fetch and master outputs */
+#define CTEST8_0066_GRP 0x20 /* Generate Receive Parity for
+ * pass through. This insures that
+ * bad parity won't reach the host
+ * bus.
+ */
+#define CTEST8_0066_TE 0x10 /* TolerANT enable. Enable
+ * active negation, should only
+ * be used for slow SCSI
+ * non-differential.
+ */
+#define CTEST8_0066_HSC 0x08 /* Halt SCSI clock */
+#define CTEST8_0066_SRA 0x04 /* Shorten REQ/ACK filtering,
+ * must be set for fast SCSI-II
+ * speeds.
+ */
+#define CTEST8_0066_DAS 0x02 /* Disable automatic target/initiator
+ * switching.
+ */
+#define CTEST8_0066_LDE 0x01 /* Last disconnect enable.
+ * The status of pending
+ * disconnect is maintained by
+ * the core, eliminating
+ * the possibility of missing a
+ * selection or reselection
+ * while waiting to fetch a
+ * WAIT DISCONNECT opcode.
+ */
+
+#define CTEST8_10_V3 0x80 /* Chip revision */
+#define CTEST8_10_V2 0x40
+#define CTEST8_10_V1 0x20
+#define CTEST8_10_V0 0x10
+#define CTEST8_10_V_MASK 0xf0
+#define CTEST8_10_FLF 0x08 /* Flush FIFOs */
+#define CTEST8_10_CLF 0x04 /* Clear FIFOs */
+#define CTEST8_10_FM 0x02 /* Fetch pin mode */
+#define CTEST8_10_SM 0x01 /* Snoop pin mode */
+
+
+/*
+ * The CTEST9 register may be used to differentiate between a
+ * NCR53c700 and a NCR53c710.
+ *
+ * Write 0xff to this register.
+ * Read it.
+ * If the contents are 0xff, it is a NCR53c700
+ * If the contents are 0x00, it is a NCR53c700-66 first revision
+ * If the contents are some other value, it is some other NCR53c700-66
+ */
+#define CTEST9_REG_00 0x23 /* Chip test 9 ro */
+#define LCRC_REG_10 0x23
+
+/*
+ * 0x24 through 0x27 are the DMA byte counter register. Instructions
+ * write their high 8 bits into the DCMD register, the low 24 bits into
+ * the DBC register.
+ *
+ * Function is dependent on the command type being executed.
+ */
+
+
+#define DBC_REG 0x24
+/*
+ * For Block Move Instructions, DBC is a 24 bit quantity representing
+ * the number of bytes to transfer.
+ * For Transfer Control Instructions, DBC is bit fielded as follows :
+ */
+/* Bits 20 - 23 should be clear */
+#define DBC_TCI_TRUE (1 << 19) /* Jump when true */
+#define DBC_TCI_COMPARE_DATA (1 << 18) /* Compare data */
+#define DBC_TCI_COMPARE_PHASE (1 << 17) /* Compare phase with DCMD field */
+#define DBC_TCI_WAIT_FOR_VALID (1 << 16) /* Wait for REQ */
+/* Bits 8 - 15 are reserved on some implementations ? */
+#define DBC_TCI_MASK_MASK 0xff00 /* Mask for data compare */
+#define DBC_TCI_MASK_SHIFT 8
+#define DBC_TCI_DATA_MASK 0xff /* Data to be compared */
+#define DBC_TCI_DATA_SHIFT 0
+
+#define DBC_RWRI_IMMEDIATE_MASK 0xff00 /* Immediate data */
+#define DBC_RWRI_IMMEDIATE_SHIFT 8 /* Amount to shift */
+#define DBC_RWRI_ADDRESS_MASK 0x3f0000 /* Register address */
+#define DBC_RWRI_ADDRESS_SHIFT 16
+
+
+/*
+ * DMA command r/w
+ */
+#define DCMD_REG 0x27
+#define DCMD_TYPE_MASK 0xc0 /* Masks off type */
+#define DCMD_TYPE_BMI 0x00 /* Indicates a Block Move instruction */
+#define DCMD_BMI_IO 0x01 /* I/O, CD, and MSG bits selecting */
+#define DCMD_BMI_CD 0x02 /* the phase for the block MOVE */
+#define DCMD_BMI_MSG 0x04 /* instruction */
+
+#define DCMD_BMI_OP_MASK 0x18 /* mask for opcode */
+#define DCMD_BMI_OP_MOVE_T 0x00 /* MOVE */
+#define DCMD_BMI_OP_MOVE_I 0x08 /* MOVE Initiator */
+
+#define DCMD_BMI_INDIRECT 0x20 /* Indirect addressing */
+
+#define DCMD_TYPE_TCI 0x80 /* Indicates a Transfer Control
+ instruction */
+#define DCMD_TCI_IO 0x01 /* I/O, CD, and MSG bits selecting */
+#define DCMD_TCI_CD 0x02 /* the phase for the block MOVE */
+#define DCMD_TCI_MSG 0x04 /* instruction */
+#define DCMD_TCI_OP_MASK 0x38 /* mask for opcode */
+#define DCMD_TCI_OP_JUMP 0x00 /* JUMP */
+#define DCMD_TCI_OP_CALL 0x08 /* CALL */
+#define DCMD_TCI_OP_RETURN 0x10 /* RETURN */
+#define DCMD_TCI_OP_INT 0x18 /* INT */
+
+#define DCMD_TYPE_RWRI 0x40 /* Indicates I/O or register Read/Write
+ instruction */
+#define DCMD_RWRI_OPC_MASK 0x38 /* Opcode mask */
+#define DCMD_RWRI_OPC_WRITE 0x28 /* Write SFBR to register */
+#define DCMD_RWRI_OPC_READ 0x30 /* Read register to SFBR */
+#define DCMD_RWRI_OPC_MODIFY 0x38 /* Modify in place */
+
+#define DCMD_RWRI_OP_MASK 0x07
+#define DCMD_RWRI_OP_MOVE 0x00
+#define DCMD_RWRI_OP_SHL 0x01
+#define DCMD_RWRI_OP_OR 0x02
+#define DCMD_RWRI_OP_XOR 0x03
+#define DCMD_RWRI_OP_AND 0x04
+#define DCMD_RWRI_OP_SHR 0x05
+#define DCMD_RWRI_OP_ADD 0x06
+#define DCMD_RWRI_OP_ADDC 0x07
+
+#define DCMD_TYPE_MMI 0xc0 /* Indicates a Memory Move instruction
+ (three words) */
+
+
+#define DNAD_REG 0x28 /* through 0x2b DMA next address for
+ data */
+#define DSP_REG 0x2c /* through 0x2f DMA SCRIPTS pointer rw */
+#define DSPS_REG 0x30 /* through 0x33 DMA SCRIPTS pointer
+ save rw */
+#define DMODE_REG_00 0x34 /* DMA mode rw */
+#define DMODE_00_BL1 0x80 /* Burst length bits */
+#define DMODE_00_BL0 0x40
+#define DMODE_BL_MASK 0xc0
+/* Burst lengths (800) */
+#define DMODE_BL_2 0x00 /* 2 transfer */
+#define DMODE_BL_4 0x40 /* 4 transfers */
+#define DMODE_BL_8 0x80 /* 8 transfers */
+#define DMODE_BL_16 0xc0 /* 16 transfers */
+
+#define DMODE_700_BW16 0x20 /* Host buswidth = 16 */
+#define DMODE_700_286 0x10 /* 286 mode */
+#define DMODE_700_IOM 0x08 /* Transfer to IO port */
+#define DMODE_700_FAM 0x04 /* Fixed address mode */
+#define DMODE_700_PIPE 0x02 /* Pipeline mode disables
+ * automatic fetch / exec
+ */
+#define DMODE_MAN 0x01 /* Manual start mode,
+ * requires a 1 to be written
+ * to the start DMA bit in the DCNTL
+ * register to run scripts
+ */
+
+#define DMODE_700_SAVE ( DMODE_00_BL_MASK | DMODE_00_BW16 | DMODE_00_286 )
+
+/* NCR53c800 series only */
+#define SCRATCHA_REG_800 0x34 /* through 0x37 Scratch A rw */
+/* NCR53c710 only */
+#define SCRATCB_REG_10 0x34 /* through 0x37 scratch B rw */
+
+#define DMODE_REG_10 0x38 /* DMA mode rw, NCR53c710 and newer */
+#define DMODE_800_SIOM 0x20 /* Source IO = 1 */
+#define DMODE_800_DIOM 0x10 /* Destination IO = 1 */
+#define DMODE_800_ERL 0x08 /* Enable Read Line */
+
+/* 35-38 are reserved on 700 and 700-66 series chips */
+#define DIEN_REG 0x39 /* DMA interrupt enable rw */
+/* 0x80, 0x40, and 0x20 are reserved on 700-series chips */
+#define DIEN_800_MDPE 0x40 /* Master data parity error */
+#define DIEN_800_BF 0x20 /* BUS fault */
+#define DIEN_ABRT 0x10 /* Enable aborted interrupt */
+#define DIEN_SSI 0x08 /* Enable single step interrupt */
+#define DIEN_SIR 0x04 /* Enable SCRIPTS INT command
+ * interrupt
+ */
+/* 0x02 is reserved on 800 series chips */
+#define DIEN_700_WTD 0x02 /* Enable watchdog timeout interrupt */
+#define DIEN_700_OPC 0x01 /* Enable illegal instruction
+ * interrupt
+ */
+#define DIEN_800_IID 0x01 /* Same meaning, different name */
+
+/*
+ * DMA watchdog timer rw
+ * set in 16 CLK input periods.
+ */
+#define DWT_REG 0x3a
+
+/* DMA control rw */
+#define DCNTL_REG 0x3b
+#define DCNTL_700_CF1 0x80 /* Clock divisor bits */
+#define DCNTL_700_CF0 0x40
+#define DCNTL_700_CF_MASK 0xc0
+/* Clock divisors Divisor SCLK range (MHZ) */
+#define DCNTL_700_CF_2 0x00 /* 2.0 37.51-50.00 */
+#define DCNTL_700_CF_1_5 0x40 /* 1.5 25.01-37.50 */
+#define DCNTL_700_CF_1 0x80 /* 1.0 16.67-25.00 */
+#define DCNTL_700_CF_3 0xc0 /* 3.0 50.01-66.67 (53c700-66) */
+
+#define DCNTL_700_S16 0x20 /* Load scripts 16 bits at a time */
+#define DCNTL_SSM 0x10 /* Single step mode */
+#define DCNTL_700_LLM 0x08 /* Low level mode, can only be set
+ * after selection */
+#define DCNTL_800_IRQM 0x08 /* Totem pole IRQ pin */
+#define DCNTL_STD 0x04 /* Start DMA / SCRIPTS */
+/* 0x02 is reserved */
+#define DCNTL_00_RST 0x01 /* Software reset, resets everything
+ * but 286 mode bit in DMODE. On the
+ * NCR53c710, this bit moved to CTEST8
+ */
+#define DCNTL_10_COM 0x01 /* 700 software compatibility mode */
+
+#define DCNTL_700_SAVE ( DCNTL_CF_MASK | DCNTL_S16)
+
+
+/* NCR53c700-66 only */
+#define SCRATCHB_REG_00 0x3c /* through 0x3f scratch b rw */
+#define SCRATCHB_REG_800 0x5c /* through 0x5f scratch b rw */
+/* NCR53c710 only */
+#define ADDER_REG_10 0x3c /* Adder, NCR53c710 only */
+
+#define SIEN1_REG_800 0x41
+#define SIEN1_800_STO 0x04 /* selection/reselection timeout */
+#define SIEN1_800_GEN 0x02 /* general purpose timer */
+#define SIEN1_800_HTH 0x01 /* handshake to handshake */
+
+#define SIST1_REG_800 0x43
+#define SIST1_800_STO 0x04 /* selection/reselection timeout */
+#define SIST1_800_GEN 0x02 /* general purpose timer */
+#define SIST1_800_HTH 0x01 /* handshake to handshake */
+
+#define SLPAR_REG_800 0x44 /* Parity */
+
+#define MACNTL_REG_800 0x46 /* Memory access control */
+#define MACNTL_800_TYP3 0x80
+#define MACNTL_800_TYP2 0x40
+#define MACNTL_800_TYP1 0x20
+#define MACNTL_800_TYP0 0x10
+#define MACNTL_800_DWR 0x08
+#define MACNTL_800_DRD 0x04
+#define MACNTL_800_PSCPT 0x02
+#define MACNTL_800_SCPTS 0x01
+
+#define GPCNTL_REG_800 0x47 /* General Purpose Pin Control */
+
+/* Timeouts are expressed such that 0=off, 1=100us, doubling after that */
+#define STIME0_REG_800 0x48 /* SCSI Timer Register 0 */
+#define STIME0_800_HTH_MASK 0xf0 /* Handshake to Handshake timeout */
+#define STIME0_800_HTH_SHIFT 4
+#define STIME0_800_SEL_MASK 0x0f /* Selection timeout */
+#define STIME0_800_SEL_SHIFT 0
+
+#define STIME1_REG_800 0x49
+#define STIME1_800_GEN_MASK 0x0f /* General purpose timer */
+
+#define RESPID_REG_800 0x4a /* Response ID, bit fielded. 8
+ bits on narrow chips, 16 on WIDE */
+
+#define STEST0_REG_800 0x4c
+#define STEST0_800_SLT 0x08 /* Selection response logic test */
+#define STEST0_800_ART 0x04 /* Arbitration priority encoder test */
+#define STEST0_800_SOZ 0x02 /* Synchronous offset zero */
+#define STEST0_800_SOM 0x01 /* Synchronous offset maximum */
+
+#define STEST1_REG_800 0x4d
+#define STEST1_800_SCLK 0x80 /* Disable SCSI clock */
+
+#define STEST2_REG_800 0x4e
+#define STEST2_800_SCE 0x80 /* Enable SOCL/SODL */
+#define STEST2_800_ROF 0x40 /* Reset SCSI sync offset */
+#define STEST2_800_SLB 0x10 /* Enable SCSI loopback mode */
+#define STEST2_800_SZM 0x08 /* SCSI high impedance mode */
+#define STEST2_800_EXT 0x02 /* Extend REQ/ACK filter 30 to 60ns */
+#define STEST2_800_LOW 0x01 /* SCSI low level mode */
+
+#define STEST3_REG_800 0x4f
+#define STEST3_800_TE 0x80 /* Enable active negation */
+#define STEST3_800_STR 0x40 /* SCSI FIFO test read */
+#define STEST3_800_HSC 0x20 /* Halt SCSI clock */
+#define STEST3_800_DSI 0x10 /* Disable single initiator response */
+#define STEST3_800_TTM 0x04 /* Time test mode */
+#define STEST3_800_CSF 0x02 /* Clear SCSI FIFO */
+#define STEST3_800_STW 0x01 /* SCSI FIFO test write */
+
+#define OPTION_PARITY 0x1 /* Enable parity checking */
+#define OPTION_TAGGED_QUEUE 0x2 /* Enable SCSI-II tagged queuing */
+#define OPTION_700 0x8 /* Always run NCR53c700 scripts */
+#define OPTION_INTFLY 0x10 /* Use INTFLY interrupts */
+#define OPTION_DEBUG_INTR 0x20 /* Debug interrupts */
+#define OPTION_DEBUG_INIT_ONLY 0x40 /* Run initialization code and
+ simple test code, return
+ DID_NO_CONNECT if any SCSI
+ commands are attempted. */
+#define OPTION_DEBUG_READ_ONLY 0x80 /* Return DID_ERROR if any
+ SCSI write is attempted */
+#define OPTION_DEBUG_TRACE 0x100 /* Animated trace mode, print
+ each address and instruction
+ executed to debug buffer. */
+#define OPTION_DEBUG_SINGLE 0x200 /* stop after executing one
+ instruction */
+#define OPTION_SYNCHRONOUS 0x400 /* Enable sync SCSI. */
+#define OPTION_MEMORY_MAPPED 0x800 /* NCR registers have valid
+ memory mapping */
+#define OPTION_IO_MAPPED 0x1000 /* NCR registers have valid
+ I/O mapping */
+#define OPTION_DEBUG_PROBE_ONLY 0x2000 /* Probe only, don't even init */
+#define OPTION_DEBUG_TESTS_ONLY 0x4000 /* Probe, init, run selected tests */
+#define OPTION_DEBUG_TEST0 0x08000 /* Run test 0 */
+#define OPTION_DEBUG_TEST1 0x10000 /* Run test 1 */
+#define OPTION_DEBUG_TEST2 0x20000 /* Run test 2 */
+#define OPTION_DEBUG_DUMP 0x40000 /* Dump commands */
+#define OPTION_DEBUG_TARGET_LIMIT 0x80000 /* Only talk to target+luns specified */
+#define OPTION_DEBUG_NCOMMANDS_LIMIT 0x100000 /* Limit the number of commands */
+#define OPTION_DEBUG_SCRIPT 0x200000 /* Print when checkpoints are passed */
+#define OPTION_DEBUG_FIXUP 0x400000 /* print fixup values */
+#define OPTION_DEBUG_DSA 0x800000
+#define OPTION_DEBUG_CORRUPTION 0x1000000 /* Detect script corruption */
+#define OPTION_DEBUG_SDTR 0x2000000 /* Debug SDTR problem */
+#define OPTION_DEBUG_MISMATCH 0x4000000 /* Debug phase mismatches */
+#define OPTION_DISCONNECT 0x8000000 /* Allow disconect */
+#define OPTION_DEBUG_DISCONNECT 0x10000000
+#define OPTION_ALWAYS_SYNCHRONOUS 0x20000000 /* Negotiate sync. transfers
+ on power up */
+#define OPTION_DEBUG_QUEUES 0x80000000
+#define OPTION_DEBUG_ALLOCATION 0x100000000LL
+#define OPTION_DEBUG_SYNCHRONOUS 0x200000000LL /* Sanity check SXFER and
+ SCNTL3 registers */
+#define OPTION_NO_ASYNC 0x400000000LL /* Don't automagically send
+ SDTR for async transfers when
+ we haven't been told to do
+ a synchronous transfer. */
+#define OPTION_NO_PRINT_RACE 0x800000000LL /* Don't print message when
+ the reselect/WAIT DISCONNECT
+ race condition hits */
+#if !defined(PERM_OPTIONS)
+#define PERM_OPTIONS 0
+#endif
+
+struct NCR53c7x0_synchronous {
+ u32 select_indirect; /* Value used for indirect selection */
+ u32 script[8]; /* Size ?? Script used when target is
+ reselected */
+ unsigned char synchronous_want[5]; /* Per target desired SDTR */
+/*
+ * Set_synchronous programs these, select_indirect and current settings after
+ * int_debug_should show a match.
+ */
+ unsigned char sxfer_sanity, scntl3_sanity;
+};
+
+#define CMD_FLAG_SDTR 1 /* Initiating synchronous
+ transfer negotiation */
+#define CMD_FLAG_WDTR 2 /* Initiating wide transfer
+ negotiation */
+#define CMD_FLAG_DID_SDTR 4 /* did SDTR */
+#define CMD_FLAG_DID_WDTR 8 /* did WDTR */
+
+struct NCR53c7x0_table_indirect {
+ u32 count;
+ void *address;
+};
+
+enum ncr_event {
+ EVENT_NONE = 0,
+/*
+ * Order is IMPORTANT, since these must correspond to the event interrupts
+ * in 53c7,8xx.scr
+ */
+
+ EVENT_ISSUE_QUEUE = 0x5000000, /* Command was added to issue queue */
+ EVENT_START_QUEUE, /* Command moved to start queue */
+ EVENT_SELECT, /* Command completed selection */
+ EVENT_DISCONNECT, /* Command disconnected */
+ EVENT_RESELECT, /* Command reselected */
+ EVENT_COMPLETE, /* Command completed */
+ EVENT_IDLE,
+ EVENT_SELECT_FAILED,
+ EVENT_BEFORE_SELECT,
+ EVENT_RESELECT_FAILED
+};
+
+struct NCR53c7x0_event {
+ enum ncr_event event; /* What type of event */
+ unsigned char target;
+ unsigned char lun;
+ struct timeval time;
+ u32 *dsa; /* What's in the DSA register now (virt) */
+/*
+ * A few things from that SCSI pid so we know what happened after
+ * the Scsi_Cmnd structure in question may have disappeared.
+ */
+ unsigned long pid; /* The SCSI PID which caused this
+ event */
+ unsigned char cmnd[12];
+};
+
+/*
+ * Things in the NCR53c7x0_cmd structure are split into two parts :
+ *
+ * 1. A fixed portion, for things which are not accessed directly by static NCR
+ * code (ie, are referenced only by the Linux side of the driver,
+ * or only by dynamically genreated code).
+ *
+ * 2. The DSA portion, for things which are accessed directly by static NCR
+ * code.
+ *
+ * This is a little ugly, but it
+ * 1. Avoids conflicts between the NCR code's picture of the structure, and
+ * Linux code's idea of what it looks like.
+ *
+ * 2. Minimizes the pain in the Linux side of the code needed
+ * to calculate real dsa locations for things, etc.
+ *
+ */
+
+struct NCR53c7x0_cmd {
+ void *real; /* Real, unaligned address for
+ free function */
+ void (* free)(void *, int); /* Command to deallocate; NULL
+ for structures allocated with
+ scsi_register, etc. */
+ Scsi_Cmnd *cmd; /* Associated Scsi_Cmnd
+ structure, Scsi_Cmnd points
+ at NCR53c7x0_cmd using
+ host_scribble structure */
+
+ int size; /* scsi_malloc'd size of this
+ structure */
+
+ int flags; /* CMD_* flags */
+
+/*
+ * SDTR and WIDE messages are an either/or affair
+ * in this message, since we will go into message out and send
+ * _the whole mess_ without dropping out of message out to
+ * let the target go into message in after sending the first
+ * message.
+ */
+
+ unsigned char select[11]; /* Select message, includes
+ IDENTIFY
+ (optional) QUEUE TAG
+ (optional) SDTR or WDTR
+ */
+
+
+ volatile struct NCR53c7x0_cmd *next; /* Linux maintained lists (free,
+ running, eventually finished */
+
+
+ u32 *data_transfer_start; /* Start of data transfer routines */
+ u32 *data_transfer_end; /* Address after end of data transfer o
+ routines */
+/*
+ * The following three fields were moved from the DSA propper to here
+ * since only dynamically generated NCR code refers to them, meaning
+ * we don't need dsa_* absolutes, and it is simpler to let the
+ * host code refer to them directly.
+ */
+
+/*
+ * HARD CODED : residual and saved_residual need to agree with the sizes
+ * used in NCR53c7,8xx.scr.
+ *
+ * FIXME: we want to consider the case where we have odd-length
+ * scatter/gather buffers and a WIDE transfer, in which case
+ * we'll need to use the CHAIN MOVE instruction. Ick.
+ */
+ u32 residual[6]; /* Residual data transfer which
+ allows pointer code to work
+ right.
+
+ [0-1] : Conditional call to
+ appropriate other transfer
+ routine.
+ [2-3] : Residual block transfer
+ instruction.
+ [4-5] : Jump to instruction
+ after splice.
+ */
+ u32 saved_residual[6]; /* Copy of old residual, so we
+ can get another partial
+ transfer and still recover
+ */
+
+ u32 saved_data_pointer; /* Saved data pointer */
+
+ u32 dsa_next_addr; /* _Address_ of dsa_next field
+ in this dsa for RISCy
+ style constant. */
+
+ u32 dsa_addr; /* Address of dsa; RISCy style
+ constant */
+
+ u32 dsa[0]; /* Variable length (depending
+ on host type, number of scatter /
+ gather buffers, etc). */
+};
+
+struct NCR53c7x0_break {
+ u32 *address, old_instruction[2];
+ struct NCR53c7x0_break *next;
+ unsigned char old_size; /* Size of old instruction */
+};
+
+/* Indicates that the NCR is not executing code */
+#define STATE_HALTED 0
+/*
+ * Indicates that the NCR is executing the wait for select / reselect
+ * script. Only used when running NCR53c700 compatible scripts, only
+ * state during which an ABORT is _not_ considered an error condition.
+ */
+#define STATE_WAITING 1
+/* Indicates that the NCR is executing other code. */
+#define STATE_RUNNING 2
+/*
+ * Indicates that the NCR was being aborted.
+ */
+#define STATE_ABORTING 3
+/* Indicates that the NCR was successfully aborted. */
+#define STATE_ABORTED 4
+/* Indicates that the NCR has been disabled due to a fatal error */
+#define STATE_DISABLED 5
+
+/*
+ * Where knowledge of SCSI SCRIPT(tm) specified values are needed
+ * in an interrupt handler, an interrupt handler exists for each
+ * different SCSI script so we don't have name space problems.
+ *
+ * Return values of these handlers are as follows :
+ */
+#define SPECIFIC_INT_NOTHING 0 /* don't even restart */
+#define SPECIFIC_INT_RESTART 1 /* restart at the next instruction */
+#define SPECIFIC_INT_ABORT 2 /* recoverable error, abort cmd */
+#define SPECIFIC_INT_PANIC 3 /* unrecoverable error, panic */
+#define SPECIFIC_INT_DONE 4 /* normal command completion */
+#define SPECIFIC_INT_BREAK 5 /* break point encountered */
+
+struct NCR53c7x0_hostdata {
+ int size; /* Size of entire Scsi_Host
+ structure */
+ int board; /* set to board type, useful if
+ we have host specific things,
+ ie, a general purpose I/O
+ bit is being used to enable
+ termination, etc. */
+
+ int chip; /* set to chip type; 700-66 is
+ 700-66, rest are last three
+ digits of part number */
+ /*
+ * PCI bus, device, function, only for NCR53c8x0 chips.
+ * pci_valid indicates that the PCI configuration information
+ * is valid, and we can twiddle MAX_LAT, etc. as recommended
+ * for maximum performance in the NCR documentation.
+ */
+ unsigned char pci_bus, pci_device_fn;
+ unsigned pci_valid:1;
+
+ u32 *dsp; /* dsp to restart with after
+ all stacked interrupts are
+ handled. */
+
+ unsigned dsp_changed:1; /* Has dsp changed within this
+ set of stacked interrupts ? */
+
+ unsigned char dstat; /* Most recent value of dstat */
+ unsigned dstat_valid:1;
+
+ unsigned expecting_iid:1; /* Expect IID interrupt */
+ unsigned expecting_sto:1; /* Expect STO interrupt */
+
+ /*
+ * The code stays cleaner if we use variables with function
+ * pointers and offsets that are unique for the different
+ * scripts rather than having a slew of switch(hostdata->chip)
+ * statements.
+ *
+ * It also means that the #defines from the SCSI SCRIPTS(tm)
+ * don't have to be visible outside of the script-specific
+ * instructions, preventing name space pollution.
+ */
+
+ void (* init_fixup)(struct Scsi_Host *host);
+ void (* init_save_regs)(struct Scsi_Host *host);
+ void (* dsa_fixup)(struct NCR53c7x0_cmd *cmd);
+ void (* soft_reset)(struct Scsi_Host *host);
+ int (* run_tests)(struct Scsi_Host *host);
+
+ /*
+ * Called when DSTAT_SIR is set, indicating an interrupt generated
+ * by the INT instruction, where values are unique for each SCSI
+ * script. Should return one of the SPEC_* values.
+ */
+
+ int (* dstat_sir_intr)(struct Scsi_Host *host, struct NCR53c7x0_cmd *cmd);
+
+ int dsa_len; /* Size of DSA structure */
+
+ /*
+ * Location of DSA fields for the SCSI SCRIPT corresponding to this
+ * chip.
+ */
+
+ s32 dsa_start;
+ s32 dsa_end;
+ s32 dsa_next;
+ s32 dsa_prev;
+ s32 dsa_cmnd;
+ s32 dsa_select;
+ s32 dsa_msgout;
+ s32 dsa_cmdout;
+ s32 dsa_dataout;
+ s32 dsa_datain;
+ s32 dsa_msgin;
+ s32 dsa_msgout_other;
+ s32 dsa_write_sync;
+ s32 dsa_write_resume;
+ s32 dsa_check_reselect;
+ s32 dsa_status;
+ s32 dsa_saved_pointer;
+ s32 dsa_jump_dest;
+
+ /*
+ * Important entry points that generic fixup code needs
+ * to know about, fixed up.
+ */
+
+ s32 E_accept_message;
+ s32 E_command_complete;
+ s32 E_data_transfer;
+ s32 E_dsa_code_template;
+ s32 E_dsa_code_template_end;
+ s32 E_end_data_transfer;
+ s32 E_msg_in;
+ s32 E_initiator_abort;
+ s32 E_other_transfer;
+ s32 E_other_in;
+ s32 E_other_out;
+ s32 E_target_abort;
+ s32 E_debug_break;
+ s32 E_reject_message;
+ s32 E_respond_message;
+ s32 E_select;
+ s32 E_select_msgout;
+ s32 E_test_0;
+ s32 E_test_1;
+ s32 E_test_2;
+ s32 E_test_3;
+ s32 E_dsa_zero;
+ s32 E_cmdout_cmdout;
+ s32 E_wait_reselect;
+ s32 E_dsa_code_begin;
+
+ long long options; /* Bitfielded set of options enabled */
+ volatile u32 test_completed; /* Test completed */
+ int test_running; /* Test currently running */
+ s32 test_source;
+ volatile s32 test_dest;
+
+ volatile int state; /* state of driver, only used for
+ OPTION_700 */
+
+ unsigned char dmode; /*
+ * set to the address of the DMODE
+ * register for this chip.
+ */
+ unsigned char istat; /*
+ * set to the address of the ISTAT
+ * register for this chip.
+ */
+
+ int scsi_clock; /*
+ * SCSI clock in HZ. 0 may be used
+ * for unknown, although this will
+ * disable synchronous negotiation.
+ */
+
+ volatile int intrs; /* Number of interrupts */
+ volatile int resets; /* Number of SCSI resets */
+ unsigned char saved_dmode;
+ unsigned char saved_ctest4;
+ unsigned char saved_ctest7;
+ unsigned char saved_dcntl;
+ unsigned char saved_scntl3;
+
+ unsigned char this_id_mask;
+
+ /* Debugger information */
+ struct NCR53c7x0_break *breakpoints, /* Linked list of all break points */
+ *breakpoint_current; /* Current breakpoint being stepped
+ through, NULL if we are running
+ normally. */
+#ifdef NCR_DEBUG
+ int debug_size; /* Size of debug buffer */
+ volatile int debug_count; /* Current data count */
+ volatile char *debug_buf; /* Output ring buffer */
+ volatile char *debug_write; /* Current write pointer */
+ volatile char *debug_read; /* Current read pointer */
+#endif /* def NCR_DEBUG */
+
+ /* XXX - primitive debugging junk, remove when working ? */
+ int debug_print_limit; /* Number of commands to print
+ out exhaustive debugging
+ information for if
+ OPTION_DEBUG_DUMP is set */
+
+ unsigned char debug_lun_limit[16]; /* If OPTION_DEBUG_TARGET_LIMIT
+ set, puke if commands are sent
+ to other target/lun combinations */
+
+ int debug_count_limit; /* Number of commands to execute
+ before puking to limit debugging
+ output */
+
+
+ volatile unsigned idle:1; /* set to 1 if idle */
+
+ /*
+ * Table of synchronous+wide transfer parameters set on a per-target
+ * basis.
+ */
+
+ volatile struct NCR53c7x0_synchronous sync[16];
+
+ volatile Scsi_Cmnd *issue_queue;
+ /* waiting to be issued by
+ Linux driver */
+ volatile struct NCR53c7x0_cmd *running_list;
+ /* commands running, maintained
+ by Linux driver */
+
+ volatile struct NCR53c7x0_cmd *current; /* currently connected
+ nexus, ONLY valid for
+ NCR53c700/NCR53c700-66
+ */
+
+ volatile struct NCR53c7x0_cmd *spare; /* pointer to spare,
+ allocated at probe time,
+ which we can use for
+ initialization */
+ volatile struct NCR53c7x0_cmd *free;
+ int max_cmd_size; /* Maximum size of NCR53c7x0_cmd
+ based on number of
+ scatter/gather segments, etc.
+ */
+ volatile int num_cmds; /* Number of commands
+ allocated */
+ volatile int extra_allocate;
+ volatile unsigned char cmd_allocated[16]; /* Have we allocated commands
+ for this target yet? If not,
+ do so ASAP */
+ volatile unsigned char busy[16][8]; /* number of commands
+ executing on each target
+ */
+ /*
+ * Eventually, I'll switch to a coroutine for calling
+ * cmd->done(cmd), etc. so that we can overlap interrupt
+ * processing with this code for maximum performance.
+ */
+
+ volatile struct NCR53c7x0_cmd *finished_queue;
+
+
+ /* Shared variables between SCRIPT and host driver */
+ volatile u32 *schedule; /* Array of JUMPs to dsa_begin
+ routines of various DSAs.
+ When not in use, replace
+ with jump to next slot */
+
+
+ volatile unsigned char msg_buf[16]; /* buffer for messages
+ other than the command
+ complete message */
+
+ /* Per-target default synchronous and WIDE messages */
+ volatile unsigned char synchronous_want[16][5];
+ volatile unsigned char wide_want[16][4];
+
+ /* Bit fielded set of targets we want to speak synchronously with */
+ volatile u16 initiate_sdtr;
+ /* Bit fielded set of targets we want to speak wide with */
+ volatile u16 initiate_wdtr;
+ /* Bit fielded list of targets we've talked to. */
+ volatile u16 talked_to;
+
+ /* Array of bit-fielded lun lists that we need to request_sense */
+ volatile unsigned char request_sense[16];
+
+ u32 addr_reconnect_dsa_head; /* RISCy style constant,
+ address of following */
+ volatile u32 reconnect_dsa_head;
+ /* Data identifying nexus we are trying to match during reselection */
+ volatile unsigned char reselected_identify; /* IDENTIFY message */
+ volatile unsigned char reselected_tag; /* second byte of queue tag
+ message or 0 */
+ /* These were static variables before we moved them */
+
+ s32 NCR53c7xx_zero;
+ s32 NCR53c7xx_sink;
+ u32 NOP_insn;
+ char NCR53c7xx_msg_reject;
+ char NCR53c7xx_msg_abort;
+ char NCR53c7xx_msg_nop;
+
+ volatile int event_size, event_index;
+ volatile struct NCR53c7x0_event *events;
+
+ /* If we need to generate code to kill off the currently connected
+ command, this is where we do it. Should have a BMI instruction
+ to source or sink the current data, followed by a JUMP
+ to abort_connected */
+
+ u32 *abort_script;
+
+ int script_count; /* Size of script in words */
+ u32 script[0]; /* Relocated SCSI script */
+
+};
+
+#define IRQ_NONE 255
+#define DMA_NONE 255
+#define IRQ_AUTO 254
+#define DMA_AUTO 254
+
+#define BOARD_GENERIC 0
+
+#define NCR53c7x0_insn_size(insn) \
+ (((insn) & DCMD_TYPE_MASK) == DCMD_TYPE_MMI ? 3 : 2)
+
+
+#define NCR53c7x0_local_declare() \
+ volatile unsigned char *NCR53c7x0_address_memory; \
+ unsigned int NCR53c7x0_address_io; \
+ int NCR53c7x0_memory_mapped
+
+#define NCR53c7x0_local_setup(host) \
+ NCR53c7x0_address_memory = (void *) (host)->base; \
+ NCR53c7x0_address_io = (unsigned int) (host)->io_port; \
+ NCR53c7x0_memory_mapped = ((struct NCR53c7x0_hostdata *) \
+ host->hostdata)-> options & OPTION_MEMORY_MAPPED
+
+#define NCR53c7x0_read8(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readb(NCR53c7x0_address_memory + (address)) : \
+ inb(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read16(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int)readw(NCR53c7x0_address_memory + (address)) : \
+ inw(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_read32(address) \
+ (NCR53c7x0_memory_mapped ? \
+ (unsigned int) readl(NCR53c7x0_address_memory + (address)) : \
+ inl(NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write8(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writeb((value), NCR53c7x0_address_memory + (address)); mb();}) : \
+ outb((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write16(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writew((value), NCR53c7x0_address_memory + (address)); mb();}) : \
+ outw((value), NCR53c7x0_address_io + (address)))
+
+#define NCR53c7x0_write32(address,value) \
+ (NCR53c7x0_memory_mapped ? \
+ ({writel((value), NCR53c7x0_address_memory + (address)); mb();}) : \
+ outl((value), NCR53c7x0_address_io + (address)))
+
+/* Patch arbitrary 32 bit words in the script */
+#define patch_abs_32(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) { \
+ (script)[A_##symbol##_used[i] - (offset)] += (value); \
+ if (hostdata->options & OPTION_DEBUG_FIXUP) \
+ printk("scsi%d : %s reference %d at 0x%x in %s is now 0x%x\n",\
+ host->host_no, #symbol, i, A_##symbol##_used[i] - \
+ (int)(offset), #script, (script)[A_##symbol##_used[i] - \
+ (offset)]); \
+ }
+
+/* Patch read/write instruction immediate field */
+#define patch_abs_rwri_data(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) \
+ (script)[A_##symbol##_used[i] - (offset)] = \
+ ((script)[A_##symbol##_used[i] - (offset)] & \
+ ~DBC_RWRI_IMMEDIATE_MASK) | \
+ (((value) << DBC_RWRI_IMMEDIATE_SHIFT) & \
+ DBC_RWRI_IMMEDIATE_MASK)
+
+/* Patch transfer control instruction data field */
+#define patch_abs_tci_data(script, offset, symbol, value) \
+ for (i = 0; i < (sizeof (A_##symbol##_used) / sizeof \
+ (u32)); ++i) \
+ (script)[A_##symbol##_used[i] - (offset)] = \
+ ((script)[A_##symbol##_used[i] - (offset)] & \
+ ~DBC_TCI_DATA_MASK) | \
+ (((value) << DBC_TCI_DATA_SHIFT) & \
+ DBC_TCI_DATA_MASK)
+
+/* Patch field in dsa structure (assignment should be +=?) */
+#define patch_dsa_32(dsa, symbol, word, value) \
+ { \
+ (dsa)[(hostdata->##symbol - hostdata->dsa_start) / sizeof(u32) \
+ + (word)] = (value); \
+ if (hostdata->options & OPTION_DEBUG_DSA) \
+ printk("scsi : dsa %s symbol %s(%d) word %d now 0x%x\n", \
+ #dsa, #symbol, hostdata->##symbol, \
+ (word), (u32) (value)); \
+ }
+
+/* Paranoid people could use panic() here. */
+#define FATAL(host) shutdown((host));
+
+#endif /* NCR53c7x0_C */
+#endif /* NCR53c7x0_H */
diff --git a/i386/i386at/gpl/linux/scsi/53c8xx_d.h b/i386/i386at/gpl/linux/scsi/53c8xx_d.h
new file mode 100644
index 00000000..dd45baee
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/53c8xx_d.h
@@ -0,0 +1,2677 @@
+u32 SCRIPT[] = {
+/*
+
+
+; NCR 53c810 driver, main script
+; Sponsored by
+; iX Multiuser Multitasking Magazine
+; hm@ix.de
+;
+; Copyright 1993, 1994, 1995 Drew Eckhardt
+; Visionary Computing
+; (Unix and Linux consulting and custom programming)
+; drew@PoohSticks.ORG
+; +1 (303) 786-7975
+;
+; TolerANT and SCSI SCRIPTS are registered trademarks of NCR Corporation.
+;
+; PRE-ALPHA
+;
+; For more information, please consult
+;
+; NCR 53C810
+; PCI-SCSI I/O Processor
+; Data Manual
+;
+; NCR 53C710
+; SCSI I/O Processor
+; Programmers Guide
+;
+; NCR Microelectronics
+; 1635 Aeroplaza Drive
+; Colorado Springs, CO 80916
+; 1+ (719) 578-3400
+;
+; Toll free literature number
+; +1 (800) 334-5454
+;
+; IMPORTANT : This code is self modifying due to the limitations of
+; the NCR53c7,8xx series chips. Persons debugging this code with
+; the remote debugger should take this into account, and NOT set
+; breakpoints in modified instructions.
+;
+; Design:
+; The NCR53c7,8xx family of SCSI chips are busmasters with an onboard
+; microcontroller using a simple instruction set.
+;
+; So, to minimize the effects of interrupt latency, and to maximize
+; throughput, this driver offloads the practical maximum amount
+; of processing to the SCSI chip while still maintaining a common
+; structure.
+;
+; Where tradeoffs were needed between efficiency on the older
+; chips and the newer NCR53c800 series, the NCR53c800 series
+; was chosen.
+;
+; While the NCR53c700 and NCR53c700-66 lacked the facilities to fully
+; automate SCSI transfers without host processor intervention, this
+; isn't the case with the NCR53c710 and newer chips which allow
+;
+; - reads and writes to the internal registers from within the SCSI
+; scripts, allowing the SCSI SCRIPTS(tm) code to save processor
+; state so that multiple threads of execution are possible, and also
+; provide an ALU for loop control, etc.
+;
+; - table indirect addressing for some instructions. This allows
+; pointers to be located relative to the DSA ((Data Structure
+; Address) register.
+;
+; These features make it possible to implement a mailbox style interface,
+; where the same piece of code is run to handle I/O for multiple threads
+; at once minimizing our need to relocate code. Since the NCR53c700/
+; NCR53c800 series have a unique combination of features, making a
+; a standard ingoing/outgoing mailbox system, costly, I've modified it.
+;
+; - Mailboxes are a mixture of code and data. This lets us greatly
+; simplify the NCR53c810 code and do things that would otherwise
+; not be possible.
+;
+; The saved data pointer is now implemented as follows :
+;
+; Control flow has been architected such that if control reaches
+; munge_save_data_pointer, on a restore pointers message or
+; reconnection, a jump to the address formerly in the TEMP register
+; will allow the SCSI command to resume execution.
+;
+
+;
+; Note : the DSA structures must be aligned on 32 bit boundaries,
+; since the source and destination of MOVE MEMORY instructions
+; must share the same alignment and this is the alignment of the
+; NCR registers.
+;
+
+ABSOLUTE dsa_temp_lun = 0 ; Patch to lun for current dsa
+ABSOLUTE dsa_temp_next = 0 ; Patch to dsa next for current dsa
+ABSOLUTE dsa_temp_addr_next = 0 ; Patch to address of dsa next address
+ ; for current dsa
+ABSOLUTE dsa_temp_sync = 0 ; Patch to address of per-target
+ ; sync routine
+ABSOLUTE dsa_temp_target = 0 ; Patch to id for current dsa
+ABSOLUTE dsa_temp_addr_saved_pointer = 0; Patch to address of per-command
+ ; saved data pointer
+ABSOLUTE dsa_temp_addr_residual = 0 ; Patch to address of per-command
+ ; current residual code
+ABSOLUTE dsa_temp_addr_saved_residual = 0; Patch to address of per-command
+ ; saved residual code
+ABSOLUTE dsa_temp_addr_new_value = 0 ; Address of value for JUMP operand
+ABSOLUTE dsa_temp_addr_array_value = 0 ; Address to copy to
+ABSOLUTE dsa_temp_addr_dsa_value = 0 ; Address of this DSA value
+
+;
+; Once a device has initiated reselection, we need to compare it
+; against the singly linked list of commands which have disconnected
+; and are pending reselection. These commands are maintained in
+; an unordered singly linked list of DSA structures, through the
+; DSA pointers at their 'centers' headed by the reconnect_dsa_head
+; pointer.
+;
+; To avoid complications in removing commands from the list,
+; I minimize the amount of expensive (at eight operations per
+; addition @ 500-600ns each) pointer operations which must
+; be done in the NCR driver by precomputing them on the
+; host processor during dsa structure generation.
+;
+; The fixed-up per DSA code knows how to recognize the nexus
+; associated with the corresponding SCSI command, and modifies
+; the source and destination pointers for the MOVE MEMORY
+; instruction which is executed when reselected_ok is called
+; to remove the command from the list. Similarly, DSA is
+; loaded with the address of the next DSA structure and
+; reselected_check_next is called if a failure occurs.
+;
+; Perhaps more conscisely, the net effect of the mess is
+;
+; for (dsa = reconnect_dsa_head, dest = &reconnect_dsa_head,
+; src = NULL; dsa; dest = &dsa->next, dsa = dsa->next) {
+; src = &dsa->next;
+; if (target_id == dsa->id && target_lun == dsa->lun) {
+; *dest = *src;
+; break;
+; }
+; }
+;
+; if (!dsa)
+; error (int_err_unexpected_reselect);
+; else
+; longjmp (dsa->jump_resume, 0);
+;
+;
+
+
+; Define DSA structure used for mailboxes
+ENTRY dsa_code_template
+dsa_code_template:
+ENTRY dsa_code_begin
+dsa_code_begin:
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000000 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, dsa_temp_addr_dsa_value, addr_scratch
+
+at 0x00000002 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000005 : */ 0x78380000,0x00000000,
+/*
+ CALL scratch_to_dsa
+
+at 0x00000007 : */ 0x88080000,0x00000980,
+/*
+ CALL select
+
+at 0x00000009 : */ 0x88080000,0x000001fc,
+/*
+; Handle the phase mismatch which may have resulted from the
+; MOVE FROM dsa_msgout if we returned here. The CLEAR ATN
+; may or may not be necessary, and we should update script_asm.pl
+; to handle multiple pieces.
+ CLEAR ATN
+
+at 0x0000000b : */ 0x60000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000000d : */ 0x60000040,0x00000000,
+/*
+
+; Replace second operand with address of JUMP instruction dest operand
+; in schedule table for this DSA. Becomes dsa_jump_dest in 53c7,8xx.c.
+ENTRY dsa_code_fix_jump
+dsa_code_fix_jump:
+ MOVE MEMORY 4, NOP_insn, 0
+
+at 0x0000000f : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ JUMP select_done
+
+at 0x00000012 : */ 0x80080000,0x00000224,
+/*
+
+; wrong_dsa loads the DSA register with the value of the dsa_next
+; field.
+;
+wrong_dsa:
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the destination address is the address of the OLD
+; next pointer.
+;
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok + 8
+
+at 0x00000014 : */ 0xc0000004,0x00000000,0x00000758,
+/*
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000017 : */ 0x78380000,0x00000000,
+/*
+;
+; Move the _contents_ of the next pointer into the DSA register as
+; the next I_T_L or I_T_L_Q tupple to check against the established
+; nexus.
+;
+ MOVE MEMORY 4, dsa_temp_next, addr_scratch
+
+at 0x00000019 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x0000001c : */ 0x78380000,0x00000000,
+/*
+ CALL scratch_to_dsa
+
+at 0x0000001e : */ 0x88080000,0x00000980,
+/*
+ JUMP reselected_check_next
+
+at 0x00000020 : */ 0x80080000,0x000006a4,
+/*
+
+ABSOLUTE dsa_save_data_pointer = 0
+ENTRY dsa_code_save_data_pointer
+dsa_code_save_data_pointer:
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000022 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_temp, dsa_temp_addr_saved_pointer
+
+at 0x00000024 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000027 : */ 0x78380000,0x00000000,
+/*
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_residual, dsa_temp_addr_saved_residual
+
+at 0x00000029 : */ 0xc0000018,0x00000000,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000002c : */ 0x60000040,0x00000000,
+/*
+
+
+
+ RETURN
+
+at 0x0000002e : */ 0x90080000,0x00000000,
+/*
+ABSOLUTE dsa_restore_pointers = 0
+ENTRY dsa_code_restore_pointers
+dsa_code_restore_pointers:
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000030 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, dsa_temp_addr_saved_pointer, addr_temp
+
+at 0x00000032 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000035 : */ 0x78380000,0x00000000,
+/*
+; HARD CODED : 24 bytes needs to agree with 53c7,8xx.h
+ MOVE MEMORY 24, dsa_temp_addr_saved_residual, dsa_temp_addr_residual
+
+at 0x00000037 : */ 0xc0000018,0x00000000,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000003a : */ 0x60000040,0x00000000,
+/*
+
+
+
+ RETURN
+
+at 0x0000003c : */ 0x90080000,0x00000000,
+/*
+
+ABSOLUTE dsa_check_reselect = 0
+; dsa_check_reselect determines whether or not the current target and
+; lun match the current DSA
+ENTRY dsa_code_check_reselect
+dsa_code_check_reselect:
+ MOVE SSID TO SFBR ; SSID contains 3 bit target ID
+
+at 0x0000003e : */ 0x720a0000,0x00000000,
+/*
+; FIXME : we need to accomodate bit fielded and binary here for '7xx/'8xx chips
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_target, AND MASK 0xf8
+
+at 0x00000040 : */ 0x8084f800,0x00ffff48,
+/*
+;
+; Hack - move to scratch first, since SFBR is not writeable
+; via the CPU and hence a MOVE MEMORY instruction.
+;
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x00000042 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 1, reselected_identify, addr_scratch
+
+at 0x00000044 : */ 0xc0000001,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000047 : */ 0x78380000,0x00000000,
+/*
+ MOVE SCRATCH0 TO SFBR
+
+at 0x00000049 : */ 0x72340000,0x00000000,
+/*
+; FIXME : we need to accomodate bit fielded and binary here for '7xx/'8xx chips
+ JUMP REL (wrong_dsa), IF NOT dsa_temp_lun, AND MASK 0xf8
+
+at 0x0000004b : */ 0x8084f800,0x00ffff1c,
+/*
+; Patch the MOVE MEMORY INSTRUCTION such that
+; the source address is the address of this dsa's
+; next pointer.
+ MOVE MEMORY 4, dsa_temp_addr_next, reselected_ok + 4
+
+at 0x0000004d : */ 0xc0000004,0x00000000,0x00000754,
+/*
+ CALL reselected_ok
+
+at 0x00000050 : */ 0x88080000,0x00000750,
+/*
+ CALL dsa_temp_sync
+
+at 0x00000052 : */ 0x88080000,0x00000000,
+/*
+; Release ACK on the IDENTIFY message _after_ we've set the synchronous
+; transfer parameters!
+ CLEAR ACK
+
+at 0x00000054 : */ 0x60000040,0x00000000,
+/*
+; Implicitly restore pointers on reselection, so a RETURN
+; will transfer control back to the right spot.
+ CALL REL (dsa_code_restore_pointers)
+
+at 0x00000056 : */ 0x88880000,0x00ffff60,
+/*
+ RETURN
+
+at 0x00000058 : */ 0x90080000,0x00000000,
+/*
+ENTRY dsa_zero
+dsa_zero:
+ENTRY dsa_code_template_end
+dsa_code_template_end:
+
+; Perform sanity check for dsa_fields_start == dsa_code_template_end -
+; dsa_zero, puke.
+
+ABSOLUTE dsa_fields_start = 0 ; Sanity marker
+ ; pad 48 bytes (fix this RSN)
+ABSOLUTE dsa_next = 48 ; len 4 Next DSA
+ ; del 4 Previous DSA address
+ABSOLUTE dsa_cmnd = 56 ; len 4 Scsi_Cmnd * for this thread.
+ABSOLUTE dsa_select = 60 ; len 4 Device ID, Period, Offset for
+ ; table indirect select
+ABSOLUTE dsa_msgout = 64 ; len 8 table indirect move parameter for
+ ; select message
+ABSOLUTE dsa_cmdout = 72 ; len 8 table indirect move parameter for
+ ; command
+ABSOLUTE dsa_dataout = 80 ; len 4 code pointer for dataout
+ABSOLUTE dsa_datain = 84 ; len 4 code pointer for datain
+ABSOLUTE dsa_msgin = 88 ; len 8 table indirect move for msgin
+ABSOLUTE dsa_status = 96 ; len 8 table indirect move for status byte
+ABSOLUTE dsa_msgout_other = 104 ; len 8 table indirect for normal message out
+ ; (Synchronous transfer negotiation, etc).
+ABSOLUTE dsa_end = 112
+
+ABSOLUTE schedule = 0 ; Array of JUMP dsa_begin or JUMP (next),
+ ; terminated by a call to JUMP wait_reselect
+
+; Linked lists of DSA structures
+ABSOLUTE reconnect_dsa_head = 0 ; Link list of DSAs which can reconnect
+ABSOLUTE addr_reconnect_dsa_head = 0 ; Address of variable contataining
+ ; address of reconnect_dsa_head
+
+; These select the source and destination of a MOVE MEMORY instruction
+ABSOLUTE dmode_memory_to_memory = 0x0
+ABSOLUTE dmode_memory_to_ncr = 0x0
+ABSOLUTE dmode_ncr_to_memory = 0x0
+
+ABSOLUTE addr_scratch = 0x0
+ABSOLUTE addr_temp = 0x0
+
+
+; Interrupts -
+; MSB indicates type
+; 0 handle error condition
+; 1 handle message
+; 2 handle normal condition
+; 3 debugging interrupt
+; 4 testing interrupt
+; Next byte indicates specific error
+
+; XXX not yet implemented, I'm not sure if I want to -
+; Next byte indicates the routine the error occurred in
+; The LSB indicates the specific place the error occurred
+
+ABSOLUTE int_err_unexpected_phase = 0x00000000 ; Unexpected phase encountered
+ABSOLUTE int_err_selected = 0x00010000 ; SELECTED (nee RESELECTED)
+ABSOLUTE int_err_unexpected_reselect = 0x00020000
+ABSOLUTE int_err_check_condition = 0x00030000
+ABSOLUTE int_err_no_phase = 0x00040000
+ABSOLUTE int_msg_wdtr = 0x01000000 ; WDTR message received
+ABSOLUTE int_msg_sdtr = 0x01010000 ; SDTR received
+ABSOLUTE int_msg_1 = 0x01020000 ; single byte special message
+ ; received
+
+ABSOLUTE int_norm_select_complete = 0x02000000 ; Select complete, reprogram
+ ; registers.
+ABSOLUTE int_norm_reselect_complete = 0x02010000 ; Nexus established
+ABSOLUTE int_norm_command_complete = 0x02020000 ; Command complete
+ABSOLUTE int_norm_disconnected = 0x02030000 ; Disconnected
+ABSOLUTE int_norm_aborted =0x02040000 ; Aborted *dsa
+ABSOLUTE int_norm_reset = 0x02050000 ; Generated BUS reset.
+ABSOLUTE int_debug_break = 0x03000000 ; Break point
+
+ABSOLUTE int_debug_panic = 0x030b0000 ; Panic driver
+
+
+ABSOLUTE int_test_1 = 0x04000000 ; Test 1 complete
+ABSOLUTE int_test_2 = 0x04010000 ; Test 2 complete
+ABSOLUTE int_test_3 = 0x04020000 ; Test 3 complete
+
+
+; These should start with 0x05000000, with low bits incrementing for
+; each one.
+
+
+
+ABSOLUTE NCR53c7xx_msg_abort = 0 ; Pointer to abort message
+ABSOLUTE NCR53c7xx_msg_reject = 0 ; Pointer to reject message
+ABSOLUTE NCR53c7xx_zero = 0 ; long with zero in it, use for source
+ABSOLUTE NCR53c7xx_sink = 0 ; long to dump worthless data in
+ABSOLUTE NOP_insn = 0 ; NOP instruction
+
+; Pointer to message, potentially multi-byte
+ABSOLUTE msg_buf = 0
+
+; Pointer to holding area for reselection information
+ABSOLUTE reselected_identify = 0
+ABSOLUTE reselected_tag = 0
+
+; Request sense command pointer, it's a 6 byte command, should
+; be constant for all commands since we always want 16 bytes of
+; sense and we don't need to change any fields as we did under
+; SCSI-I when we actually cared about the LUN field.
+;EXTERNAL NCR53c7xx_sense ; Request sense command
+
+
+; dsa_schedule
+; PURPOSE : after a DISCONNECT message has been received, and pointers
+; saved, insert the current DSA structure at the head of the
+; disconnected queue and fall through to the scheduler.
+;
+; CALLS : OK
+;
+; INPUTS : dsa - current DSA structure, reconnect_dsa_head - list
+; of disconnected commands
+;
+; MODIFIES : SCRATCH, reconnect_dsa_head
+;
+; EXITS : always passes control to schedule
+
+ENTRY dsa_schedule
+dsa_schedule:
+
+
+
+
+;
+; Calculate the address of the next pointer within the DSA
+; structure of the command that is currently disconnecting
+;
+ CALL dsa_to_scratch
+
+at 0x0000005a : */ 0x88080000,0x00000938,
+/*
+ MOVE SCRATCH0 + dsa_next TO SCRATCH0
+
+at 0x0000005c : */ 0x7e343000,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x0000005e : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x00000060 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x00000062 : */ 0x7f370000,0x00000000,
+/*
+
+; Point the next field of this DSA structure at the current disconnected
+; list
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000064 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, dsa_schedule_insert + 8
+
+at 0x00000066 : */ 0xc0000004,0x00000000,0x000001b4,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000069 : */ 0x78380000,0x00000000,
+/*
+dsa_schedule_insert:
+ MOVE MEMORY 4, reconnect_dsa_head, 0
+
+at 0x0000006b : */ 0xc0000004,0x00000000,0x00000000,
+/*
+
+; And update the head pointer.
+ CALL dsa_to_scratch
+
+at 0x0000006e : */ 0x88080000,0x00000938,
+/*
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000070 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, reconnect_dsa_head
+
+at 0x00000072 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000075 : */ 0x78380000,0x00000000,
+/*
+
+
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000077 : */ 0x7c027f00,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000079 : */ 0x60000040,0x00000000,
+/*
+
+ WAIT DISCONNECT
+
+at 0x0000007b : */ 0x48000000,0x00000000,
+/*
+
+
+
+
+
+
+ JUMP schedule
+
+at 0x0000007d : */ 0x80080000,0x00000000,
+/*
+
+
+;
+; select
+;
+; PURPOSE : establish a nexus for the SCSI command referenced by DSA.
+; On success, the current DSA structure is removed from the issue
+; queue. Usually, this is entered as a fall-through from schedule,
+; although the contingent allegiance handling code will write
+; the select entry address to the DSP to restart a command as a
+; REQUEST SENSE. A message is sent (usually IDENTIFY, although
+; additional SDTR or WDTR messages may be sent). COMMAND OUT
+; is handled.
+;
+; INPUTS : DSA - SCSI command, issue_dsa_head
+;
+; CALLS : NOT OK
+;
+; MODIFIES : SCRATCH, issue_dsa_head
+;
+; EXITS : on reselection or selection, go to select_failed
+; otherwise, RETURN so control is passed back to
+; dsa_begin.
+;
+
+ENTRY select
+select:
+
+
+
+
+
+
+
+
+
+
+
+
+ CLEAR TARGET
+
+at 0x0000007f : */ 0x60000200,0x00000000,
+/*
+
+; XXX
+;
+; In effect, SELECTION operations are backgrounded, with execution
+; continuing until code which waits for REQ or a fatal interrupt is
+; encountered.
+;
+; So, for more performance, we could overlap the code which removes
+; the command from the NCRs issue queue with the selection, but
+; at this point I don't want to deal with the error recovery.
+;
+
+
+ SELECT ATN FROM dsa_select, select_failed
+
+at 0x00000081 : */ 0x4300003c,0x000007a4,
+/*
+ JUMP select_msgout, WHEN MSG_OUT
+
+at 0x00000083 : */ 0x860b0000,0x00000214,
+/*
+ENTRY select_msgout
+select_msgout:
+ MOVE FROM dsa_msgout, WHEN MSG_OUT
+
+at 0x00000085 : */ 0x1e000000,0x00000040,
+/*
+
+
+
+
+
+
+
+
+
+
+ RETURN
+
+at 0x00000087 : */ 0x90080000,0x00000000,
+/*
+
+;
+; select_done
+;
+; PURPOSE: continue on to normal data transfer; called as the exit
+; point from dsa_begin.
+;
+; INPUTS: dsa
+;
+; CALLS: OK
+;
+;
+
+select_done:
+
+
+
+
+
+
+
+; After a successful selection, we should get either a CMD phase or
+; some transfer request negotiation message.
+
+ JUMP cmdout, WHEN CMD
+
+at 0x00000089 : */ 0x820b0000,0x00000244,
+/*
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x0000008b : */ 0x9f030000,0x00000000,
+/*
+
+select_msg_in:
+ CALL msg_in, WHEN MSG_IN
+
+at 0x0000008d : */ 0x8f0b0000,0x00000404,
+/*
+ JUMP select_msg_in, WHEN MSG_IN
+
+at 0x0000008f : */ 0x870b0000,0x00000234,
+/*
+
+cmdout:
+ INT int_err_unexpected_phase, WHEN NOT CMD
+
+at 0x00000091 : */ 0x9a030000,0x00000000,
+/*
+
+
+
+ENTRY cmdout_cmdout
+cmdout_cmdout:
+
+ MOVE FROM dsa_cmdout, WHEN CMD
+
+at 0x00000093 : */ 0x1a000000,0x00000048,
+/*
+
+
+
+
+;
+; data_transfer
+; other_out
+; other_in
+; other_transfer
+;
+; PURPOSE : handle the main data transfer for a SCSI command in
+; several parts. In the first part, data_transfer, DATA_IN
+; and DATA_OUT phases are allowed, with the user provided
+; code (usually dynamically generated based on the scatter/gather
+; list associated with a SCSI command) called to handle these
+; phases.
+;
+; After control has passed to one of the user provided
+; DATA_IN or DATA_OUT routines, back calls are made to
+; other_tranfer_in or other_transfer_out to handle non-DATA IN
+; and DATA OUT phases respectively, with the state of the active
+; data pointer being preserved in TEMP.
+;
+; On completion, the user code passes control to other_transfer
+; which causes DATA_IN and DATA_OUT to result in unexpected_phase
+; interrupts so that data overruns may be trapped.
+;
+; INPUTS : DSA - SCSI command
+;
+; CALLS : OK in data_transfer_start, not ok in other_out and other_in, ok in
+; other_transfer
+;
+; MODIFIES : SCRATCH
+;
+; EXITS : if STATUS IN is detected, signifying command completion,
+; the NCR jumps to command_complete. If MSG IN occurs, a
+; CALL is made to msg_in. Otherwise, other_transfer runs in
+; an infinite loop.
+;
+
+ENTRY data_transfer
+data_transfer:
+ JUMP cmdout_cmdout, WHEN CMD
+
+at 0x00000095 : */ 0x820b0000,0x0000024c,
+/*
+ CALL msg_in, WHEN MSG_IN
+
+at 0x00000097 : */ 0x8f0b0000,0x00000404,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x00000099 : */ 0x9e0b0000,0x00000000,
+/*
+ JUMP do_dataout, WHEN DATA_OUT
+
+at 0x0000009b : */ 0x800b0000,0x0000028c,
+/*
+ JUMP do_datain, WHEN DATA_IN
+
+at 0x0000009d : */ 0x810b0000,0x000002e4,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x0000009f : */ 0x830b0000,0x0000060c,
+/*
+ JUMP data_transfer
+
+at 0x000000a1 : */ 0x80080000,0x00000254,
+/*
+ENTRY end_data_transfer
+end_data_transfer:
+
+;
+; FIXME: On NCR53c700 and NCR53c700-66 chips, do_dataout/do_datain
+; should be fixed up whenever the nexus changes so it can point to the
+; correct routine for that command.
+;
+
+
+; Nasty jump to dsa->dataout
+do_dataout:
+ CALL dsa_to_scratch
+
+at 0x000000a3 : */ 0x88080000,0x00000938,
+/*
+ MOVE SCRATCH0 + dsa_dataout TO SCRATCH0
+
+at 0x000000a5 : */ 0x7e345000,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x000000a7 : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x000000a9 : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x000000ab : */ 0x7f370000,0x00000000,
+/*
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000000ad : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, dataout_to_jump + 4
+
+at 0x000000af : */ 0xc0000004,0x00000000,0x000002d4,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000000b2 : */ 0x78380000,0x00000000,
+/*
+dataout_to_jump:
+ MOVE MEMORY 4, 0, dataout_jump + 4
+
+at 0x000000b4 : */ 0xc0000004,0x00000000,0x000002e0,
+/*
+dataout_jump:
+ JUMP 0
+
+at 0x000000b7 : */ 0x80080000,0x00000000,
+/*
+
+; Nasty jump to dsa->dsain
+do_datain:
+ CALL dsa_to_scratch
+
+at 0x000000b9 : */ 0x88080000,0x00000938,
+/*
+ MOVE SCRATCH0 + dsa_datain TO SCRATCH0
+
+at 0x000000bb : */ 0x7e345400,0x00000000,
+/*
+ MOVE SCRATCH1 + 0 TO SCRATCH1 WITH CARRY
+
+at 0x000000bd : */ 0x7f350000,0x00000000,
+/*
+ MOVE SCRATCH2 + 0 TO SCRATCH2 WITH CARRY
+
+at 0x000000bf : */ 0x7f360000,0x00000000,
+/*
+ MOVE SCRATCH3 + 0 TO SCRATCH3 WITH CARRY
+
+at 0x000000c1 : */ 0x7f370000,0x00000000,
+/*
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000000c3 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, datain_to_jump + 4
+
+at 0x000000c5 : */ 0xc0000004,0x00000000,0x0000032c,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000000c8 : */ 0x78380000,0x00000000,
+/*
+ENTRY datain_to_jump
+datain_to_jump:
+ MOVE MEMORY 4, 0, datain_jump + 4
+
+at 0x000000ca : */ 0xc0000004,0x00000000,0x00000338,
+/*
+
+
+
+datain_jump:
+ JUMP 0
+
+at 0x000000cd : */ 0x80080000,0x00000000,
+/*
+
+
+
+; Note that other_out and other_in loop until a non-data phase
+; is discoverred, so we only execute return statements when we
+; can go on to the next data phase block move statement.
+
+ENTRY other_out
+other_out:
+
+
+
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000cf : */ 0x9a0b0000,0x00000000,
+/*
+ JUMP msg_in_restart, WHEN MSG_IN
+
+at 0x000000d1 : */ 0x870b0000,0x000003e4,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000d3 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000d5 : */ 0x990b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000d7 : */ 0x830b0000,0x0000060c,
+/*
+ JUMP other_out, WHEN NOT DATA_OUT
+
+at 0x000000d9 : */ 0x80030000,0x0000033c,
+/*
+ RETURN
+
+at 0x000000db : */ 0x90080000,0x00000000,
+/*
+
+ENTRY other_in
+other_in:
+
+
+
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000dd : */ 0x9a0b0000,0x00000000,
+/*
+ JUMP msg_in_restart, WHEN MSG_IN
+
+at 0x000000df : */ 0x870b0000,0x000003e4,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000e1 : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000e3 : */ 0x980b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000e5 : */ 0x830b0000,0x0000060c,
+/*
+ JUMP other_in, WHEN NOT DATA_IN
+
+at 0x000000e7 : */ 0x81030000,0x00000374,
+/*
+ RETURN
+
+at 0x000000e9 : */ 0x90080000,0x00000000,
+/*
+
+
+ENTRY other_transfer
+other_transfer:
+ INT int_err_unexpected_phase, WHEN CMD
+
+at 0x000000eb : */ 0x9a0b0000,0x00000000,
+/*
+ CALL msg_in, WHEN MSG_IN
+
+at 0x000000ed : */ 0x8f0b0000,0x00000404,
+/*
+ INT int_err_unexpected_phase, WHEN MSG_OUT
+
+at 0x000000ef : */ 0x9e0b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_OUT
+
+at 0x000000f1 : */ 0x980b0000,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN DATA_IN
+
+at 0x000000f3 : */ 0x990b0000,0x00000000,
+/*
+ JUMP command_complete, WHEN STATUS
+
+at 0x000000f5 : */ 0x830b0000,0x0000060c,
+/*
+ JUMP other_transfer
+
+at 0x000000f7 : */ 0x80080000,0x000003ac,
+/*
+
+;
+; msg_in_restart
+; msg_in
+; munge_msg
+;
+; PURPOSE : process messages from a target. msg_in is called when the
+; caller hasn't read the first byte of the message. munge_message
+; is called when the caller has read the first byte of the message,
+; and left it in SFBR. msg_in_restart is called when the caller
+; hasnt read the first byte of the message, and wishes RETURN
+; to transfer control back to the address of the conditional
+; CALL instruction rather than to the instruction after it.
+;
+; Various int_* interrupts are generated when the host system
+; needs to intervene, as is the case with SDTR, WDTR, and
+; INITIATE RECOVERY messages.
+;
+; When the host system handles one of these interrupts,
+; it can respond by reentering at reject_message,
+; which rejects the message and returns control to
+; the caller of msg_in or munge_msg, accept_message
+; which clears ACK and returns control, or reply_message
+; which sends the message pointed to by the DSA
+; msgout_other table indirect field.
+;
+; DISCONNECT messages are handled by moving the command
+; to the reconnect_dsa_queue.
+;
+; INPUTS : DSA - SCSI COMMAND, SFBR - first byte of message (munge_msg
+; only)
+;
+; CALLS : NO. The TEMP register isn't backed up to allow nested calls.
+;
+; MODIFIES : SCRATCH, DSA on DISCONNECT
+;
+; EXITS : On receipt of SAVE DATA POINTER, RESTORE POINTERS,
+; and normal return from message handlers running under
+; Linux, control is returned to the caller. Receipt
+; of DISCONNECT messages pass control to dsa_schedule.
+;
+ENTRY msg_in_restart
+msg_in_restart:
+; XXX - hackish
+;
+; Since it's easier to debug changes to the statically
+; compiled code, rather than the dynamically generated
+; stuff, such as
+;
+; MOVE x, y, WHEN data_phase
+; CALL other_z, WHEN NOT data_phase
+; MOVE x, y, WHEN data_phase
+;
+; I'd like to have certain routines (notably the message handler)
+; restart on the conditional call rather than the next instruction.
+;
+; So, subtract 8 from the return address
+
+ MOVE TEMP0 + 0xf8 TO TEMP0
+
+at 0x000000f9 : */ 0x7e1cf800,0x00000000,
+/*
+ MOVE TEMP1 + 0xff TO TEMP1 WITH CARRY
+
+at 0x000000fb : */ 0x7f1dff00,0x00000000,
+/*
+ MOVE TEMP2 + 0xff TO TEMP2 WITH CARRY
+
+at 0x000000fd : */ 0x7f1eff00,0x00000000,
+/*
+ MOVE TEMP3 + 0xff TO TEMP3 WITH CARRY
+
+at 0x000000ff : */ 0x7f1fff00,0x00000000,
+/*
+
+ENTRY msg_in
+msg_in:
+ MOVE 1, msg_buf, WHEN MSG_IN
+
+at 0x00000101 : */ 0x0f000001,0x00000000,
+/*
+
+munge_msg:
+ JUMP munge_extended, IF 0x01 ; EXTENDED MESSAGE
+
+at 0x00000103 : */ 0x800c0001,0x00000524,
+/*
+ JUMP munge_2, IF 0x20, AND MASK 0xdf ; two byte message
+
+at 0x00000105 : */ 0x800cdf20,0x0000044c,
+/*
+;
+; XXX - I've seen a handful of broken SCSI devices which fail to issue
+; a SAVE POINTERS message before disconnecting in the middle of
+; a transfer, assuming that the DATA POINTER will be implicitly
+; restored.
+;
+; Historically, I've often done an implicit save when the DISCONNECT
+; message is processed. We may want to consider having the option of
+; doing that here.
+;
+ JUMP munge_save_data_pointer, IF 0x02 ; SAVE DATA POINTER
+
+at 0x00000107 : */ 0x800c0002,0x00000454,
+/*
+ JUMP munge_restore_pointers, IF 0x03 ; RESTORE POINTERS
+
+at 0x00000109 : */ 0x800c0003,0x000004b8,
+/*
+ JUMP munge_disconnect, IF 0x04 ; DISCONNECT
+
+at 0x0000010b : */ 0x800c0004,0x0000051c,
+/*
+ INT int_msg_1, IF 0x07 ; MESSAGE REJECT
+
+at 0x0000010d : */ 0x980c0007,0x01020000,
+/*
+ INT int_msg_1, IF 0x0f ; INITIATE RECOVERY
+
+at 0x0000010f : */ 0x980c000f,0x01020000,
+/*
+
+
+
+ JUMP reject_message
+
+at 0x00000111 : */ 0x80080000,0x000005b4,
+/*
+
+munge_2:
+ JUMP reject_message
+
+at 0x00000113 : */ 0x80080000,0x000005b4,
+/*
+;
+; The SCSI standard allows targets to recover from transient
+; error conditions by backing up the data pointer with a
+; RESTORE POINTERS message.
+;
+; So, we must save and restore the _residual_ code as well as
+; the current instruction pointer. Because of this messiness,
+; it is simpler to put dynamic code in the dsa for this and to
+; just do a simple jump down there.
+;
+
+munge_save_data_pointer:
+ MOVE DSA0 + dsa_save_data_pointer TO SFBR
+
+at 0x00000115 : */ 0x76100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000117 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+
+at 0x00000119 : */ 0x7711ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x0000011b : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+
+at 0x0000011d : */ 0x7712ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x0000011f : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+
+at 0x00000121 : */ 0x7713ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x00000123 : */ 0x6a370000,0x00000000,
+/*
+
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x00000125 : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, jump_dsa_save + 4
+
+at 0x00000127 : */ 0xc0000004,0x00000000,0x000004b4,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x0000012a : */ 0x78380000,0x00000000,
+/*
+jump_dsa_save:
+ JUMP 0
+
+at 0x0000012c : */ 0x80080000,0x00000000,
+/*
+
+munge_restore_pointers:
+ MOVE DSA0 + dsa_restore_pointers TO SFBR
+
+at 0x0000012e : */ 0x76100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000130 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 + 0xff TO SFBR WITH CARRY
+
+at 0x00000132 : */ 0x7711ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x00000134 : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 + 0xff TO SFBR WITH CARRY
+
+at 0x00000136 : */ 0x7712ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x00000138 : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 + 0xff TO SFBR WITH CARRY
+
+at 0x0000013a : */ 0x7713ff00,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x0000013c : */ 0x6a370000,0x00000000,
+/*
+
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x0000013e : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, jump_dsa_restore + 4
+
+at 0x00000140 : */ 0xc0000004,0x00000000,0x00000518,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000143 : */ 0x78380000,0x00000000,
+/*
+jump_dsa_restore:
+ JUMP 0
+
+at 0x00000145 : */ 0x80080000,0x00000000,
+/*
+
+
+munge_disconnect:
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ JUMP dsa_schedule
+
+at 0x00000147 : */ 0x80080000,0x00000168,
+/*
+
+
+
+
+
+munge_extended:
+ CLEAR ACK
+
+at 0x00000149 : */ 0x60000040,0x00000000,
+/*
+ INT int_err_unexpected_phase, WHEN NOT MSG_IN
+
+at 0x0000014b : */ 0x9f030000,0x00000000,
+/*
+ MOVE 1, msg_buf + 1, WHEN MSG_IN
+
+at 0x0000014d : */ 0x0f000001,0x00000001,
+/*
+ JUMP munge_extended_2, IF 0x02
+
+at 0x0000014f : */ 0x800c0002,0x00000554,
+/*
+ JUMP munge_extended_3, IF 0x03
+
+at 0x00000151 : */ 0x800c0003,0x00000584,
+/*
+ JUMP reject_message
+
+at 0x00000153 : */ 0x80080000,0x000005b4,
+/*
+
+munge_extended_2:
+ CLEAR ACK
+
+at 0x00000155 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000157 : */ 0x0f000001,0x00000002,
+/*
+ JUMP reject_message, IF NOT 0x02 ; Must be WDTR
+
+at 0x00000159 : */ 0x80040002,0x000005b4,
+/*
+ CLEAR ACK
+
+at 0x0000015b : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 3, WHEN MSG_IN
+
+at 0x0000015d : */ 0x0f000001,0x00000003,
+/*
+ INT int_msg_wdtr
+
+at 0x0000015f : */ 0x98080000,0x01000000,
+/*
+
+munge_extended_3:
+ CLEAR ACK
+
+at 0x00000161 : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, msg_buf + 2, WHEN MSG_IN
+
+at 0x00000163 : */ 0x0f000001,0x00000002,
+/*
+ JUMP reject_message, IF NOT 0x01 ; Must be SDTR
+
+at 0x00000165 : */ 0x80040001,0x000005b4,
+/*
+ CLEAR ACK
+
+at 0x00000167 : */ 0x60000040,0x00000000,
+/*
+ MOVE 2, msg_buf + 3, WHEN MSG_IN
+
+at 0x00000169 : */ 0x0f000002,0x00000003,
+/*
+ INT int_msg_sdtr
+
+at 0x0000016b : */ 0x98080000,0x01010000,
+/*
+
+ENTRY reject_message
+reject_message:
+ SET ATN
+
+at 0x0000016d : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000016f : */ 0x60000040,0x00000000,
+/*
+ MOVE 1, NCR53c7xx_msg_reject, WHEN MSG_OUT
+
+at 0x00000171 : */ 0x0e000001,0x00000000,
+/*
+ RETURN
+
+at 0x00000173 : */ 0x90080000,0x00000000,
+/*
+
+ENTRY accept_message
+accept_message:
+ CLEAR ATN
+
+at 0x00000175 : */ 0x60000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000177 : */ 0x60000040,0x00000000,
+/*
+ RETURN
+
+at 0x00000179 : */ 0x90080000,0x00000000,
+/*
+
+ENTRY respond_message
+respond_message:
+ SET ATN
+
+at 0x0000017b : */ 0x58000008,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000017d : */ 0x60000040,0x00000000,
+/*
+ MOVE FROM dsa_msgout_other, WHEN MSG_OUT
+
+at 0x0000017f : */ 0x1e000000,0x00000068,
+/*
+ RETURN
+
+at 0x00000181 : */ 0x90080000,0x00000000,
+/*
+
+;
+; command_complete
+;
+; PURPOSE : handle command termination when STATUS IN is detected by reading
+; a status byte followed by a command termination message.
+;
+; Normal termination results in an INTFLY instruction, and
+; the host system can pick out which command terminated by
+; examining the MESSAGE and STATUS buffers of all currently
+; executing commands;
+;
+; Abnormal (CHECK_CONDITION) termination results in an
+; int_err_check_condition interrupt so that a REQUEST SENSE
+; command can be issued out-of-order so that no other command
+; clears the contingent allegiance condition.
+;
+;
+; INPUTS : DSA - command
+;
+; CALLS : OK
+;
+; EXITS : On successful termination, control is passed to schedule.
+; On abnormal termination, the user will usually modify the
+; DSA fields and corresponding buffers and return control
+; to select.
+;
+
+ENTRY command_complete
+command_complete:
+ MOVE FROM dsa_status, WHEN STATUS
+
+at 0x00000183 : */ 0x1b000000,0x00000060,
+/*
+
+ MOVE SFBR TO SCRATCH0 ; Save status
+
+at 0x00000185 : */ 0x6a340000,0x00000000,
+/*
+
+ENTRY command_complete_msgin
+command_complete_msgin:
+ MOVE FROM dsa_msgin, WHEN MSG_IN
+
+at 0x00000187 : */ 0x1f000000,0x00000058,
+/*
+; Indicate that we should be expecting a disconnect
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000189 : */ 0x7c027f00,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x0000018b : */ 0x60000040,0x00000000,
+/*
+
+ WAIT DISCONNECT
+
+at 0x0000018d : */ 0x48000000,0x00000000,
+/*
+
+;
+; The SCSI specification states that when a UNIT ATTENTION condition
+; is pending, as indicated by a CHECK CONDITION status message,
+; the target shall revert to asynchronous transfers. Since
+; synchronous transfers parameters are maintained on a per INITIATOR/TARGET
+; basis, and returning control to our scheduler could work on a command
+; running on another lun on that target using the old parameters, we must
+; interrupt the host processor to get them changed, or change them ourselves.
+;
+; Once SCSI-II tagged queueing is implemented, things will be even more
+; hairy, since contingent allegiance conditions exist on a per-target/lun
+; basis, and issuing a new command with a different tag would clear it.
+; In these cases, we must interrupt the host processor to get a request
+; added to the HEAD of the queue with the request sense command, or we
+; must automatically issue the request sense command.
+
+
+
+
+
+ INTFLY
+
+at 0x0000018f : */ 0x98180000,0x00000000,
+/*
+
+
+
+
+
+ JUMP schedule
+
+at 0x00000191 : */ 0x80080000,0x00000000,
+/*
+command_failed:
+ INT int_err_check_condition
+
+at 0x00000193 : */ 0x98080000,0x00030000,
+/*
+
+
+
+
+;
+; wait_reselect
+;
+; PURPOSE : This is essentially the idle routine, where control lands
+; when there are no new processes to schedule. wait_reselect
+; waits for reselection, selection, and new commands.
+;
+; When a successful reselection occurs, with the aid
+; of fixed up code in each DSA, wait_reselect walks the
+; reconnect_dsa_queue, asking each dsa if the target ID
+; and LUN match its.
+;
+; If a match is found, a call is made back to reselected_ok,
+; which through the miracles of self modifying code, extracts
+; the found DSA from the reconnect_dsa_queue and then
+; returns control to the DSAs thread of execution.
+;
+; INPUTS : NONE
+;
+; CALLS : OK
+;
+; MODIFIES : DSA,
+;
+; EXITS : On successful reselection, control is returned to the
+; DSA which called reselected_ok. If the WAIT RESELECT
+; was interrupted by a new commands arrival signaled by
+; SIG_P, control is passed to schedule. If the NCR is
+; selected, the host system is interrupted with an
+; int_err_selected which is usually responded to by
+; setting DSP to the target_abort address.
+
+ENTRY wait_reselect
+wait_reselect:
+
+
+
+
+
+
+ WAIT RESELECT wait_reselect_failed
+
+at 0x00000195 : */ 0x50000000,0x0000076c,
+/*
+
+reselected:
+
+
+
+ CLEAR TARGET
+
+at 0x00000197 : */ 0x60000200,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x00000199 : */ 0x78380000,0x00000000,
+/*
+ ; Read all data needed to reestablish the nexus -
+ MOVE 1, reselected_identify, WHEN MSG_IN
+
+at 0x0000019b : */ 0x0f000001,0x00000000,
+/*
+ ; We used to CLEAR ACK here.
+
+
+
+
+
+ ; Point DSA at the current head of the disconnected queue.
+ MOVE dmode_memory_to_ncr TO DMODE
+
+at 0x0000019d : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, reconnect_dsa_head, addr_scratch
+
+at 0x0000019f : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000001a2 : */ 0x78380000,0x00000000,
+/*
+ CALL scratch_to_dsa
+
+at 0x000001a4 : */ 0x88080000,0x00000980,
+/*
+
+ ; Fix the update-next pointer so that the reconnect_dsa_head
+ ; pointer is the one that will be updated if this DSA is a hit
+ ; and we remove it from the queue.
+
+ MOVE MEMORY 4, addr_reconnect_dsa_head, reselected_ok + 8
+
+at 0x000001a6 : */ 0xc0000004,0x00000000,0x00000758,
+/*
+
+ENTRY reselected_check_next
+reselected_check_next:
+
+
+
+ ; Check for a NULL pointer.
+ MOVE DSA0 TO SFBR
+
+at 0x000001a9 : */ 0x72100000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001ab : */ 0x80040000,0x000006ec,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x000001ad : */ 0x72110000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001af : */ 0x80040000,0x000006ec,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x000001b1 : */ 0x72120000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001b3 : */ 0x80040000,0x000006ec,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x000001b5 : */ 0x72130000,0x00000000,
+/*
+ JUMP reselected_not_end, IF NOT 0
+
+at 0x000001b7 : */ 0x80040000,0x000006ec,
+/*
+ INT int_err_unexpected_reselect
+
+at 0x000001b9 : */ 0x98080000,0x00020000,
+/*
+
+reselected_not_end:
+ ;
+ ; XXX the ALU is only eight bits wide, and the assembler
+ ; wont do the dirt work for us. As long as dsa_check_reselect
+ ; is negative, we need to sign extend with 1 bits to the full
+ ; 32 bit width of the address.
+ ;
+ ; A potential work around would be to have a known alignment
+ ; of the DSA structure such that the base address plus
+ ; dsa_check_reselect doesn't require carrying from bytes
+ ; higher than the LSB.
+ ;
+
+ MOVE DSA0 TO SFBR
+
+at 0x000001bb : */ 0x72100000,0x00000000,
+/*
+ MOVE SFBR + dsa_check_reselect TO SCRATCH0
+
+at 0x000001bd : */ 0x6e340000,0x00000000,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x000001bf : */ 0x72110000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH1 WITH CARRY
+
+at 0x000001c1 : */ 0x6f35ff00,0x00000000,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x000001c3 : */ 0x72120000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH2 WITH CARRY
+
+at 0x000001c5 : */ 0x6f36ff00,0x00000000,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x000001c7 : */ 0x72130000,0x00000000,
+/*
+ MOVE SFBR + 0xff TO SCRATCH3 WITH CARRY
+
+at 0x000001c9 : */ 0x6f37ff00,0x00000000,
+/*
+
+ MOVE dmode_ncr_to_memory TO DMODE
+
+at 0x000001cb : */ 0x78380000,0x00000000,
+/*
+ MOVE MEMORY 4, addr_scratch, reselected_check + 4
+
+at 0x000001cd : */ 0xc0000004,0x00000000,0x0000074c,
+/*
+ MOVE dmode_memory_to_memory TO DMODE
+
+at 0x000001d0 : */ 0x78380000,0x00000000,
+/*
+reselected_check:
+ JUMP 0
+
+at 0x000001d2 : */ 0x80080000,0x00000000,
+/*
+
+
+;
+;
+ENTRY reselected_ok
+reselected_ok:
+ MOVE MEMORY 4, 0, 0 ; Patched : first word
+
+at 0x000001d4 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ ; is address of
+ ; successful dsa_next
+ ; Second word is last
+ ; unsuccessful dsa_next,
+ ; starting with
+ ; dsa_reconnect_head
+ ; We used to CLEAR ACK here.
+
+
+
+
+
+
+ RETURN ; Return control to where
+
+at 0x000001d7 : */ 0x90080000,0x00000000,
+/*
+
+
+
+
+selected:
+ INT int_err_selected;
+
+at 0x000001d9 : */ 0x98080000,0x00010000,
+/*
+
+;
+; A select or reselect failure can be caused by one of two conditions :
+; 1. SIG_P was set. This will be the case if the user has written
+; a new value to a previously NULL head of the issue queue.
+;
+; 2. The NCR53c810 was selected or reselected by another device.
+;
+; 3. The bus was allready busy since we were selected or reselected
+; before starting the command.
+
+wait_reselect_failed:
+
+
+
+; Check selected bit.
+ MOVE SIST0 & 0x20 TO SFBR
+
+at 0x000001db : */ 0x74422000,0x00000000,
+/*
+ JUMP selected, IF 0x20
+
+at 0x000001dd : */ 0x800c0020,0x00000764,
+/*
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x000001df : */ 0x741a4000,0x00000000,
+/*
+ JUMP schedule, IF 0x40
+
+at 0x000001e1 : */ 0x800c0040,0x00000000,
+/*
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+ MOVE ISTAT & 0x08 TO SFBR
+
+at 0x000001e3 : */ 0x74140800,0x00000000,
+/*
+ JUMP reselected, IF 0x08
+
+at 0x000001e5 : */ 0x800c0008,0x0000065c,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+
+
+
+ INT int_debug_panic
+
+at 0x000001e7 : */ 0x98080000,0x030b0000,
+/*
+
+
+
+select_failed:
+
+
+
+; Otherwise, mask the selected and reselected bits off SIST0
+ MOVE SIST0 & 0x30 TO SFBR
+
+at 0x000001e9 : */ 0x74423000,0x00000000,
+/*
+ JUMP selected, IF 0x20
+
+at 0x000001eb : */ 0x800c0020,0x00000764,
+/*
+ JUMP reselected, IF 0x10
+
+at 0x000001ed : */ 0x800c0010,0x0000065c,
+/*
+; If SIGP is set, the user just gave us another command, and
+; we should restart or return to the scheduler.
+; Reading CTEST2 clears the SIG_P bit in the ISTAT register.
+ MOVE CTEST2 & 0x40 TO SFBR
+
+at 0x000001ef : */ 0x741a4000,0x00000000,
+/*
+ JUMP select, IF 0x40
+
+at 0x000001f1 : */ 0x800c0040,0x000001fc,
+/*
+; Check connected bit.
+; FIXME: this needs to change if we support target mode
+; FIXME: is this really necessary?
+ MOVE ISTAT & 0x08 TO SFBR
+
+at 0x000001f3 : */ 0x74140800,0x00000000,
+/*
+ JUMP reselected, IF 0x08
+
+at 0x000001f5 : */ 0x800c0008,0x0000065c,
+/*
+; FIXME : Something bogus happened, and we shouldn't fail silently.
+
+
+
+ INT int_debug_panic
+
+at 0x000001f7 : */ 0x98080000,0x030b0000,
+/*
+
+
+;
+; test_1
+; test_2
+;
+; PURPOSE : run some verification tests on the NCR. test_1
+; copies test_src to test_dest and interrupts the host
+; processor, testing for cache coherency and interrupt
+; problems in the processes.
+;
+; test_2 runs a command with offsets relative to the
+; DSA on entry, and is useful for miscellaneous experimentation.
+;
+
+; Verify that interrupts are working correctly and that we don't
+; have a cache invalidation problem.
+
+ABSOLUTE test_src = 0, test_dest = 0
+ENTRY test_1
+test_1:
+ MOVE MEMORY 4, test_src, test_dest
+
+at 0x000001f9 : */ 0xc0000004,0x00000000,0x00000000,
+/*
+ INT int_test_1
+
+at 0x000001fc : */ 0x98080000,0x04000000,
+/*
+
+;
+; Run arbitrary commands, with test code establishing a DSA
+;
+
+ENTRY test_2
+test_2:
+ CLEAR TARGET
+
+at 0x000001fe : */ 0x60000200,0x00000000,
+/*
+ SELECT ATN FROM 0, test_2_fail
+
+at 0x00000200 : */ 0x43000000,0x00000850,
+/*
+ JUMP test_2_msgout, WHEN MSG_OUT
+
+at 0x00000202 : */ 0x860b0000,0x00000810,
+/*
+ENTRY test_2_msgout
+test_2_msgout:
+ MOVE FROM 8, WHEN MSG_OUT
+
+at 0x00000204 : */ 0x1e000000,0x00000008,
+/*
+ MOVE FROM 16, WHEN CMD
+
+at 0x00000206 : */ 0x1a000000,0x00000010,
+/*
+ MOVE FROM 24, WHEN DATA_IN
+
+at 0x00000208 : */ 0x19000000,0x00000018,
+/*
+ MOVE FROM 32, WHEN STATUS
+
+at 0x0000020a : */ 0x1b000000,0x00000020,
+/*
+ MOVE FROM 40, WHEN MSG_IN
+
+at 0x0000020c : */ 0x1f000000,0x00000028,
+/*
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x0000020e : */ 0x7c027f00,0x00000000,
+/*
+ CLEAR ACK
+
+at 0x00000210 : */ 0x60000040,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x00000212 : */ 0x48000000,0x00000000,
+/*
+test_2_fail:
+ INT int_test_2
+
+at 0x00000214 : */ 0x98080000,0x04010000,
+/*
+
+ENTRY debug_break
+debug_break:
+ INT int_debug_break
+
+at 0x00000216 : */ 0x98080000,0x03000000,
+/*
+
+;
+; initiator_abort
+; target_abort
+;
+; PURPOSE : Abort the currently established nexus from with initiator
+; or target mode.
+;
+;
+
+ENTRY target_abort
+target_abort:
+ SET TARGET
+
+at 0x00000218 : */ 0x58000200,0x00000000,
+/*
+ DISCONNECT
+
+at 0x0000021a : */ 0x48000000,0x00000000,
+/*
+ CLEAR TARGET
+
+at 0x0000021c : */ 0x60000200,0x00000000,
+/*
+ JUMP schedule
+
+at 0x0000021e : */ 0x80080000,0x00000000,
+/*
+
+ENTRY initiator_abort
+initiator_abort:
+ SET ATN
+
+at 0x00000220 : */ 0x58000008,0x00000000,
+/*
+;
+; The SCSI-I specification says that targets may go into MSG out at
+; their leisure upon receipt of the ATN single. On all versions of the
+; specification, we can't change phases until REQ transitions true->false,
+; so we need to sink/source one byte of data to allow the transition.
+;
+; For the sake of safety, we'll only source one byte of data in all
+; cases, but to accomodate the SCSI-I dain bramage, we'll sink an
+; arbitrary number of bytes.
+ JUMP spew_cmd, WHEN CMD
+
+at 0x00000222 : */ 0x820b0000,0x000008b8,
+/*
+ JUMP eat_msgin, WHEN MSG_IN
+
+at 0x00000224 : */ 0x870b0000,0x000008c8,
+/*
+ JUMP eat_datain, WHEN DATA_IN
+
+at 0x00000226 : */ 0x810b0000,0x000008f8,
+/*
+ JUMP eat_status, WHEN STATUS
+
+at 0x00000228 : */ 0x830b0000,0x000008e0,
+/*
+ JUMP spew_dataout, WHEN DATA_OUT
+
+at 0x0000022a : */ 0x800b0000,0x00000910,
+/*
+ JUMP sated
+
+at 0x0000022c : */ 0x80080000,0x00000918,
+/*
+spew_cmd:
+ MOVE 1, NCR53c7xx_zero, WHEN CMD
+
+at 0x0000022e : */ 0x0a000001,0x00000000,
+/*
+ JUMP sated
+
+at 0x00000230 : */ 0x80080000,0x00000918,
+/*
+eat_msgin:
+ MOVE 1, NCR53c7xx_sink, WHEN MSG_IN
+
+at 0x00000232 : */ 0x0f000001,0x00000000,
+/*
+ JUMP eat_msgin, WHEN MSG_IN
+
+at 0x00000234 : */ 0x870b0000,0x000008c8,
+/*
+ JUMP sated
+
+at 0x00000236 : */ 0x80080000,0x00000918,
+/*
+eat_status:
+ MOVE 1, NCR53c7xx_sink, WHEN STATUS
+
+at 0x00000238 : */ 0x0b000001,0x00000000,
+/*
+ JUMP eat_status, WHEN STATUS
+
+at 0x0000023a : */ 0x830b0000,0x000008e0,
+/*
+ JUMP sated
+
+at 0x0000023c : */ 0x80080000,0x00000918,
+/*
+eat_datain:
+ MOVE 1, NCR53c7xx_sink, WHEN DATA_IN
+
+at 0x0000023e : */ 0x09000001,0x00000000,
+/*
+ JUMP eat_datain, WHEN DATA_IN
+
+at 0x00000240 : */ 0x810b0000,0x000008f8,
+/*
+ JUMP sated
+
+at 0x00000242 : */ 0x80080000,0x00000918,
+/*
+spew_dataout:
+ MOVE 1, NCR53c7xx_zero, WHEN DATA_OUT
+
+at 0x00000244 : */ 0x08000001,0x00000000,
+/*
+sated:
+ MOVE SCNTL2 & 0x7f TO SCNTL2
+
+at 0x00000246 : */ 0x7c027f00,0x00000000,
+/*
+ MOVE 1, NCR53c7xx_msg_abort, WHEN MSG_OUT
+
+at 0x00000248 : */ 0x0e000001,0x00000000,
+/*
+ WAIT DISCONNECT
+
+at 0x0000024a : */ 0x48000000,0x00000000,
+/*
+ INT int_norm_aborted
+
+at 0x0000024c : */ 0x98080000,0x02040000,
+/*
+
+;
+; dsa_to_scratch
+; scratch_to_dsa
+;
+; PURPOSE :
+; The NCR chips cannot do a move memory instruction with the DSA register
+; as the source or destination. So, we provide a couple of subroutines
+; that let us switch between the DSA register and scratch register.
+;
+; Memory moves to/from the DSPS register also don't work, but we
+; don't use them.
+;
+;
+
+
+dsa_to_scratch:
+ MOVE DSA0 TO SFBR
+
+at 0x0000024e : */ 0x72100000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH0
+
+at 0x00000250 : */ 0x6a340000,0x00000000,
+/*
+ MOVE DSA1 TO SFBR
+
+at 0x00000252 : */ 0x72110000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH1
+
+at 0x00000254 : */ 0x6a350000,0x00000000,
+/*
+ MOVE DSA2 TO SFBR
+
+at 0x00000256 : */ 0x72120000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH2
+
+at 0x00000258 : */ 0x6a360000,0x00000000,
+/*
+ MOVE DSA3 TO SFBR
+
+at 0x0000025a : */ 0x72130000,0x00000000,
+/*
+ MOVE SFBR TO SCRATCH3
+
+at 0x0000025c : */ 0x6a370000,0x00000000,
+/*
+ RETURN
+
+at 0x0000025e : */ 0x90080000,0x00000000,
+/*
+
+scratch_to_dsa:
+ MOVE SCRATCH0 TO SFBR
+
+at 0x00000260 : */ 0x72340000,0x00000000,
+/*
+ MOVE SFBR TO DSA0
+
+at 0x00000262 : */ 0x6a100000,0x00000000,
+/*
+ MOVE SCRATCH1 TO SFBR
+
+at 0x00000264 : */ 0x72350000,0x00000000,
+/*
+ MOVE SFBR TO DSA1
+
+at 0x00000266 : */ 0x6a110000,0x00000000,
+/*
+ MOVE SCRATCH2 TO SFBR
+
+at 0x00000268 : */ 0x72360000,0x00000000,
+/*
+ MOVE SFBR TO DSA2
+
+at 0x0000026a : */ 0x6a120000,0x00000000,
+/*
+ MOVE SCRATCH3 TO SFBR
+
+at 0x0000026c : */ 0x72370000,0x00000000,
+/*
+ MOVE SFBR TO DSA3
+
+at 0x0000026e : */ 0x6a130000,0x00000000,
+/*
+ RETURN
+
+at 0x00000270 : */ 0x90080000,0x00000000,
+};
+
+#define A_NCR53c7xx_msg_abort 0x00000000
+u32 A_NCR53c7xx_msg_abort_used[] = {
+ 0x00000249,
+};
+
+#define A_NCR53c7xx_msg_reject 0x00000000
+u32 A_NCR53c7xx_msg_reject_used[] = {
+ 0x00000172,
+};
+
+#define A_NCR53c7xx_sink 0x00000000
+u32 A_NCR53c7xx_sink_used[] = {
+ 0x00000233,
+ 0x00000239,
+ 0x0000023f,
+};
+
+#define A_NCR53c7xx_zero 0x00000000
+u32 A_NCR53c7xx_zero_used[] = {
+ 0x0000022f,
+ 0x00000245,
+};
+
+#define A_NOP_insn 0x00000000
+u32 A_NOP_insn_used[] = {
+ 0x00000010,
+};
+
+#define A_addr_reconnect_dsa_head 0x00000000
+u32 A_addr_reconnect_dsa_head_used[] = {
+ 0x000001a7,
+};
+
+#define A_addr_scratch 0x00000000
+u32 A_addr_scratch_used[] = {
+ 0x00000004,
+ 0x0000001b,
+ 0x00000046,
+ 0x00000067,
+ 0x00000073,
+ 0x000000b0,
+ 0x000000c6,
+ 0x00000128,
+ 0x00000141,
+ 0x000001a1,
+ 0x000001ce,
+};
+
+#define A_addr_temp 0x00000000
+u32 A_addr_temp_used[] = {
+ 0x00000025,
+ 0x00000034,
+};
+
+#define A_dmode_memory_to_memory 0x00000000
+u32 A_dmode_memory_to_memory_used[] = {
+ 0x00000005,
+ 0x0000001c,
+ 0x00000027,
+ 0x00000035,
+ 0x00000047,
+ 0x00000069,
+ 0x00000075,
+ 0x000000b2,
+ 0x000000c8,
+ 0x0000012a,
+ 0x00000143,
+ 0x00000199,
+ 0x000001a2,
+ 0x000001d0,
+};
+
+#define A_dmode_memory_to_ncr 0x00000000
+u32 A_dmode_memory_to_ncr_used[] = {
+ 0x00000000,
+ 0x00000017,
+ 0x00000030,
+ 0x00000042,
+ 0x0000019d,
+};
+
+#define A_dmode_ncr_to_memory 0x00000000
+u32 A_dmode_ncr_to_memory_used[] = {
+ 0x00000022,
+ 0x00000064,
+ 0x00000070,
+ 0x000000ad,
+ 0x000000c3,
+ 0x00000125,
+ 0x0000013e,
+ 0x000001cb,
+};
+
+#define A_dsa_check_reselect 0x00000000
+u32 A_dsa_check_reselect_used[] = {
+ 0x000001bd,
+};
+
+#define A_dsa_cmdout 0x00000048
+u32 A_dsa_cmdout_used[] = {
+ 0x00000094,
+};
+
+#define A_dsa_cmnd 0x00000038
+u32 A_dsa_cmnd_used[] = {
+};
+
+#define A_dsa_datain 0x00000054
+u32 A_dsa_datain_used[] = {
+ 0x000000bb,
+};
+
+#define A_dsa_dataout 0x00000050
+u32 A_dsa_dataout_used[] = {
+ 0x000000a5,
+};
+
+#define A_dsa_end 0x00000070
+u32 A_dsa_end_used[] = {
+};
+
+#define A_dsa_fields_start 0x00000000
+u32 A_dsa_fields_start_used[] = {
+};
+
+#define A_dsa_msgin 0x00000058
+u32 A_dsa_msgin_used[] = {
+ 0x00000188,
+};
+
+#define A_dsa_msgout 0x00000040
+u32 A_dsa_msgout_used[] = {
+ 0x00000086,
+};
+
+#define A_dsa_msgout_other 0x00000068
+u32 A_dsa_msgout_other_used[] = {
+ 0x00000180,
+};
+
+#define A_dsa_next 0x00000030
+u32 A_dsa_next_used[] = {
+ 0x0000005c,
+};
+
+#define A_dsa_restore_pointers 0x00000000
+u32 A_dsa_restore_pointers_used[] = {
+ 0x0000012e,
+};
+
+#define A_dsa_save_data_pointer 0x00000000
+u32 A_dsa_save_data_pointer_used[] = {
+ 0x00000115,
+};
+
+#define A_dsa_select 0x0000003c
+u32 A_dsa_select_used[] = {
+ 0x00000081,
+};
+
+#define A_dsa_status 0x00000060
+u32 A_dsa_status_used[] = {
+ 0x00000184,
+};
+
+#define A_dsa_temp_addr_array_value 0x00000000
+u32 A_dsa_temp_addr_array_value_used[] = {
+};
+
+#define A_dsa_temp_addr_dsa_value 0x00000000
+u32 A_dsa_temp_addr_dsa_value_used[] = {
+ 0x00000003,
+};
+
+#define A_dsa_temp_addr_new_value 0x00000000
+u32 A_dsa_temp_addr_new_value_used[] = {
+};
+
+#define A_dsa_temp_addr_next 0x00000000
+u32 A_dsa_temp_addr_next_used[] = {
+ 0x00000015,
+ 0x0000004e,
+};
+
+#define A_dsa_temp_addr_residual 0x00000000
+u32 A_dsa_temp_addr_residual_used[] = {
+ 0x0000002a,
+ 0x00000039,
+};
+
+#define A_dsa_temp_addr_saved_pointer 0x00000000
+u32 A_dsa_temp_addr_saved_pointer_used[] = {
+ 0x00000026,
+ 0x00000033,
+};
+
+#define A_dsa_temp_addr_saved_residual 0x00000000
+u32 A_dsa_temp_addr_saved_residual_used[] = {
+ 0x0000002b,
+ 0x00000038,
+};
+
+#define A_dsa_temp_lun 0x00000000
+u32 A_dsa_temp_lun_used[] = {
+ 0x0000004b,
+};
+
+#define A_dsa_temp_next 0x00000000
+u32 A_dsa_temp_next_used[] = {
+ 0x0000001a,
+};
+
+#define A_dsa_temp_sync 0x00000000
+u32 A_dsa_temp_sync_used[] = {
+ 0x00000053,
+};
+
+#define A_dsa_temp_target 0x00000000
+u32 A_dsa_temp_target_used[] = {
+ 0x00000040,
+};
+
+#define A_int_debug_break 0x03000000
+u32 A_int_debug_break_used[] = {
+ 0x00000217,
+};
+
+#define A_int_debug_panic 0x030b0000
+u32 A_int_debug_panic_used[] = {
+ 0x000001e8,
+ 0x000001f8,
+};
+
+#define A_int_err_check_condition 0x00030000
+u32 A_int_err_check_condition_used[] = {
+ 0x00000194,
+};
+
+#define A_int_err_no_phase 0x00040000
+u32 A_int_err_no_phase_used[] = {
+};
+
+#define A_int_err_selected 0x00010000
+u32 A_int_err_selected_used[] = {
+ 0x000001da,
+};
+
+#define A_int_err_unexpected_phase 0x00000000
+u32 A_int_err_unexpected_phase_used[] = {
+ 0x0000008c,
+ 0x00000092,
+ 0x0000009a,
+ 0x000000d0,
+ 0x000000d4,
+ 0x000000d6,
+ 0x000000de,
+ 0x000000e2,
+ 0x000000e4,
+ 0x000000ec,
+ 0x000000f0,
+ 0x000000f2,
+ 0x000000f4,
+ 0x0000014c,
+};
+
+#define A_int_err_unexpected_reselect 0x00020000
+u32 A_int_err_unexpected_reselect_used[] = {
+ 0x000001ba,
+};
+
+#define A_int_msg_1 0x01020000
+u32 A_int_msg_1_used[] = {
+ 0x0000010e,
+ 0x00000110,
+};
+
+#define A_int_msg_sdtr 0x01010000
+u32 A_int_msg_sdtr_used[] = {
+ 0x0000016c,
+};
+
+#define A_int_msg_wdtr 0x01000000
+u32 A_int_msg_wdtr_used[] = {
+ 0x00000160,
+};
+
+#define A_int_norm_aborted 0x02040000
+u32 A_int_norm_aborted_used[] = {
+ 0x0000024d,
+};
+
+#define A_int_norm_command_complete 0x02020000
+u32 A_int_norm_command_complete_used[] = {
+};
+
+#define A_int_norm_disconnected 0x02030000
+u32 A_int_norm_disconnected_used[] = {
+};
+
+#define A_int_norm_reselect_complete 0x02010000
+u32 A_int_norm_reselect_complete_used[] = {
+};
+
+#define A_int_norm_reset 0x02050000
+u32 A_int_norm_reset_used[] = {
+};
+
+#define A_int_norm_select_complete 0x02000000
+u32 A_int_norm_select_complete_used[] = {
+};
+
+#define A_int_test_1 0x04000000
+u32 A_int_test_1_used[] = {
+ 0x000001fd,
+};
+
+#define A_int_test_2 0x04010000
+u32 A_int_test_2_used[] = {
+ 0x00000215,
+};
+
+#define A_int_test_3 0x04020000
+u32 A_int_test_3_used[] = {
+};
+
+#define A_msg_buf 0x00000000
+u32 A_msg_buf_used[] = {
+ 0x00000102,
+ 0x0000014e,
+ 0x00000158,
+ 0x0000015e,
+ 0x00000164,
+ 0x0000016a,
+};
+
+#define A_reconnect_dsa_head 0x00000000
+u32 A_reconnect_dsa_head_used[] = {
+ 0x0000006c,
+ 0x00000074,
+ 0x000001a0,
+};
+
+#define A_reselected_identify 0x00000000
+u32 A_reselected_identify_used[] = {
+ 0x00000045,
+ 0x0000019c,
+};
+
+#define A_reselected_tag 0x00000000
+u32 A_reselected_tag_used[] = {
+};
+
+#define A_schedule 0x00000000
+u32 A_schedule_used[] = {
+ 0x0000007e,
+ 0x00000192,
+ 0x000001e2,
+ 0x0000021f,
+};
+
+#define A_test_dest 0x00000000
+u32 A_test_dest_used[] = {
+ 0x000001fb,
+};
+
+#define A_test_src 0x00000000
+u32 A_test_src_used[] = {
+ 0x000001fa,
+};
+
+#define Ent_accept_message 0x000005d4
+#define Ent_cmdout_cmdout 0x0000024c
+#define Ent_command_complete 0x0000060c
+#define Ent_command_complete_msgin 0x0000061c
+#define Ent_data_transfer 0x00000254
+#define Ent_datain_to_jump 0x00000328
+#define Ent_debug_break 0x00000858
+#define Ent_dsa_code_begin 0x00000000
+#define Ent_dsa_code_check_reselect 0x000000f8
+#define Ent_dsa_code_fix_jump 0x0000003c
+#define Ent_dsa_code_restore_pointers 0x000000c0
+#define Ent_dsa_code_save_data_pointer 0x00000088
+#define Ent_dsa_code_template 0x00000000
+#define Ent_dsa_code_template_end 0x00000168
+#define Ent_dsa_schedule 0x00000168
+#define Ent_dsa_zero 0x00000168
+#define Ent_end_data_transfer 0x0000028c
+#define Ent_initiator_abort 0x00000880
+#define Ent_msg_in 0x00000404
+#define Ent_msg_in_restart 0x000003e4
+#define Ent_other_in 0x00000374
+#define Ent_other_out 0x0000033c
+#define Ent_other_transfer 0x000003ac
+#define Ent_reject_message 0x000005b4
+#define Ent_reselected_check_next 0x000006a4
+#define Ent_reselected_ok 0x00000750
+#define Ent_respond_message 0x000005ec
+#define Ent_select 0x000001fc
+#define Ent_select_msgout 0x00000214
+#define Ent_target_abort 0x00000860
+#define Ent_test_1 0x000007e4
+#define Ent_test_2 0x000007f8
+#define Ent_test_2_msgout 0x00000810
+#define Ent_wait_reselect 0x00000654
+u32 LABELPATCHES[] = {
+ 0x00000008,
+ 0x0000000a,
+ 0x00000013,
+ 0x00000016,
+ 0x0000001f,
+ 0x00000021,
+ 0x0000004f,
+ 0x00000051,
+ 0x0000005b,
+ 0x00000068,
+ 0x0000006f,
+ 0x00000082,
+ 0x00000084,
+ 0x0000008a,
+ 0x0000008e,
+ 0x00000090,
+ 0x00000096,
+ 0x00000098,
+ 0x0000009c,
+ 0x0000009e,
+ 0x000000a0,
+ 0x000000a2,
+ 0x000000a4,
+ 0x000000b1,
+ 0x000000b6,
+ 0x000000ba,
+ 0x000000c7,
+ 0x000000cc,
+ 0x000000d2,
+ 0x000000d8,
+ 0x000000da,
+ 0x000000e0,
+ 0x000000e6,
+ 0x000000e8,
+ 0x000000ee,
+ 0x000000f6,
+ 0x000000f8,
+ 0x00000104,
+ 0x00000106,
+ 0x00000108,
+ 0x0000010a,
+ 0x0000010c,
+ 0x00000112,
+ 0x00000114,
+ 0x00000129,
+ 0x00000142,
+ 0x00000148,
+ 0x00000150,
+ 0x00000152,
+ 0x00000154,
+ 0x0000015a,
+ 0x00000166,
+ 0x00000196,
+ 0x000001a5,
+ 0x000001a8,
+ 0x000001ac,
+ 0x000001b0,
+ 0x000001b4,
+ 0x000001b8,
+ 0x000001cf,
+ 0x000001de,
+ 0x000001e6,
+ 0x000001ec,
+ 0x000001ee,
+ 0x000001f2,
+ 0x000001f6,
+ 0x00000201,
+ 0x00000203,
+ 0x00000223,
+ 0x00000225,
+ 0x00000227,
+ 0x00000229,
+ 0x0000022b,
+ 0x0000022d,
+ 0x00000231,
+ 0x00000235,
+ 0x00000237,
+ 0x0000023b,
+ 0x0000023d,
+ 0x00000241,
+ 0x00000243,
+};
+
+struct {
+ u32 offset;
+ void *address;
+} EXTERNAL_PATCHES[] = {
+};
+
+u32 INSTRUCTIONS = 301;
+u32 PATCHES = 81;
+u32 EXTERNAL_PATCHES_LEN = 0;
diff --git a/i386/i386at/gpl/linux/scsi/53c8xx_u.h b/i386/i386at/gpl/linux/scsi/53c8xx_u.h
new file mode 100644
index 00000000..c3d486fe
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/53c8xx_u.h
@@ -0,0 +1,97 @@
+#undef A_NCR53c7xx_msg_abort
+#undef A_NCR53c7xx_msg_reject
+#undef A_NCR53c7xx_sink
+#undef A_NCR53c7xx_zero
+#undef A_NOP_insn
+#undef A_addr_reconnect_dsa_head
+#undef A_addr_scratch
+#undef A_addr_temp
+#undef A_dmode_memory_to_memory
+#undef A_dmode_memory_to_ncr
+#undef A_dmode_ncr_to_memory
+#undef A_dsa_check_reselect
+#undef A_dsa_cmdout
+#undef A_dsa_cmnd
+#undef A_dsa_datain
+#undef A_dsa_dataout
+#undef A_dsa_end
+#undef A_dsa_fields_start
+#undef A_dsa_msgin
+#undef A_dsa_msgout
+#undef A_dsa_msgout_other
+#undef A_dsa_next
+#undef A_dsa_restore_pointers
+#undef A_dsa_save_data_pointer
+#undef A_dsa_select
+#undef A_dsa_status
+#undef A_dsa_temp_addr_array_value
+#undef A_dsa_temp_addr_dsa_value
+#undef A_dsa_temp_addr_new_value
+#undef A_dsa_temp_addr_next
+#undef A_dsa_temp_addr_residual
+#undef A_dsa_temp_addr_saved_pointer
+#undef A_dsa_temp_addr_saved_residual
+#undef A_dsa_temp_lun
+#undef A_dsa_temp_next
+#undef A_dsa_temp_sync
+#undef A_dsa_temp_target
+#undef A_int_debug_break
+#undef A_int_debug_panic
+#undef A_int_err_check_condition
+#undef A_int_err_no_phase
+#undef A_int_err_selected
+#undef A_int_err_unexpected_phase
+#undef A_int_err_unexpected_reselect
+#undef A_int_msg_1
+#undef A_int_msg_sdtr
+#undef A_int_msg_wdtr
+#undef A_int_norm_aborted
+#undef A_int_norm_command_complete
+#undef A_int_norm_disconnected
+#undef A_int_norm_reselect_complete
+#undef A_int_norm_reset
+#undef A_int_norm_select_complete
+#undef A_int_test_1
+#undef A_int_test_2
+#undef A_int_test_3
+#undef A_msg_buf
+#undef A_reconnect_dsa_head
+#undef A_reselected_identify
+#undef A_reselected_tag
+#undef A_schedule
+#undef A_test_dest
+#undef A_test_src
+#undef Ent_accept_message
+#undef Ent_cmdout_cmdout
+#undef Ent_command_complete
+#undef Ent_command_complete_msgin
+#undef Ent_data_transfer
+#undef Ent_datain_to_jump
+#undef Ent_debug_break
+#undef Ent_dsa_code_begin
+#undef Ent_dsa_code_check_reselect
+#undef Ent_dsa_code_fix_jump
+#undef Ent_dsa_code_restore_pointers
+#undef Ent_dsa_code_save_data_pointer
+#undef Ent_dsa_code_template
+#undef Ent_dsa_code_template_end
+#undef Ent_dsa_schedule
+#undef Ent_dsa_zero
+#undef Ent_end_data_transfer
+#undef Ent_initiator_abort
+#undef Ent_msg_in
+#undef Ent_msg_in_restart
+#undef Ent_other_in
+#undef Ent_other_out
+#undef Ent_other_transfer
+#undef Ent_reject_message
+#undef Ent_reselected_check_next
+#undef Ent_reselected_ok
+#undef Ent_respond_message
+#undef Ent_select
+#undef Ent_select_msgout
+#undef Ent_target_abort
+#undef Ent_test_1
+#undef Ent_test_2
+#undef Ent_test_2_msgout
+#undef Ent_wait_reselect
diff --git a/i386/i386at/gpl/linux/scsi/AM53C974.c b/i386/i386at/gpl/linux/scsi/AM53C974.c
new file mode 100644
index 00000000..5d092c9a
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/AM53C974.c
@@ -0,0 +1,2249 @@
+#include <linux/config.h>
+#include <linux/delay.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/string.h>
+#include <linux/blk.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+
+#include "scsi.h"
+#include "hosts.h"
+#include "AM53C974.h"
+#include "constants.h"
+#include "sd.h"
+
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ * For the architecture and all pieces of code which can also be found
+ * in the NCR5380 device driver:
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * The AM53C974_nobios_detect code was origininally developed by
+ * Robin Cutshaw (robin@xfree86.org) and is used here in a
+ * slightly modified form.
+ *
+ * For the remaining code:
+ * Copyright 1994, D. Frieauff
+ * EMail: fri@rsx42sun0.dofn.de
+ * Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.c,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:58 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:05 goel
+ * Linux driver merge.
+ *
+ */
+
+#ifdef AM53C974_DEBUG
+ #define DEB(x) x
+ #ifdef AM53C974_DEBUG_KEYWAIT
+ #define KEYWAIT() AM53C974_keywait()
+ #else
+ #define KEYWAIT()
+ #endif
+ #ifdef AM53C974_DEBUG_INIT
+ #define DEB_INIT(x) x
+ #else
+ #define DEB_INIT(x)
+ #endif
+ #ifdef AM53C974_DEBUG_MSG
+ #define DEB_MSG(x) x
+ #else
+ #define DEB_MSG(x)
+ #endif
+ #ifdef AM53C974_DEB_RESEL
+ #define DEB_RESEL(x) x
+ #else
+ #define DEB_RESEL(x)
+ #endif
+ #ifdef AM53C974_DEBUG_QUEUE
+ #define DEB_QUEUE(x) x
+ #define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+ #define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+ #else
+ #define DEB_QUEUE(x)
+ #define LIST(x,y)
+ #define REMOVE(w,x,y,z)
+ #endif
+ #ifdef AM53C974_DEBUG_INFO
+ #define DEB_INFO(x) x
+ #else
+ #define DEB_INFO(x)
+ #endif
+ #ifdef AM53C974_DEBUG_LINKED
+ #define DEB_LINKED(x) x
+ #else
+ #define DEB_LINKED(x)
+ #endif
+ #ifdef AM53C974_DEBUG_INTR
+ #define DEB_INTR(x) x
+ #else
+ #define DEB_INTR(x)
+ #endif
+#else
+ #define DEB_INIT(x)
+ #define DEB(x)
+ #define DEB_QUEUE(x)
+ #define LIST(x,y)
+ #define REMOVE(w,x,y,z)
+ #define DEB_INFO(x)
+ #define DEB_LINKED(x)
+ #define DEB_INTR(x)
+ #define DEB_MSG(x)
+ #define DEB_RESEL(x)
+ #define KEYWAIT()
+#endif
+ #ifdef AM53C974_DEBUG_ABORT
+ #define DEB_ABORT(x) x
+ #else
+ #define DEB_ABORT(x)
+ #endif
+
+#ifdef VERBOSE_AM53C974_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define INSIDE(x,l,h) ( ((x) >= (l)) && ((x) <= (h)) )
+
+#ifdef AM53C974_DEBUG
+static void AM53C974_print_pci(struct Scsi_Host *instance);
+static void AM53C974_print_phase(struct Scsi_Host *instance);
+static void AM53C974_print_queues(struct Scsi_Host *instance);
+#endif /* AM53C974_DEBUG */
+static void AM53C974_print(struct Scsi_Host *instance);
+static void AM53C974_keywait(void);
+static int AM53C974_bios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_nobios_detect(Scsi_Host_Template *tpnt);
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config);
+static void AM53C974_config_after_reset(struct Scsi_Host *instance);
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd);
+static __inline__ void run_main(void);
+static void AM53C974_main (void);
+static void AM53C974_intr(int irq, struct pt_regs *regs);
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance);
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg);
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target);
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target);
+static void AM53C974_information_transfer(struct Scsi_Host *instance,
+ unsigned char statreg, unsigned char isreg,
+ unsigned char instreg, unsigned char cfifo,
+ unsigned char dmastatus);
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd, unsigned char msg);
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg);
+static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+ unsigned long length, char *data);
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+ unsigned char statreg);
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance);
+
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+static struct Scsi_Host *first_host = NULL; /* Head of list of AMD boards */
+static volatile int main_running = 0;
+static int commandline_current = 0;
+override_t overrides[7] = { {-1, 0, 0, 0}, }; /* LILO overrides */
+
+#ifdef AM53C974_DEBUG
+static int deb_stop = 1;
+
+/**************************************************************************
+ * Function : void AM53C974_print_pci(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the PCI registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_pci(struct Scsi_Host *instance)
+{
+int i;
+unsigned short vendor_id, device_id, command, status, scratch[8];
+unsigned long class_revision, base;
+unsigned char irq, cache_line_size, latency_timer, header_type;
+
+AM53C974_PCIREG_OPEN();
+
+for (i = 0; i < 8; i++) *(scratch + i) = AM53C974_PCIREG_READ_WORD(instance, PCI_SCRATCH_REG_0 + 2*i);
+vendor_id = AM53C974_PCIREG_READ_WORD(instance, PCI_VENDOR_ID);
+device_id = AM53C974_PCIREG_READ_WORD(instance, PCI_DEVICE_ID);
+command = AM53C974_PCIREG_READ_WORD(instance, PCI_COMMAND);
+status = AM53C974_PCIREG_READ_WORD(instance, PCI_STATUS);
+class_revision = AM53C974_PCIREG_READ_DWORD(instance, PCI_CLASS_REVISION);
+cache_line_size = AM53C974_PCIREG_READ_BYTE(instance, PCI_CACHE_LINE_SIZE);
+latency_timer = AM53C974_PCIREG_READ_BYTE(instance, PCI_LATENCY_TIMER);
+header_type = AM53C974_PCIREG_READ_BYTE(instance, PCI_HEADER_TYPE);
+base = AM53C974_PCIREG_READ_DWORD(instance, PCI_BASE_ADDRESS_0);
+irq = AM53C974_PCIREG_READ_BYTE(instance, PCI_INTERRUPT_LINE);
+
+AM53C974_PCIREG_CLOSE();
+
+
+printk("------------- start of PCI register dump -------------\n");
+printk("PCI_VENDOR_ID: 0x%x\n", vendor_id);
+printk("PCI_DEVICE_ID: 0x%x\n", device_id);
+printk("PCI_COMMAND: 0x%x\n", command);
+printk("PCI_STATUS: 0x%x\n", status);
+printk("PCI_CLASS_REVISION: 0x%lx\n", class_revision);
+printk("PCI_CACHE_LINE_SIZE: 0x%x\n", cache_line_size);
+printk("PCI_LATENCY_TIMER: 0x%x\n", latency_timer);
+printk("PCI_HEADER_TYPE: 0x%x\n", header_type);
+printk("PCI_BASE_ADDRESS_0: 0x%lx\n", base);
+printk("PCI_INTERRUPT_LINE: %d\n", irq);
+for (i = 0; i < 8; i++) printk("PCI_SCRATCH_%d: 0x%x\n", i, scratch[i]);
+printk("------------- end of PCI register dump -------------\n\n");
+}
+
+static struct {
+ unsigned char value;
+ char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_RES_0, "RESERVED 0"}, {PHASE_RES_1, "RESERVED 1"}};
+
+/**************************************************************************
+ * Function : void AM53C974_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_phase(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char statreg, latched;
+int i;
+AM53C974_setio(instance);
+
+latched = (AM53C974_read_8(CNTLREG2)) & CNTLREG2_ENF;
+statreg = AM53C974_read_8(STATREG);
+for (i = 0; (phases[i].value != PHASE_RES_1) &&
+ (phases[i].value != (statreg & STATREG_PHASE)); ++i);
+if (latched)
+ printk("scsi%d : phase %s, latched at end of last command\n", instance->host_no, phases[i].name);
+ else
+ printk("scsi%d : phase %s, real time\n", instance->host_no, phases[i].name);
+}
+
+/**************************************************************************
+ * Function : void AM53C974_print_queues(struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues
+ *
+ * Inputs : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print_queues(struct Scsi_Host *instance)
+{
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *ptr;
+
+printk("AM53C974: coroutine is%s running.\n", main_running ? "" : "n't");
+
+cli();
+
+if (!hostdata->connected) {
+ printk ("scsi%d: no currently connected command\n", instance->host_no); }
+ else {
+ print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->connected); }
+if (!hostdata->sel_cmd) {
+ printk ("scsi%d: no currently arbitrating command\n", instance->host_no); }
+ else {
+ print_Scsi_Cmnd ((Scsi_Cmnd *)hostdata->sel_cmd); }
+
+printk ("scsi%d: issue_queue ", instance->host_no);
+if (!hostdata->issue_queue)
+ printk("empty\n");
+ else {
+ printk(":\n");
+ for (ptr = (Scsi_Cmnd *)hostdata->issue_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble)
+ print_Scsi_Cmnd (ptr); }
+
+printk ("scsi%d: disconnected_queue ", instance->host_no);
+if (!hostdata->disconnected_queue)
+ printk("empty\n");
+ else {
+ printk(":\n");
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr; ptr = (Scsi_Cmnd *)ptr->host_scribble)
+ print_Scsi_Cmnd (ptr); }
+
+sti();
+}
+
+#endif /* AM53C974_DEBUG */
+
+/**************************************************************************
+ * Function : void AM53C974_print(struct Scsi_Host *instance)
+ *
+ * Purpose : dump the chip registers for debugging purposes
+ *
+ * Input : instance - which AM53C974
+ **************************************************************************/
+static void AM53C974_print(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned long ctcreg, dmastc, dmaspa, dmawbc, dmawac;
+unsigned char cmdreg, statreg, isreg, cfireg, cntlreg[4], dmacmd, dmastatus;
+AM53C974_setio(instance);
+
+cli();
+ctcreg = AM53C974_read_8(CTCHREG) << 16;
+ctcreg |= AM53C974_read_8(CTCMREG) << 8;
+ctcreg |= AM53C974_read_8(CTCLREG);
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+cfireg = AM53C974_read_8(CFIREG);
+cntlreg[0] = AM53C974_read_8(CNTLREG1);
+cntlreg[1] = AM53C974_read_8(CNTLREG2);
+cntlreg[2] = AM53C974_read_8(CNTLREG3);
+cntlreg[3] = AM53C974_read_8(CNTLREG4);
+dmacmd = AM53C974_read_8(DMACMD);
+dmastc = AM53C974_read_32(DMASTC);
+dmaspa = AM53C974_read_32(DMASPA);
+dmawbc = AM53C974_read_32(DMAWBC);
+dmawac = AM53C974_read_32(DMAWAC);
+dmastatus = AM53C974_read_8(DMASTATUS);
+sti();
+
+printk("AM53C974 register dump:\n");
+printk("IO base: 0x%04lx; CTCREG: 0x%04lx; CMDREG: 0x%02x; STATREG: 0x%02x; ISREG: 0x%02x\n",
+ io_port, ctcreg, cmdreg, statreg, isreg);
+printk("CFIREG: 0x%02x; CNTLREG1-4: 0x%02x; 0x%02x; 0x%02x; 0x%02x\n",
+ cfireg, cntlreg[0], cntlreg[1], cntlreg[2], cntlreg[3]);
+printk("DMACMD: 0x%02x; DMASTC: 0x%04lx; DMASPA: 0x%04lx\n", dmacmd, dmastc, dmaspa);
+printk("DMAWBC: 0x%04lx; DMAWAC: 0x%04lx; DMASTATUS: 0x%02x\n", dmawbc, dmawac, dmastatus);
+printk("---------------------------------------------------------\n");
+}
+
+/**************************************************************************
+* Function : void AM53C974_keywait(void)
+*
+* Purpose : wait until a key is pressed, if it was the 'r' key leave singlestep mode;
+* this function is used for debugging only
+*
+* Input : none
+**************************************************************************/
+static void AM53C974_keywait(void)
+{
+#ifdef AM53C974_DEBUG
+int key;
+
+if (!deb_stop) return;
+#endif
+
+cli();
+while ((inb_p(0x64) & 0x01) != 0x01) ;
+#ifdef AM53C974_DEBUG
+key = inb(0x60);
+if (key == 0x93) deb_stop = 0; /* don't stop if 'r' was pressed */
+#endif
+sti();
+}
+
+/**************************************************************************
+* Function : AM53C974_setup(char *str, int *ints)
+*
+* Purpose : LILO command line initialization of the overrides array,
+*
+* Inputs : str - unused, ints - array of integer parameters with ints[0]
+* equal to the number of ints.
+*
+* NOTE : this function needs to be declared as an external function
+* in init/main.c and included there in the bootsetups list
+***************************************************************************/
+void AM53C974_setup(char *str, int *ints)
+{
+if (ints[0] < 4)
+ printk("AM53C974_setup: wrong number of parameters;\n correct syntax is: AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset\n");
+ else {
+ if (commandline_current < (sizeof(overrides) / sizeof(override_t))) {
+ if ((ints[1] < 0) || (ints[1] > 7) ||
+ (ints[2] < 0) || (ints[2] > 7) ||
+ (ints[1] == ints[2]) ||
+ (ints[3] < (DEF_CLK / MAX_PERIOD)) || (ints[3] > (DEF_CLK / MIN_PERIOD)) ||
+ (ints[4] < 0) || (ints[4] > MAX_OFFSET))
+ printk("AM53C974_setup: illegal parameter\n");
+ else {
+ overrides[commandline_current].host_scsi_id = ints[1];
+ overrides[commandline_current].target_scsi_id = ints[2];
+ overrides[commandline_current].max_rate = ints[3];
+ overrides[commandline_current].max_offset = ints[4];
+ commandline_current++; }
+ }
+ else
+ printk("AM53C974_setup: too many overrides\n");
+ }
+}
+
+#if defined (CONFIG_PCI)
+/**************************************************************************
+* Function : int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips with PCI Bios
+*
+* Inputs : tpnt - host template
+*
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_bios_detect(Scsi_Host_Template *tpnt)
+{
+int count = 0; /* number of boards detected */
+int pci_index;
+pci_config_t pci_config;
+
+for (pci_index = 0; pci_index <= 16; ++pci_index) {
+ unsigned char pci_bus, pci_device_fn;
+ if (pcibios_find_device(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_SCSI, pci_index, &pci_bus, &pci_device_fn) != 0)
+ break;
+
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_VENDOR_ID, &pci_config._vendor);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_DEVICE_ID, &pci_config._device);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_COMMAND, &pci_config._command);
+ pcibios_read_config_word(pci_bus, pci_device_fn, PCI_STATUS, &pci_config._status);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_CLASS_REVISION, &pci_config._class_revision);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_CACHE_LINE_SIZE, &pci_config._cache_line_size);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_LATENCY_TIMER, &pci_config._latency_timer);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_HEADER_TYPE, &pci_config._header_type);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_BIST, &pci_config._bist);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_0, &pci_config._base0);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_1, &pci_config._base1);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_2, &pci_config._base2);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_3, &pci_config._base3);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_4, &pci_config._base4);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_BASE_ADDRESS_5, &pci_config._base5);
+ pcibios_read_config_dword(pci_bus, pci_device_fn, PCI_ROM_ADDRESS, &pci_config._baserom);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_LINE, &pci_config._int_line);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_INTERRUPT_PIN, &pci_config._int_pin);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MIN_GNT, &pci_config._min_gnt);
+ pcibios_read_config_byte(pci_bus, pci_device_fn, PCI_MAX_LAT, &pci_config._max_lat);
+ pci_config._pcibus = 0xFFFFFFFF;
+ pci_config._cardnum = 0xFFFFFFFF;
+
+ /* check whether device is I/O mapped -- should be */
+ if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+ /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+ to set the PCI Master Enable Bit if needed.
+ (from Mark Stockton <marks@schooner.sys.hou.compaq.com>) */
+ if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+ pci_config._command |= PCI_COMMAND_MASTER;
+ printk("PCI Master Bit has not been set. Setting...\n");
+ pcibios_write_config_word(pci_bus, pci_device_fn, PCI_COMMAND, pci_config._command); }
+
+ /* everything seems OK now, so initialize */
+ if (AM53C974_init(tpnt, pci_config)) count++ ;
+ }
+return (count);
+}
+#endif
+
+/**************************************************************************
+* Function : int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips using PCI config 2
+*
+* Inputs : tpnt - host template
+*
+* Returns : number of host adapters detected
+*
+* NOTE : This code assumes the controller on PCI bus 0.
+*
+* Origin: Robin Cutshaw (robin@xfree86.org)
+**************************************************************************/
+int AM53C974_nobios_detect(Scsi_Host_Template *tpnt)
+{
+int count = 0; /* number of boards detected */
+pci_config_t pci_config;
+
+/* first try PCI config method 1 */
+for (pci_config._pcibus = 0; pci_config._pcibus < 0x10; pci_config._pcibus++) {
+ for (pci_config._cardnum = 0; pci_config._cardnum < 0x20; pci_config._cardnum++) {
+ unsigned long config_cmd;
+ config_cmd = 0x80000000 | (pci_config._pcibus<<16) | (pci_config._cardnum<<11);
+
+ outl(config_cmd, 0xCF8); /* ioreg 0 */
+ pci_config._device_vendor = inl(0xCFC);
+
+ if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+ outl(config_cmd | PCI_COMMAND, 0xCF8); pci_config._status_command = inl(0xCFC);
+ outl(config_cmd | PCI_CLASS_REVISION, 0xCF8); pci_config._class_revision = inl(0xCFC);
+ outl(config_cmd | PCI_CACHE_LINE_SIZE, 0xCF8); pci_config._bist_header_latency_cache = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_0, 0xCF8); pci_config._base0 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_1, 0xCF8); pci_config._base1 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_2, 0xCF8); pci_config._base2 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_3, 0xCF8); pci_config._base3 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_4, 0xCF8); pci_config._base4 = inl(0xCFC);
+ outl(config_cmd | PCI_BASE_ADDRESS_5, 0xCF8); pci_config._base5 = inl(0xCFC);
+ outl(config_cmd | PCI_ROM_ADDRESS, 0xCF8); pci_config._baserom = inl(0xCFC);
+ outl(config_cmd | PCI_INTERRUPT_LINE, 0xCF8); pci_config._max_min_ipin_iline = inl(0xCFC);
+
+ /* check whether device is I/O mapped -- should be */
+ if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+ /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+ to set the PCI Master Enable Bit if needed.
+ From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+ if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+ pci_config._command |= PCI_COMMAND_MASTER;
+ printk("Config 1; PCI Master Bit has not been set. Setting...\n");
+ outl(config_cmd | PCI_COMMAND, 0xCF8); outw(pci_config._command, 0xCFC); }
+
+ /* everything seems OK now, so initialize */
+ if (AM53C974_init(tpnt, pci_config)) count++ ;
+ }
+ }
+ }
+outb(0, 0xCF8); /* is this really necessary? */
+
+/* try PCI config method 2, if no device was detected by method 1 */
+if (!count) {
+ AM53C974_PCIREG_OPEN();
+
+ pci_config._pcibus = 0xFFFFFFFF;
+ pci_config._cardnum = 0xFFFFFFFF;
+
+ for (pci_config._ioaddr = 0xC000; pci_config._ioaddr < 0xD000; pci_config._ioaddr += 0x0100) {
+ pci_config._device_vendor = inl(pci_config._ioaddr);
+
+ if ((pci_config._vendor == PCI_VENDOR_ID_AMD) && (pci_config._device == PCI_DEVICE_ID_AMD_SCSI)) {
+ pci_config._status_command = inl(pci_config._ioaddr + PCI_COMMAND);
+ pci_config._class_revision = inl(pci_config._ioaddr + PCI_CLASS_REVISION);
+ pci_config._bist_header_latency_cache = inl(pci_config._ioaddr + PCI_CACHE_LINE_SIZE);
+ pci_config._base0 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_0);
+ pci_config._base1 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_1);
+ pci_config._base2 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_2);
+ pci_config._base3 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_3);
+ pci_config._base4 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_4);
+ pci_config._base5 = inl(pci_config._ioaddr + PCI_BASE_ADDRESS_5);
+ pci_config._baserom = inl(pci_config._ioaddr + PCI_ROM_ADDRESS);
+ pci_config._max_min_ipin_iline = inl(pci_config._ioaddr + PCI_INTERRUPT_LINE);
+
+ /* check whether device is I/O mapped -- should be */
+ if (!(pci_config._command & PCI_COMMAND_IO)) continue;
+
+ /* PCI Spec 2.1 states that it is either the driver's or the PCI card's responsibility
+ to set the PCI Master Enable Bit if needed.
+ From Mark Stockton <marks@schooner.sys.hou.compaq.com> */
+ if (!(pci_config._command & PCI_COMMAND_MASTER)) {
+ pci_config._command |= PCI_COMMAND_MASTER;
+ printk("Config 2; PCI Master Bit has not been set. Setting...\n");
+ outw(pci_config._command, pci_config._ioaddr + PCI_COMMAND); }
+
+ /* everything seems OK now, so initialize */
+ if (AM53C974_init(tpnt, pci_config)) count++ ;
+ }
+ }
+ AM53C974_PCIREG_CLOSE();
+ }
+
+return(count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_detect(Scsi_Host_Template *tpnt)
+*
+* Purpose : detects and initializes AM53C974 SCSI chips
+*
+* Inputs : tpnt - host template
+*
+* Returns : number of host adapters detected
+**************************************************************************/
+int AM53C974_detect(Scsi_Host_Template *tpnt)
+{
+int count; /* number of boards detected */
+
+#if defined (CONFIG_PCI)
+if (pcibios_present())
+ count = AM53C974_bios_detect(tpnt);
+ else
+#endif
+count = AM53C974_nobios_detect(tpnt);
+return (count);
+}
+
+/**************************************************************************
+* Function : int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+*
+* Purpose : initializes instance and corresponding AM53/79C974 chip,
+*
+* Inputs : tpnt - template, pci_config - PCI configuration,
+*
+* Returns : 1 on success, 0 on failure.
+*
+* NOTE: If no override for the controller's SCSI id is given and AM53C974_SCSI_ID
+* is not defined we assume that the SCSI address of this controller is correctly
+* set up by the BIOS (as reflected by contents of register CNTLREG1).
+* This is the only BIOS assistance we need.
+**************************************************************************/
+static int AM53C974_init(Scsi_Host_Template *tpnt, pci_config_t pci_config)
+{
+AM53C974_local_declare();
+int i, j;
+struct Scsi_Host *instance, *search;
+struct AM53C974_hostdata *hostdata;
+
+#ifdef AM53C974_OPTION_DEBUG_PROBE_ONLY
+ printk ("AM53C974: probe only enabled, aborting initialization\n");
+ return -1;
+#endif
+
+instance = scsi_register(tpnt, sizeof(struct AM53C974_hostdata));
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+instance->base = NULL;
+instance->io_port = pci_config._base0 & (pci_config._base0 & 0x1 ?
+ 0xFFFFFFFC : 0xFFFFFFF0);
+instance->irq = pci_config._int_line;
+instance->dma_channel = -1;
+AM53C974_setio(instance);
+
+#ifdef AM53C974_SCSI_ID
+instance->this_id = AM53C974_SCSI_ID;
+AM53C974_write_8(CNTLREG1, instance->this_id & CNTLREG1_SID);
+#else
+instance->this_id = AM53C974_read_8(CNTLREG1) & CNTLREG1_SID;
+if (instance->this_id != 7)
+ printk("scsi%d: WARNING: unusual hostadapter SCSI id %d; please verify!\n",
+ instance->host_no, instance->this_id);
+#endif
+
+for (i = 0; i < sizeof(hostdata->msgout); i++) {
+ hostdata->msgout[i] = NOP;
+ hostdata->last_message[i] = NOP; }
+for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_per[i] = DEF_STP;
+ hostdata->sync_off[i] = 0;
+ hostdata->sync_neg[i] = 0;
+ hostdata->sync_en[i] = DEFAULT_SYNC_NEGOTIATION_ENABLED;
+ hostdata->max_rate[i] = DEFAULT_RATE;
+ hostdata->max_offset[i] = DEFAULT_SYNC_OFFSET; }
+
+/* overwrite defaults by LILO overrides */
+for (i = 0; i < commandline_current; i++) {
+ if (overrides[i].host_scsi_id == instance->this_id) {
+ j = overrides[i].target_scsi_id;
+ hostdata->sync_en[j] = 1;
+ hostdata->max_rate[j] = overrides[i].max_rate;
+ hostdata->max_offset[j] = overrides[i].max_offset;
+ }
+ }
+
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* Set up an interrupt handler if we aren't already sharing an IRQ with another board */
+for (search = first_host;
+ search && ( ((the_template != NULL) && (search->hostt != the_template)) ||
+ (search->irq != instance->irq) || (search == instance) );
+ search = search->next);
+if (!search) {
+ if (request_irq(instance->irq, AM53C974_intr, SA_INTERRUPT, "AM53C974")) {
+ printk("scsi%d: IRQ%d not free, detaching\n", instance->host_no, instance->irq);
+ scsi_unregister(instance);
+ return -1; }
+ }
+ else {
+ printk("scsi%d: using interrupt handler previously installed for scsi%d\n",
+ instance->host_no, search->host_no); }
+
+if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance; }
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV); /* reset device */
+udelay(5);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id);
+AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */
+udelay(10);
+AM53C974_config_after_reset(instance);
+
+return(0);
+}
+
+/*********************************************************************
+* Function : AM53C974_config_after_reset(struct Scsi_Host *instance) *
+* *
+* Purpose : initializes chip registers after reset *
+* *
+* Inputs : instance - which AM53C974 *
+* *
+* Returns : nothing *
+**********************************************************************/
+static void AM53C974_config_after_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+/* clear SCSI FIFO */
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+
+/* configure device */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT);
+AM53C974_write_8(STPREG, DEF_STP & STPREG_STP);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+AM53C974_write_8(CLKFREG, DEF_CLKF & CLKFREG_MASK);
+AM53C974_write_8(CNTLREG1, (DEF_ETM<<7) | CNTLREG1_DISR | (DEF_PERE<<4) | instance->this_id);
+AM53C974_write_8(CNTLREG2, (DEF_ENF<<6));
+AM53C974_write_8(CNTLREG3, (DEF_ADIDCHK<<7) | (DEF_FASTSCSI<<4) | (DEF_FASTCLK<<3));
+AM53C974_write_8(CNTLREG4, (DEF_GLITCH<<6) | (DEF_PWD<<5) | (DEF_RAE<<3) | (DEF_RADE<<2) | CNTLREG4_RES);
+}
+
+/***********************************************************************
+* Function : const char *AM53C974_info(struct Scsi_Host *instance) *
+* *
+* Purpose : return device driver information *
+* *
+* Inputs : instance - which AM53C974 *
+* *
+* Returns : info string *
+************************************************************************/
+const char *AM53C974_info(struct Scsi_Host *instance)
+{
+static char info[100];
+
+sprintf(info, "AM53/79C974 PCscsi driver rev. %d.%d; host I/O address: 0x%x; irq: %d\n",
+ AM53C974_DRIVER_REVISION_MAJOR, AM53C974_DRIVER_REVISION_MINOR,
+ instance->io_port, instance->irq);
+return (info);
+}
+
+/**************************************************************************
+* Function : int AM53C974_command (Scsi_Cmnd *SCpnt) *
+* *
+* Purpose : the unqueued SCSI command function, replaced by the *
+* AM53C974_queue_command function *
+* *
+* Inputs : SCpnt - pointer to command structure *
+* *
+* Returns :status, see hosts.h for details *
+***************************************************************************/
+int AM53C974_command(Scsi_Cmnd *SCpnt)
+{
+DEB(printk("AM53C974_command called\n"));
+return 0;
+}
+
+/**************************************************************************
+* Function : void initialize_SCp(Scsi_Cmnd *cmd) *
+* *
+* Purpose : initialize the saved data pointers for cmd to point to the *
+* start of the buffer. *
+* *
+* Inputs : cmd - Scsi_Cmnd structure to have pointers reset. *
+* *
+* Returns : nothing *
+**************************************************************************/
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd)
+{
+if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *)cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *)cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+ else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *)cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen; }
+}
+
+/**************************************************************************
+* Function : run_main(void) *
+* *
+* Purpose : insure that the coroutine is running and will process our *
+* request. main_running is checked/set here (in an inline *
+* function rather than in AM53C974_main itself to reduce the *
+* chances of stack overflow. *
+* *
+* *
+* Inputs : none *
+* *
+* Returns : nothing *
+**************************************************************************/
+static __inline__ void run_main(void)
+{
+cli();
+if (!main_running) {
+ /* main_running is cleared in AM53C974_main once it can't do
+ more work, and AM53C974_main exits with interrupts disabled. */
+ main_running = 1;
+ AM53C974_main();
+ sti(); }
+ else
+ sti();
+}
+
+/**************************************************************************
+* Function : int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+*
+* Purpose : writes SCSI command into AM53C974 FIFO
+*
+* Inputs : cmd - SCSI command, done - function called on completion, with
+* a pointer to the command descriptor.
+*
+* Returns : status, see hosts.h for details
+*
+* Side effects :
+* cmd is added to the per instance issue_queue, with minor
+* twiddling done to the host specific fields of cmd. If the
+* main coroutine is not running, it is restarted.
+**************************************************************************/
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *))
+{
+struct Scsi_Host *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *tmp;
+
+cli();
+DEB_QUEUE(printk(SEPARATOR_LINE));
+DEB_QUEUE(printk("scsi%d: AM53C974_queue_command called\n", instance->host_no));
+DEB_QUEUE(printk("cmd=%02x target=%02x lun=%02x bufflen=%d use_sg = %02x\n",
+ cmd->cmnd[0], cmd->target, cmd->lun, cmd->request_bufflen, cmd->use_sg));
+
+/* We use the host_scribble field as a pointer to the next command in a queue */
+cmd->host_scribble = NULL;
+cmd->scsi_done = done;
+cmd->result = 0;
+cmd->device->disconnect = 0;
+
+/* Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists. */
+if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = cmd; }
+ else {
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp->host_scribble;
+ tmp = (Scsi_Cmnd *)tmp->host_scribble);
+ LIST(cmd, tmp);
+ tmp->host_scribble = (unsigned char *)cmd; }
+
+DEB_QUEUE(printk("scsi%d : command added to %s of queue\n", instance->host_no,
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail"));
+
+/* Run the coroutine if it isn't already running. */
+run_main();
+return 0;
+}
+
+/**************************************************************************
+ * Function : AM53C974_main (void)
+ *
+ * Purpose : AM53C974_main is a coroutine that runs as long as more work can
+ * be done on the AM53C974 host adapters in a system. Both
+ * AM53C974_queue_command() and AM53C974_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : AM53C974_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ **************************************************************************/
+static void AM53C974_main(void)
+{
+AM53C974_local_declare();
+Scsi_Cmnd *tmp, *prev;
+struct Scsi_Host *instance;
+struct AM53C974_hostdata *hostdata;
+int done;
+
+/* We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit. */
+
+do {
+ cli(); /* Freeze request queues */
+ done = 1;
+ for (instance = first_instance; instance && instance->hostt == the_template;
+ instance = instance->next) {
+ hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+ AM53C974_setio(instance);
+ /* start to select target if we are not connected and not in the
+ selection process */
+ if (!hostdata->connected && !hostdata->sel_cmd) {
+ /* Search through the issue_queue for a command destined for a target
+ that is not busy. */
+ for (tmp = (Scsi_Cmnd *)hostdata->issue_queue, prev = NULL; tmp;
+ prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+ /* When we find one, remove it from the issue queue. */
+ if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+ if (prev) {
+ REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+ (Scsi_Cmnd *)(tmp->host_scribble));
+ prev->host_scribble = tmp->host_scribble; }
+ else {
+ REMOVE(-1, hostdata->issue_queue, tmp, tmp->host_scribble);
+ hostdata->issue_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+ tmp->host_scribble = NULL;
+
+ /* go into selection mode, disable reselection and wait for
+ SO interrupt which will continue with the selection process */
+ hostdata->selecting = 1;
+ hostdata->sel_cmd = tmp;
+ AM53C974_write_8(CMDREG, CMDREG_DSR);
+ break;
+ } /* if target/lun is not busy */
+
+ } /* for */
+ } /* if (!hostdata->connected) */
+ else {
+ DEB(printk("main: connected; cmd = 0x%lx, sel_cmd = 0x%lx\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd));
+ }
+ } /* for instance */
+ } while (!done);
+main_running = 0;
+}
+
+/*********************************************************************
+* Function : AM53C974_intr(int irq, struct pt_regs *regs) *
+* *
+* Purpose : interrupt handler *
+* *
+* Inputs : irq - interrupt line, regs - ? *
+* *
+* Returns : nothing *
+**********************************************************************/
+static void AM53C974_intr(int irq, struct pt_regs *regs)
+{
+AM53C974_local_declare();
+struct Scsi_Host *instance;
+struct AM53C974_hostdata *hostdata;
+unsigned char cmdreg, dmastatus, statreg, isreg, instreg, cfifo;
+
+/* find AM53C974 hostadapter responsible for this interrupt */
+for (instance = first_instance; instance; instance = instance->next)
+ if ((instance->irq == irq) && (instance->hostt == the_template)) goto FOUND;
+sti();
+return;
+
+/* found; now decode and process */
+FOUND:
+hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+dmastatus = AM53C974_read_8(DMASTATUS);
+
+DEB_INTR(printk(SEPARATOR_LINE));
+DEB_INTR(printk("AM53C974 interrupt; dmastatus=0x%02x\n", dmastatus));
+KEYWAIT();
+
+/*** DMA related interrupts ***/
+if (hostdata->connected && (dmastatus & (DMASTATUS_ERROR | DMASTATUS_PWDN |
+ DMASTATUS_ABORT))) {
+ /* DMA error or POWERDOWN */
+ printk("scsi%d: DMA error or powerdown; dmastatus: 0x%02x\n",
+ instance->host_no, dmastatus);
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (hostdata->connected && (dmastatus & DMASTATUS_DONE)) {
+ /* DMA transfer done */
+ unsigned long residual;
+ cli();
+ if (!(AM53C974_read_8(DMACMD) & DMACMD_DIR)) {
+ do {
+ dmastatus = AM53C974_read_8(DMASTATUS);
+ residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16);
+ residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+ } while (!(dmastatus & DMASTATUS_SCSIINT) && residual);
+ residual = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16);
+ residual += AM53C974_read_8(CFIREG) & CFIREG_CF;
+ }
+ else
+ residual = 0;
+ hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - residual;
+ hostdata->connected->SCp.this_residual = residual;
+
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+ /* if service request missed before, process it now (ugly) */
+ if (hostdata->dma_busy) {
+ hostdata->dma_busy = 0;
+ cmdreg = AM53C974_read_8(CMDREG);
+ statreg = AM53C974_read_8(STATREG);
+ isreg = AM53C974_read_8(ISREG);
+ instreg = AM53C974_read_8(INSTREG);
+ cfifo = AM53C974_cfifo();
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo,
+ dmastatus); }
+ sti();
+ }
+
+if (!(dmastatus & DMASTATUS_SCSIINT)) {
+ sti();
+ return; }
+
+/*** SCSI related interrupts ***/
+cmdreg = AM53C974_read_8(CMDREG);
+statreg = AM53C974_read_8(STATREG);
+isreg = AM53C974_read_8(ISREG);
+instreg = AM53C974_read_8(INSTREG);
+cfifo = AM53C974_cfifo();
+
+DEB_INTR(printk("scsi%d: statreg: 0x%02x; isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+ instance->host_no, statreg, isreg, instreg, cfifo));
+
+if (statreg & STATREG_PE) {
+ /* parity error */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ printk("scsi%d : PARITY error\n", instance->host_no);
+ if (hostdata->connected) hostdata->sync_off[hostdata->connected->target] = 0; /* setup asynchronous transfer */
+ hostdata->aborted = 1; }
+
+if (statreg & STATREG_IOE) {
+ /* illegal operation error */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ printk("scsi%d : ILLEGAL OPERATION error\n", instance->host_no);
+ printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; \n"
+ "isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+ cmdreg, AM53C974_read_8(DMACMD), statreg, isreg, instreg, cfifo); }
+if (hostdata->in_reset && (instreg & INSTREG_SRST)) {
+ /* RESET INTERRUPT */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ DEB(printk("Bus reset interrupt received\n"));
+ AM53C974_intr_bus_reset(instance);
+ cli();
+ if (hostdata->connected) {
+ hostdata->connected->result = DID_RESET << 16;
+ hostdata->connected->scsi_done((Scsi_Cmnd *)hostdata->connected);
+ hostdata->connected = NULL; }
+ else {
+ if (hostdata->sel_cmd) {
+ hostdata->sel_cmd->result = DID_RESET << 16;
+ hostdata->sel_cmd->scsi_done((Scsi_Cmnd *)hostdata->sel_cmd);
+ hostdata->sel_cmd = NULL; }
+ }
+ sti();
+ if (hostdata->in_reset == 1) goto EXIT;
+ else return;
+ }
+
+if (instreg & INSTREG_ICMD) {
+ /* INVALID COMMAND INTERRUPT */
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ printk("scsi%d: Invalid command interrupt\n", instance->host_no);
+ printk("cmdreg: 0x%02x; dmacmd: 0x%02x; statreg: 0x%02x; dmastatus: 0x%02x; \n"
+ "isreg: 0x%02x; instreg: 0x%02x; cfifo: 0x%02x\n",
+ cmdreg, AM53C974_read_8(DMACMD), statreg, dmastatus, isreg, instreg, cfifo);
+ panic("scsi%d: cannot recover\n", instance->host_no); }
+
+if (instreg & INSTREG_DIS) {
+ /* DISCONNECT INTERRUPT */
+ DEB_INTR(printk("Disconnect interrupt received; "));
+ cli();
+ AM53C974_intr_disconnect(instance);
+ sti();
+ goto EXIT; }
+
+if (instreg & INSTREG_RESEL) {
+ /* RESELECTION INTERRUPT */
+ DEB_INTR(printk("Reselection interrupt received\n"));
+ cli();
+ AM53C974_intr_reselect(instance, statreg);
+ sti();
+ goto EXIT; }
+
+if (instreg & INSTREG_SO) {
+ DEB_INTR(printk("Successful operation interrupt received\n"));
+ if (hostdata->selecting) {
+ DEB_INTR(printk("DSR completed, starting select\n"));
+ cli();
+ AM53C974_select(instance, (Scsi_Cmnd *)hostdata->sel_cmd,
+ (hostdata->sel_cmd->cmnd[0] == REQUEST_SENSE) ?
+ TAG_NONE : TAG_NEXT);
+ hostdata->selecting = 0;
+ AM53C974_set_sync(instance, hostdata->sel_cmd->target);
+ sti();
+ return; }
+
+ if (hostdata->sel_cmd != NULL) {
+ if ( ((isreg & ISREG_IS) != ISREG_OK_NO_STOP) &&
+ ((isreg & ISREG_IS) != ISREG_OK_STOP) ) {
+ /* UNSUCCESSFUL SELECTION */
+ DEB_INTR(printk("unsuccessful selection\n"));
+ cli();
+ hostdata->dma_busy = 0;
+ LIST(hostdata->sel_cmd, hostdata->issue_queue);
+ hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = hostdata->sel_cmd;
+ hostdata->sel_cmd = NULL;
+ hostdata->selecting = 0;
+ sti();
+ goto EXIT; }
+ else {
+ /* SUCCESSFUL SELECTION */
+ DEB(printk("successful selection; cmd=0x%02lx\n", (long)hostdata->sel_cmd));
+ cli();
+ hostdata->dma_busy = 0;
+ hostdata->disconnecting = 0;
+ hostdata->connected = hostdata->sel_cmd;
+ hostdata->sel_cmd = NULL;
+ hostdata->selecting = 0;
+#ifdef SCSI2
+ if (!hostdata->connected->device->tagged_queue)
+#endif
+ hostdata->busy[hostdata->connected->target] |= (1 << hostdata->connected->lun);
+ /* very strange -- use_sg is sometimes nonzero for request sense commands !! */
+ if ((hostdata->connected->cmnd[0] == REQUEST_SENSE) && hostdata->connected->use_sg) {
+ DEB(printk("scsi%d: REQUEST_SENSE command with nonzero use_sg\n", instance->host_no));
+ KEYWAIT();
+ hostdata->connected->use_sg = 0; }
+ initialize_SCp((Scsi_Cmnd *)hostdata->connected);
+ hostdata->connected->SCp.phase = PHASE_CMDOUT;
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+ sti();
+ return; }
+ }
+ else {
+ cli();
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+ sti();
+ return; }
+ }
+
+if (instreg & INSTREG_SR) {
+ DEB_INTR(printk("Service request interrupt received, "));
+ if (hostdata->connected) {
+ DEB_INTR(printk("calling information_transfer\n"));
+ cli();
+ AM53C974_information_transfer(instance, statreg, isreg, instreg, cfifo, dmastatus);
+ sti(); }
+ else {
+ printk("scsi%d: weird: service request when no command connected\n", instance->host_no);
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO); } /* clear FIFO */
+ return;
+ }
+
+EXIT:
+ DEB_INTR(printk("intr: starting main\n"));
+ run_main();
+ DEB_INTR(printk("end of intr\n"));
+}
+
+/**************************************************************************
+* Function : AM53C974_intr_disconnect(struct Scsi_Host *instance)
+*
+* Purpose : manage target disconnection
+*
+* Inputs : instance -- which AM53C974
+*
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_disconnect(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *cmd;
+AM53C974_setio(instance);
+
+if (hostdata->sel_cmd != NULL) {
+ /* normal selection timeout, typical for nonexisting targets */
+ cmd = (Scsi_Cmnd *)hostdata->sel_cmd;
+ DEB_INTR(printk("bad target\n"));
+ cmd->result = DID_BAD_TARGET << 16;
+ goto EXIT_FINISHED; }
+
+if (!hostdata->connected) {
+ /* can happen if controller was reset, a device tried to reconnect,
+ failed and disconnects now */
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+ return; }
+
+if (hostdata->disconnecting) {
+ /* target sent disconnect message, so we are prepared */
+ cmd = (Scsi_Cmnd *)hostdata->connected;
+ AM53C974_set_async(instance, cmd->target);
+ DEB_INTR(printk("scsi%d : disc. from cmnd %d for ta %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ if (cmd->device->disconnect) {
+ /* target wants to reselect later */
+ DEB_INTR(printk("ok, re-enabling selection\n"));
+ LIST(cmd,hostdata->disconnected_queue);
+ cmd->host_scribble = (unsigned char *)hostdata->disconnected_queue;
+ hostdata->disconnected_queue = cmd;
+ DEB_QUEUE(printk("scsi%d : command for target %d lun %d this %d was moved from connected to"
+ " the disconnected_queue\n", instance->host_no, cmd->target,
+ cmd->lun, hostdata->disconnected_queue->SCp.this_residual));
+ DEB_QUEUE(AM53C974_print_queues(instance));
+ goto EXIT_UNFINISHED; }
+ else {
+ /* target does not want to reselect later, we are really finished */
+#ifdef AM53C974_DEBUG
+ if (cmd->cmnd[0] == REQUEST_SENSE) {
+ int i;
+ printk("Request sense data dump:\n");
+ for (i = 0; i < cmd->request_bufflen; i++) {
+ printk("%02x ", *((char *)(cmd->request_buffer) + i));
+ if (i && !(i % 16)) printk("\n"); }
+ printk("\n"); }
+#endif
+ goto EXIT_FINISHED; } /* !cmd->device->disconnect */
+ } /* if (hostdata->disconnecting) */
+
+/* no disconnect message received; unexpected disconnection */
+cmd = (Scsi_Cmnd *)hostdata->connected;
+if (cmd) {
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ AM53C974_set_async(instance, cmd->target);
+ printk("scsi%d: Unexpected disconnect; phase: %d; target: %d; this_residual: %d; buffers_residual: %d; message: %d\n",
+ instance->host_no, cmd->SCp.phase, cmd->target, cmd->SCp.this_residual, cmd->SCp.buffers_residual,
+ cmd->SCp.Message);
+ printk("cmdreg: 0x%02x; statreg: 0x%02x; isreg: 0x%02x; cfifo: 0x%02x\n",
+ AM53C974_read_8(CMDREG), AM53C974_read_8(STATREG), AM53C974_read_8(ISREG),
+ AM53C974_read_8(CFIREG) & CFIREG_CF);
+
+ if ((hostdata->last_message[0] == EXTENDED_MESSAGE) &&
+ (hostdata->last_message[2] == EXTENDED_SDTR)) {
+ /* sync. negotiation was aborted, setup asynchronous transfer with target */
+ hostdata->sync_off[cmd->target] = 0; }
+ if (hostdata->aborted || hostdata->msgout[0] == ABORT)
+ cmd->result = DID_ABORT << 16;
+ else
+ cmd->result = DID_ERROR << 16;
+ goto EXIT_FINISHED; }
+
+EXIT_FINISHED:
+hostdata->aborted = 0;
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n",
+ (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+cmd->scsi_done(cmd);
+
+if (!hostdata->selecting) {
+ AM53C974_set_async(instance, cmd->target);
+ AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+
+EXIT_UNFINISHED:
+hostdata->msgout[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+DEB(printk("disconnect; issue_queue: 0x%lx, disconnected_queue: 0x%lx\n",
+ (long)hostdata->issue_queue, (long)hostdata->disconnected_queue));
+if (!hostdata->selecting) {
+ AM53C974_set_async(instance, cmd->target);
+ AM53C974_write_8(CMDREG, CMDREG_ESR); } /* allow reselect */
+return;
+}
+
+/**************************************************************************
+* Function : int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
+*
+* Purpose : setup message string for sync. negotiation
+*
+* Inputs : instance -- which AM53C974
+* target -- which SCSI target to deal with
+* msg -- input message string
+*
+* Returns : 0 if parameters accepted or 1 if not accepted
+*
+* Side effects: hostdata is changed
+*
+* Note: we assume here that fastclk is enabled
+**************************************************************************/
+static int AM53C974_sync_neg(struct Scsi_Host *instance, int target, unsigned char *msg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+int period, offset, i, rate, rate_rem;
+AM53C974_setio(instance);
+
+period = (DEF_CLK * msg[3] * 8 + 1000) / 2000;
+if (period < MIN_PERIOD) {
+ period = MIN_PERIOD;
+ hostdata->msgout[3] = period / 4; }
+ else
+ if (period > MAX_PERIOD) {
+ period = MAX_PERIOD;
+ hostdata->msgout[3] = period / 4; }
+ else
+ hostdata->msgout[3] = msg[3];
+offset = msg[4];
+if (offset > MAX_OFFSET) offset = MAX_OFFSET;
+hostdata->msgout[4] = offset;
+hostdata->sync_per[target] = period;
+hostdata->sync_off[target] = offset;
+for (i = 0; i < 3; i++) hostdata->msgout[i] = msg[i];
+if ((hostdata->msgout[3] != msg[3]) || (msg[4] != offset)) return(1);
+
+rate = DEF_CLK / period;
+rate_rem = 10 * (DEF_CLK - period * rate) / period;
+
+if (offset)
+ printk("\ntarget %d: rate=%d.%d Mhz, synchronous, sync offset=%d bytes\n",
+ target, rate, rate_rem, offset);
+ else
+ printk("\ntarget %d: rate=%d.%d Mhz, asynchronous\n", target, rate, rate_rem);
+
+return(0);
+}
+
+/**************************************************************************
+* Function : AM53C974_set_async(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into async. mode
+*
+* Inputs : instance -- which AM53C974
+* target -- which SCSI target to deal with
+*
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_async(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/**************************************************************************
+* Function : AM53C974_set_sync(struct Scsi_Host *instance, int target)
+*
+* Purpose : put controller into sync. mode
+*
+* Inputs : instance -- which AM53C974
+* target -- which SCSI target to deal with
+*
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_set_sync(struct Scsi_Host *instance, int target)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+AM53C974_write_8(STPREG, hostdata->sync_per[target]);
+AM53C974_write_8(SOFREG, (SOFREG_SO & hostdata->sync_off[target]) |
+ (DEF_SOF_RAD<<6) | (DEF_SOF_RAA<<4));
+}
+
+/***********************************************************************
+* Function : AM53C974_information_transfer(struct Scsi_Host *instance, *
+* unsigned char statreg, unsigned char isreg, *
+* unsigned char instreg, unsigned char cfifo, *
+* unsigned char dmastatus) *
+* *
+* Purpose : handle phase changes *
+* *
+* Inputs : instance - which AM53C974 *
+* statreg - stus register *
+* isreg - internal state register *
+* instreg - interrupt status register *
+* cfifo - number of bytes in FIFO *
+* dmastatus - dma status register *
+* *
+* Returns : nothing *
+************************************************************************/
+static void AM53C974_information_transfer(struct Scsi_Host *instance,
+ unsigned char statreg, unsigned char isreg,
+ unsigned char instreg, unsigned char cfifo,
+ unsigned char dmastatus)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *cmd = (Scsi_Cmnd *)hostdata->connected;
+int ret, i, len, residual=-1;
+AM53C974_setio(instance);
+
+DEB_INFO(printk(SEPARATOR_LINE));
+switch (statreg & STATREG_PHASE) { /* scsi phase */
+ case PHASE_DATAOUT:
+ DEB_INFO(printk("Dataout phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+ cmd->SCp.phase = PHASE_DATAOUT;
+ goto PHASE_DATA_IO;
+
+ case PHASE_DATAIN:
+ DEB_INFO(printk("Datain phase; cmd=0x%lx, sel_cmd=0x%lx, this_residual=%d, buffers_residual=%d\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd, cmd->SCp.this_residual, cmd->SCp.buffers_residual));
+ cmd->SCp.phase = PHASE_DATAIN;
+ PHASE_DATA_IO:
+ if (hostdata->aborted) {
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ return; }
+ if ((!cmd->SCp.this_residual) && cmd->SCp.buffers_residual) {
+ cmd->SCp.buffer++;
+ cmd->SCp.buffers_residual--;
+ cmd->SCp.ptr = (unsigned char *)cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length; }
+ if (cmd->SCp.this_residual) {
+ if (!(AM53C974_read_8(DMACMD) & DMACMD_START)) {
+ hostdata->dma_busy = 0;
+ AM53C974_transfer_dma(instance, statreg & STATREG_IO,
+ (unsigned long)cmd->SCp.this_residual,
+ cmd->SCp.ptr); }
+ else
+ hostdata->dma_busy = 1;
+ }
+ return;
+
+ case PHASE_MSGIN:
+ DEB_INFO(printk("Message-In phase; cmd=0x%lx, sel_cmd=0x%lx\n",
+ (long)hostdata->connected, (long)hostdata->sel_cmd));
+ AM53C974_set_async(instance, cmd->target);
+ if (cmd->SCp.phase == PHASE_DATAIN)
+ AM53C974_dma_blast(instance, dmastatus, statreg);
+ if ((cmd->SCp.phase == PHASE_DATAOUT) && (AM53C974_read_8(DMACMD) & DMACMD_START)) {
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16));
+ cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+ cmd->SCp.this_residual = residual;
+ if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+ }
+ if (cmd->SCp.phase == PHASE_STATIN) {
+ while ((AM53C974_read_8(CFIREG) & CFIREG_CF) < 2) ;
+ cmd->SCp.Status = AM53C974_read_8(FFREG);
+ cmd->SCp.Message = AM53C974_read_8(FFREG);
+ DEB_INFO(printk("Message-In phase; status=0x%02x, message=0x%02x\n",
+ cmd->SCp.Status, cmd->SCp.Message));
+ ret = AM53C974_message(instance, cmd, cmd->SCp.Message); }
+ else {
+ if (!cfifo) {
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ AM53C974_poll_int();
+ cmd->SCp.Message = AM53C974_read_8(FFREG);
+ }
+ ret = AM53C974_message(instance, cmd, cmd->SCp.Message);
+ }
+ cmd->SCp.phase = PHASE_MSGIN;
+ AM53C974_set_sync(instance, cmd->target);
+ break;
+ case PHASE_MSGOUT:
+ DEB_INFO(printk("Message-Out phase; cfifo=%d; msgout[0]=0x%02x\n",
+ AM53C974_read_8(CFIREG) & CFIREG_CF, hostdata->msgout[0]));
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ AM53C974_set_async(instance, cmd->target);
+ for (i = 0; i < sizeof(hostdata->last_message); i++)
+ hostdata->last_message[i] = hostdata->msgout[i];
+ if ((hostdata->msgout[0] == 0) || INSIDE(hostdata->msgout[0], 0x02, 0x1F) ||
+ INSIDE(hostdata->msgout[0], 0x80, 0xFF))
+ len = 1;
+ else {
+ if (hostdata->msgout[0] == EXTENDED_MESSAGE) {
+#ifdef AM53C974_DEBUG_INFO
+ printk("Extended message dump:\n");
+ for (i = 0; i < hostdata->msgout[1] + 2; i++) {
+ printk("%02x ", hostdata->msgout[i]);
+ if (i && !(i % 16)) printk("\n"); }
+ printk("\n");
+#endif
+ len = hostdata->msgout[1] + 2; }
+ else
+ len = 2;
+ }
+ for (i = 0; i < len; i++) AM53C974_write_8(FFREG, hostdata->msgout[i]);
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ cmd->SCp.phase = PHASE_MSGOUT;
+ hostdata->msgout[0] = NOP;
+ AM53C974_set_sync(instance, cmd->target);
+ break;
+
+ case PHASE_CMDOUT:
+ DEB_INFO(printk("Command-Out phase\n"));
+ AM53C974_set_async(instance, cmd->target);
+ for (i = 0; i < cmd->cmd_len; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ cmd->SCp.phase = PHASE_CMDOUT;
+ AM53C974_set_sync(instance, cmd->target);
+ break;
+
+ case PHASE_STATIN:
+ DEB_INFO(printk("Status phase\n"));
+ if (cmd->SCp.phase == PHASE_DATAIN)
+ AM53C974_dma_blast(instance, dmastatus, statreg);
+ AM53C974_set_async(instance, cmd->target);
+ if (cmd->SCp.phase == PHASE_DATAOUT) {
+ unsigned long residual;
+
+ if (AM53C974_read_8(DMACMD) & DMACMD_START) {
+ AM53C974_write_8(DMACMD, DMACMD_IDLE);
+ residual = cfifo + (AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16));
+ cmd->SCp.ptr += cmd->SCp.this_residual - residual;
+ cmd->SCp.this_residual = residual; }
+ if (cfifo) { AM53C974_write_8(CMDREG, CMDREG_CFIFO); cfifo = 0; }
+ }
+ cmd->SCp.phase = PHASE_STATIN;
+ AM53C974_write_8(CMDREG, CMDREG_ICCS); /* command complete */
+ break;
+
+ case PHASE_RES_0:
+ case PHASE_RES_1:
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ DEB_INFO(printk("Reserved phase\n"));
+ break;
+ }
+KEYWAIT();
+}
+
+/******************************************************************************
+* Function : int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+* unsigned char msg)
+*
+* Purpose : handle SCSI messages
+*
+* Inputs : instance -- which AM53C974
+* cmd -- SCSI command the message belongs to
+* msg -- message id byte
+*
+* Returns : 1 on success, 0 on failure.
+**************************************************************************/
+static int AM53C974_message(struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ unsigned char msg)
+{
+AM53C974_local_declare();
+static unsigned char extended_msg[10];
+unsigned char statreg;
+int len, ret = 0;
+unsigned char *p;
+#ifdef AM53C974_DEBUG_MSG
+int j;
+#endif
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+DEB_MSG(printk(SEPARATOR_LINE));
+
+/* Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands. */
+switch (msg) {
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by releasing ACK */
+ DEB_LINKED(printk("scsi%d : target %d lun %d linked command complete.\n",
+ instance->host_no, cmd->target, cmd->lun));
+ /* Sanity check : A linked command should only terminate with
+ * one of these messages if there are more linked commands available. */
+ if (!cmd->next_link) {
+ printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
+ instance->host_no, cmd->target, cmd->lun);
+ hostdata->aborted = 1;
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break; }
+ if (hostdata->aborted) {
+ DEB_ABORT(printk("ATN set for cmnd %d upon reception of LINKED_CMD_COMPLETE or"
+ "LINKED_FLG_CMD_COMPLETE message\n", cmd->cmnd[0]));
+ AM53C974_write_8(CMDREG, CMDREG_SATN); }
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ DEB_LINKED(printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
+ instance->host_no, cmd->target, cmd->lun));
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+
+#endif /* def LINKED */
+
+ case ABORT:
+ case COMMAND_COMPLETE:
+ DEB_MSG(printk("scsi%d: command complete message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ hostdata->disconnecting = 1;
+ cmd->device->disconnect = 0;
+
+ /* I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ * If it was a REQUEST SENSE command, we need some way
+ * to differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the result
+ * code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed */
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+ if (hostdata->aborted) {
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ DEB_ABORT(printk("ATN set for cmnd %d upon reception of ABORT or"
+ "COMMAND_COMPLETE message\n", cmd->cmnd[0]));
+ break; }
+ if ((cmd->cmnd[0] != REQUEST_SENSE) && (cmd->SCp.Status == CHECK_CONDITION)) {
+ DEB_MSG(printk("scsi%d : performing request sense\n", instance->host_no));
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *)cmd->sense_buffer;
+ cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+ LIST(cmd,hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *)cmd;
+ DEB_MSG(printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no));
+ }
+
+ /* Accept message by clearing ACK */
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ case MESSAGE_REJECT:
+ DEB_MSG(printk("scsi%d: reject message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ switch (hostdata->last_message[0]) {
+ case EXTENDED_MESSAGE:
+ if (hostdata->last_message[2] == EXTENDED_SDTR) {
+ /* sync. negotiation was rejected, setup asynchronous transfer with target */
+ printk("\ntarget %d: rate=%d Mhz, asynchronous (sync. negotiation rejected)\n",
+ cmd->target, DEF_CLK / DEF_STP);
+ hostdata->sync_off[cmd->target] = 0;
+ hostdata->sync_per[cmd->target] = DEF_STP; }
+ break;
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ cmd->device->tagged_queue = 0;
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+ break;
+ default:
+ break;
+ }
+ if (hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ case DISCONNECT:
+ DEB_MSG(printk("scsi%d: disconnect message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ cmd->device->disconnect = 1;
+ hostdata->disconnecting = 1;
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* Accept message by clearing ACK */
+ break;
+
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ DEB_MSG(printk("scsi%d: save/restore pointers message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ /* The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible. */
+ if (hostdata->aborted) {
+ DEB_ABORT(printk("ATN set for cmnd %d upon reception of SAVE/REST. POINTERS message\n",
+ cmd->cmnd[0]));
+ AM53C974_write_8(CMDREG, CMDREG_SATN); }
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ case EXTENDED_MESSAGE:
+ DEB_MSG(printk("scsi%d: extended message received; cmd %d for target %d, lun %d\n",
+ instance->host_no, cmd->cmnd[0], cmd->target, cmd->lun));
+ /* Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ */
+ /* BEWARE!! THIS CODE IS EXTREMELY UGLY */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ AM53C974_read_8(INSTREG) ; /* clear int */
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
+ AM53C974_poll_int();
+ /* get length */
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ AM53C974_poll_int();
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
+ AM53C974_poll_int();
+ extended_msg[1] = len = AM53C974_read_8(FFREG); /* get length */
+ p = extended_msg+2;
+ /* read the remaining (len) bytes */
+ while (len) {
+ AM53C974_write_8(CMDREG, CMDREG_IT);
+ AM53C974_poll_int();
+ if (len > 1) {
+ AM53C974_write_8(CMDREG, CMDREG_MA); /* ack. msg byte, then wait for SO */
+ AM53C974_poll_int(); }
+ *p = AM53C974_read_8(FFREG);
+ p++; len--; }
+
+#ifdef AM53C974_DEBUG_MSG
+ printk("scsi%d: received extended message: ", instance->host_no);
+ for (j = 0; j < extended_msg[1] + 2; j++) {
+ printk("0x%02x ", extended_msg[j]);
+ if (j && !(j % 16)) printk("\n"); }
+ printk("\n");
+#endif
+
+ /* check message */
+ if (extended_msg[2] == EXTENDED_SDTR)
+ ret = AM53C974_sync_neg(instance, cmd->target, extended_msg);
+ if (ret || hostdata->aborted) AM53C974_write_8(CMDREG, CMDREG_SATN);
+
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ break;
+
+ default:
+ printk("scsi%d: unknown message 0x%02x received\n",instance->host_no, msg);
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+ /* reject message */
+ hostdata->msgout[0] = MESSAGE_REJECT;
+ AM53C974_write_8(CMDREG, CMDREG_SATN);
+ AM53C974_write_8(CMDREG, CMDREG_MA);
+ return(0);
+ break;
+
+ } /* switch (msg) */
+KEYWAIT();
+return(1);
+}
+
+/**************************************************************************
+* Function : AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+*
+* Purpose : try to establish nexus for the command;
+* start sync negotiation via start stop and transfer the command in
+* cmdout phase in case of an inquiry or req. sense command with no
+* sync. neg. performed yet
+*
+* Inputs : instance -- which AM53C974
+* cmd -- command which requires the selection
+* tag -- tagged queueing
+*
+* Returns : nothing
+*
+* Note: this function initializes the selection process, which is continued
+* in the interrupt handler
+**************************************************************************/
+static void AM53C974_select(struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char cfifo, tmp[3];
+unsigned int i, len, cmd_size = COMMAND_SIZE(cmd->cmnd[0]);
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+if (cfifo) {
+ printk("scsi%d: select error; %d residual bytes in FIFO\n", instance->host_no, cfifo);
+ AM53C974_write_8(CMDREG, CMDREG_CFIFO); /* clear FIFO */
+ }
+
+tmp[0] = IDENTIFY(1, cmd->lun);
+
+#ifdef SCSI2
+if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+ tmp[1] = SIMPLE_QUEUE_TAG;
+ if (tag == TAG_NEXT) {
+ /* 0 is TAG_NONE, used to imply no tag for this command */
+ if (cmd->device->current_tag == 0) cmd->device->current_tag = 1;
+ cmd->tag = cmd->device->current_tag;
+ cmd->device->current_tag++; }
+ else
+ cmd->tag = (unsigned char)tag;
+ tmp[2] = cmd->tag;
+ hostdata->last_message[0] = SIMPLE_QUEUE_TAG;
+ len = 3;
+ AM53C974_write_8(FFREG, tmp[0]);
+ AM53C974_write_8(FFREG, tmp[1]);
+ AM53C974_write_8(FFREG, tmp[2]);
+ }
+ else
+#endif /* def SCSI2 */
+ {
+ len = 1;
+ AM53C974_write_8(FFREG, tmp[0]);
+ cmd->tag = 0; }
+
+/* in case of an inquiry or req. sense command with no sync. neg performed yet, we start
+ sync negotiation via start stops and transfer the command in cmdout phase */
+if (((cmd->cmnd[0] == INQUIRY) || (cmd->cmnd[0] == REQUEST_SENSE)) &&
+ !(hostdata->sync_neg[cmd->target]) && hostdata->sync_en[cmd->target]) {
+ hostdata->sync_neg[cmd->target] = 1;
+ hostdata->msgout[0] = EXTENDED_MESSAGE;
+ hostdata->msgout[1] = 3;
+ hostdata->msgout[2] = EXTENDED_SDTR;
+ hostdata->msgout[3] = 250 / (int)hostdata->max_rate[cmd->target];
+ hostdata->msgout[4] = hostdata->max_offset[cmd->target];
+ len += 5; }
+
+AM53C974_write_8(SDIDREG, SDIREG_MASK & cmd->target); /* setup dest. id */
+AM53C974_write_8(STIMREG, DEF_SCSI_TIMEOUT); /* setup timeout reg */
+switch (len) {
+ case 1:
+ for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+ AM53C974_write_8(CMDREG, CMDREG_SAS); /* select with ATN, 1 msg byte */
+ hostdata->msgout[0] = NOP;
+ break;
+ case 3:
+ for (i = 0; i < cmd_size; i++) AM53C974_write_8(FFREG, cmd->cmnd[i]);
+ AM53C974_write_8(CMDREG, CMDREG_SA3S); /* select with ATN, 3 msg bytes */
+ hostdata->msgout[0] = NOP;
+ break;
+ default:
+ AM53C974_write_8(CMDREG, CMDREG_SASS); /* select with ATN, stop steps; continue in message out phase */
+ break;
+ }
+}
+
+/**************************************************************************
+* Function : AM53C974_intr_select(struct Scsi_Host *instance, unsigned char statreg)
+*
+* Purpose : handle reselection
+*
+* Inputs : instance -- which AM53C974
+* statreg -- status register
+*
+* Returns : nothing
+*
+* side effects: manipulates hostdata
+**************************************************************************/
+static void AM53C974_intr_reselect(struct Scsi_Host *instance, unsigned char statreg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned char cfifo, msg[3], lun, t, target = 0;
+#ifdef SCSI2
+ unsigned char tag;
+#endif
+Scsi_Cmnd *tmp = NULL, *prev;
+AM53C974_setio(instance);
+
+cfifo = AM53C974_cfifo();
+
+if (hostdata->selecting) {
+ /* caught reselect interrupt in selection process;
+ put selecting command back into the issue queue and continue with the
+ reselecting command */
+ DEB_RESEL(printk("AM53C974_intr_reselect: in selection process\n"));
+ LIST(hostdata->sel_cmd, hostdata->issue_queue);
+ hostdata->sel_cmd->host_scribble = (unsigned char *)hostdata->issue_queue;
+ hostdata->issue_queue = hostdata->sel_cmd;
+ hostdata->sel_cmd = NULL;
+ hostdata->selecting = 0; }
+
+/* 2 bytes must be in the FIFO now */
+if (cfifo != 2) {
+ printk("scsi %d: error: %d bytes in fifo, 2 expected\n", instance->host_no, cfifo);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+
+/* determine target which reselected */
+t = AM53C974_read_8(FFREG);
+if (!(t & (1 << instance->this_id))) {
+ printk("scsi %d: error: invalid host id\n", instance->host_no);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+t ^= (1 << instance->this_id);
+target = 0; while (t != 1) { t >>= 1; target++; }
+DEB_RESEL(printk("scsi %d: reselect; target: %d\n", instance->host_no, target));
+
+if (hostdata->aborted) goto EXIT_ABORT;
+
+if ((statreg & STATREG_PHASE) != PHASE_MSGIN) {
+ printk("scsi %d: error: upon reselection interrupt not in MSGIN\n", instance->host_no);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+
+msg[0] = AM53C974_read_8(FFREG);
+if (!msg[0] & 0x80) {
+ printk("scsi%d: error: expecting IDENTIFY message, got ", instance->host_no);
+ print_msg(msg);
+ hostdata->aborted = 1;
+ goto EXIT_ABORT; }
+
+lun = (msg[0] & 0x07);
+
+/* We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer. */
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+/* Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = (Scsi_Cmnd *)tmp->host_scribble)
+ if ((target == tmp->target) && (lun == tmp->lun)
+#ifdef SCSI2
+ && (tag == tmp->tag)
+#endif
+ ) {
+ if (prev) {
+ REMOVE(prev, (Scsi_Cmnd *)(prev->host_scribble), tmp,
+ (Scsi_Cmnd *)(tmp->host_scribble));
+ prev->host_scribble = tmp->host_scribble; }
+ else {
+ REMOVE(-1, hostdata->disconnected_queue, tmp, tmp->host_scribble);
+ hostdata->disconnected_queue = (Scsi_Cmnd *)tmp->host_scribble; }
+ tmp->host_scribble = NULL;
+ hostdata->connected = tmp;
+ break; }
+
+if (!tmp) {
+#ifdef SCSI2
+ printk("scsi%d: warning : target %d lun %d tag %d not in disconnect_queue.\n",
+ instance->host_no, target, lun, tag);
+#else
+ printk("scsi%d: warning : target %d lun %d not in disconnect_queue.\n",
+ instance->host_no, target, lun);
+#endif
+ /* Since we have an established nexus that we can't do anything with, we must abort it. */
+ hostdata->aborted = 1;
+ DEB(AM53C974_keywait());
+ goto EXIT_ABORT; }
+ else
+ goto EXIT_OK;
+
+EXIT_ABORT:
+AM53C974_write_8(CMDREG, CMDREG_SATN);
+AM53C974_write_8(CMDREG, CMDREG_MA);
+return;
+
+EXIT_OK:
+DEB_RESEL(printk("scsi%d: nexus established, target = %d, lun = %d, tag = %d\n",
+ instance->host_no, target, tmp->lun, tmp->tag));
+AM53C974_set_sync(instance, target);
+AM53C974_write_8(SDIDREG, SDIREG_MASK & target); /* setup dest. id */
+AM53C974_write_8(CMDREG, CMDREG_MA);
+hostdata->dma_busy = 0;
+hostdata->connected->SCp.phase = PHASE_CMDOUT;
+}
+
+/**************************************************************************
+* Function : AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+* unsigned long length, char *data)
+*
+* Purpose : setup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+* dir -- direction flag, 0: write to device, read from memory;
+* 1: read from device, write to memory
+* length -- number of bytes to transfer to from buffer
+* data -- pointer to data buffer
+*
+* Returns : nothing
+**************************************************************************/
+static __inline__ void AM53C974_transfer_dma(struct Scsi_Host *instance, short dir,
+ unsigned long length, char *data)
+{
+AM53C974_local_declare();
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D); /* idle command */
+AM53C974_write_8(STCLREG, (unsigned char)(length & 0xff));
+AM53C974_write_8(STCMREG, (unsigned char)((length & 0xff00) >> 8));
+AM53C974_write_8(STCHREG, (unsigned char)((length & 0xff0000) >> 16));
+AM53C974_write_32(DMASTC, length & 0xffffff);
+AM53C974_write_32(DMASPA, (unsigned long)data);
+AM53C974_write_8(CMDREG, CMDREG_IT | CMDREG_DMA);
+AM53C974_write_8(DMACMD, (dir << 7) | DMACMD_INTE_D | DMACMD_START);
+}
+
+/**************************************************************************
+* Function : AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+* unsigned char statreg)
+*
+* Purpose : cleanup DMA transfer
+*
+* Inputs : instance -- which AM53C974
+* dmastatus -- dma status register
+* statreg -- status register
+*
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_dma_blast(struct Scsi_Host *instance, unsigned char dmastatus,
+ unsigned char statreg)
+{
+AM53C974_local_declare();
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+unsigned long ctcreg;
+int dir = statreg & STATREG_IO;
+int cfifo, pio, i = 0;
+AM53C974_setio(instance);
+
+do {
+ cfifo = AM53C974_cfifo();
+ i++;
+ } while (cfifo && (i < 50000));
+pio = (i == 50000) ? 1: 0;
+
+if (statreg & STATREG_CTZ) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+if (dmastatus & DMASTATUS_DONE) { AM53C974_write_8(DMACMD, DMACMD_IDLE); return; }
+
+AM53C974_write_8(DMACMD, ((dir << 7) & DMACMD_DIR) | DMACMD_BLAST);
+while(!(AM53C974_read_8(DMASTATUS) & DMASTATUS_BCMPLT)) ;
+AM53C974_write_8(DMACMD, DMACMD_IDLE);
+
+if (pio) {
+ /* transfer residual bytes via PIO */
+ unsigned char *wac = (unsigned char *)AM53C974_read_32(DMAWAC);
+ printk("pio mode, residual=%d\n", AM53C974_read_8(CFIREG) & CFIREG_CF);
+ while (AM53C974_read_8(CFIREG) & CFIREG_CF) *(wac++) = AM53C974_read_8(FFREG);
+ }
+
+ctcreg = AM53C974_read_8(CTCLREG) | (AM53C974_read_8(CTCMREG) << 8) |
+ (AM53C974_read_8(CTCHREG) << 16);
+
+hostdata->connected->SCp.ptr += hostdata->connected->SCp.this_residual - ctcreg;
+hostdata->connected->SCp.this_residual = ctcreg;
+}
+
+/**************************************************************************
+* Function : AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+*
+* Purpose : handle bus reset interrupt
+*
+* Inputs : instance -- which AM53C974
+*
+* Returns : nothing
+**************************************************************************/
+static void AM53C974_intr_bus_reset(struct Scsi_Host *instance)
+{
+AM53C974_local_declare();
+unsigned char cntlreg1;
+AM53C974_setio(instance);
+
+AM53C974_write_8(CMDREG, CMDREG_CFIFO);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+
+cntlreg1 = AM53C974_read_8(CNTLREG1);
+AM53C974_write_8(CNTLREG1, cntlreg1 | CNTLREG1_DISR);
+}
+
+/**************************************************************************
+* Function : int AM53C974_abort(Scsi_Cmnd *cmd)
+*
+* Purpose : abort a command
+*
+* Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+* host byte of the result field to, if zero DID_ABORTED is
+* used.
+*
+* Returns : 0 - success, -1 on failure.
+ **************************************************************************/
+int AM53C974_abort(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+struct Scsi_Host *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+Scsi_Cmnd *tmp, **prev;
+
+#ifdef AM53C974_DEBUG
+ deb_stop = 1;
+#endif
+cli();
+AM53C974_setio(instance);
+
+DEB_ABORT(printk(SEPARATOR_LINE));
+DEB_ABORT(printk("scsi%d : AM53C974_abort called -- trouble starts!!\n", instance->host_no));
+DEB_ABORT(AM53C974_print(instance));
+DEB_ABORT(AM53C974_keywait());
+
+/* Case 1 : If the command is the currently executing command,
+ we'll set the aborted flag and return control so that the
+ information transfer routine can exit cleanly. */
+if ((hostdata->connected == cmd) || (hostdata->sel_cmd == cmd)) {
+ DEB_ABORT(printk("scsi%d: aborting connected command\n", instance->host_no));
+ hostdata->aborted = 1;
+ hostdata->msgout[0] = ABORT;
+ sti();
+ return(SCSI_ABORT_PENDING); }
+
+/* Case 2 : If the command hasn't been issued yet,
+ we simply remove it from the issue queue. */
+for (prev = (Scsi_Cmnd **)&(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *)hostdata->issue_queue; tmp;
+ prev = (Scsi_Cmnd **)&(tmp->host_scribble),
+ tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+ if (cmd == tmp) {
+ DEB_ABORT(printk("scsi%d : abort removed command from issue queue.\n", instance->host_no));
+ REMOVE(5, *prev, tmp, tmp->host_scribble);
+ (*prev) = (Scsi_Cmnd *)tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ sti();
+ tmp->done(tmp);
+ return(SCSI_ABORT_SUCCESS); }
+#ifdef AM53C974_DEBUG_ABORT
+ else {
+ if (prev == (Scsi_Cmnd **)tmp)
+ printk("scsi%d : LOOP\n", instance->host_no);
+ }
+#endif
+ }
+
+/* Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail. */
+if (hostdata->connected || hostdata->sel_cmd) {
+ DEB_ABORT(printk("scsi%d : abort failed, other command connected.\n", instance->host_no));
+ sti();
+ return(SCSI_ABORT_NOT_RUNNING); }
+
+/* Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message. */
+for (tmp = (Scsi_Cmnd *)hostdata->disconnected_queue; tmp;
+ tmp = (Scsi_Cmnd *)tmp->host_scribble) {
+ if (cmd == tmp) {
+ DEB_ABORT(printk("scsi%d: aborting disconnected command\n", instance->host_no));
+ hostdata->aborted = 1;
+ hostdata->msgout[0] = ABORT;
+ hostdata->selecting = 1;
+ hostdata->sel_cmd = tmp;
+ AM53C974_write_8(CMDREG, CMDREG_DSR);
+ sti();
+ return(SCSI_ABORT_PENDING); }
+ }
+
+/* Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke. */
+DEB_ABORT(printk("scsi%d : abort failed, command not found.\n", instance->host_no));
+sti();
+return(SCSI_ABORT_NOT_RUNNING);
+}
+
+/**************************************************************************
+* Function : int AM53C974_reset(Scsi_Cmnd *cmd)
+*
+* Purpose : reset the SCSI controller and bus
+*
+* Inputs : cmd -- which command within the command block was responsible for the reset
+*
+* Returns : status (SCSI_ABORT_SUCCESS)
+**************************************************************************/
+int AM53C974_reset(Scsi_Cmnd *cmd)
+{
+AM53C974_local_declare();
+int i;
+struct Scsi_Host *instance = cmd->host;
+struct AM53C974_hostdata *hostdata = (struct AM53C974_hostdata *)instance->hostdata;
+AM53C974_setio(instance);
+
+cli();
+DEB(printk("AM53C974_reset called; "));
+
+printk("AM53C974_reset called\n");
+AM53C974_print(instance);
+AM53C974_keywait();
+
+/* do hard reset */
+AM53C974_write_8(CMDREG, CMDREG_RDEV);
+AM53C974_write_8(CMDREG, CMDREG_NOP);
+hostdata->msgout[0] = NOP;
+for (i = 0; i < 8; i++) {
+ hostdata->busy[i] = 0;
+ hostdata->sync_per[i] = DEF_STP;
+ hostdata->sync_off[i] = 0;
+ hostdata->sync_neg[i] = 0; }
+hostdata->last_message[0] = NOP;
+hostdata->sel_cmd = NULL;
+hostdata->connected = NULL;
+hostdata->issue_queue = NULL;
+hostdata->disconnected_queue = NULL;
+hostdata->in_reset = 0;
+hostdata->aborted = 0;
+hostdata->selecting = 0;
+hostdata->disconnecting = 0;
+hostdata->dma_busy = 0;
+
+/* reset bus */
+AM53C974_write_8(CNTLREG1, CNTLREG1_DISR | instance->this_id); /* disable interrupt upon SCSI RESET */
+AM53C974_write_8(CMDREG, CMDREG_RBUS); /* reset SCSI bus */
+udelay(40);
+AM53C974_config_after_reset(instance);
+
+sti();
+cmd->result = DID_RESET << 16;
+cmd->scsi_done(cmd);
+return SCSI_ABORT_SUCCESS;
+}
diff --git a/i386/i386at/gpl/linux/scsi/AM53C974.h b/i386/i386at/gpl/linux/scsi/AM53C974.h
new file mode 100644
index 00000000..2a07a5a4
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/AM53C974.h
@@ -0,0 +1,419 @@
+/* AM53/79C974 (PCscsi) driver release 0.5
+ *
+ * The architecture and much of the code of this device
+ * driver was originally developed by Drew Eckhardt for
+ * the NCR5380. The following copyrights apply:
+ * For the architecture and all parts similar to the NCR5380:
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * The AM53C974_nobios_detect code was origininally developed by
+ * Robin Cutshaw (robin@xfree86.org) and is used here in a
+ * modified form.
+ *
+ * For the other parts:
+ * Copyright 1994, D. Frieauff
+ * EMail: fri@rsx42sun0.dofn.de
+ * Phone: x49-7545-8-2256 , x49-7541-42305
+ */
+
+/*
+ * $Log: AM53C974.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:58 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:06 goel
+ * Linux driver merge.
+ *
+ */
+
+#ifndef AM53C974_H
+#define AM53C974_H
+
+#include <linux/scsicam.h>
+
+/***************************************************************************************
+* Default setting of the controller's SCSI id. Edit and uncomment this only if your *
+* BIOS does not correctly initialize the controller's SCSI id. *
+* If you don't get a warning during boot, it is correctly initialized. *
+****************************************************************************************/
+/* #define AM53C974_SCSI_ID 7 */
+
+/***************************************************************************************
+* Default settings for sync. negotiation enable, transfer rate and sync. offset. *
+* These settings can be replaced by LILO overrides (append) with the following syntax: *
+* AM53C974=host-scsi-id, target-scsi-id, max-rate, max-offset *
+* Sync. negotiation is disabled by default and will be enabled for those targets which *
+* are specified in the LILO override *
+****************************************************************************************/
+#define DEFAULT_SYNC_NEGOTIATION_ENABLED 0 /* 0 or 1 */
+#define DEFAULT_RATE 5 /* MHz, min: 3; max: 10 */
+#define DEFAULT_SYNC_OFFSET 0 /* bytes, min: 0; max: 15; use 0 for async. mode */
+
+
+/* --------------------- don't edit below here --------------------- */
+
+#define AM53C974_DRIVER_REVISION_MAJOR 0
+#define AM53C974_DRIVER_REVISION_MINOR 5
+#define SEPARATOR_LINE \
+"--------------------------------------------------------------------------\n"
+
+/* debug control */
+/* #define AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_MSG */
+/* #define AM53C974_DEBUG_KEYWAIT */
+/* #define AM53C974_DEBUG_INIT */
+/* #define AM53C974_DEBUG_QUEUE */
+/* #define AM53C974_DEBUG_INFO */
+/* #define AM53C974_DEBUG_LINKED */
+/* #define VERBOSE_AM53C974_DEBUG */
+/* #define AM53C974_DEBUG_INTR */
+/* #define AM53C974_DEB_RESEL */
+#define AM53C974_DEBUG_ABORT
+/* #define AM53C974_OPTION_DEBUG_PROBE_ONLY */
+
+/* special options/constants */
+#define DEF_CLK 40 /* chip clock freq. in MHz */
+#define MIN_PERIOD 4 /* for negotiation: min. number of clocks per cycle */
+#define MAX_PERIOD 13 /* for negotiation: max. number of clocks per cycle */
+#define MAX_OFFSET 15 /* for negotiation: max. offset (0=async) */
+
+#define DEF_SCSI_TIMEOUT 245 /* STIMREG value, 40 Mhz */
+#define DEF_STP 8 /* STPREG value assuming 5.0 MB/sec, FASTCLK, FASTSCSI */
+#define DEF_SOF_RAD 0 /* REQ/ACK deassertion delay */
+#define DEF_SOF_RAA 0 /* REQ/ACK assertion delay */
+#define DEF_ETM 0 /* CNTLREG1, ext. timing mode */
+#define DEF_PERE 1 /* CNTLREG1, parity error reporting */
+#define DEF_CLKF 0 /* CLKFREG, 0=40 Mhz */
+#define DEF_ENF 1 /* CNTLREG2, enable features */
+#define DEF_ADIDCHK 0 /* CNTLREG3, additional ID check */
+#define DEF_FASTSCSI 1 /* CNTLREG3, fast SCSI */
+#define DEF_FASTCLK 1 /* CNTLREG3, fast clocking, 5 MB/sec at 40MHz chip clk */
+#define DEF_GLITCH 1 /* CNTLREG4, glitch eater, 0=12ns, 1=35ns, 2=25ns, 3=off */
+#define DEF_PWD 0 /* CNTLREG4, reduced power feature */
+#define DEF_RAE 0 /* CNTLREG4, RAE active negation on REQ, ACK only */
+#define DEF_RADE 1 /* 1CNTLREG4, active negation on REQ, ACK and data */
+
+/*** PCI block ***/
+/* standard registers are defined in <linux/pci.h> */
+#ifndef PCI_VENDOR_ID_AMD
+#define PCI_VENDOR_ID_AMD 0x1022
+#define PCI_DEVICE_ID_AMD_SCSI 0x2020
+#endif
+#define PCI_BASE_MASK 0xFFFFFFE0
+#define PCI_COMMAND_PERREN 0x40
+#define PCI_SCRATCH_REG_0 0x40 /* 16 bits */
+#define PCI_SCRATCH_REG_1 0x42 /* 16 bits */
+#define PCI_SCRATCH_REG_2 0x44 /* 16 bits */
+#define PCI_SCRATCH_REG_3 0x46 /* 16 bits */
+#define PCI_SCRATCH_REG_4 0x48 /* 16 bits */
+#define PCI_SCRATCH_REG_5 0x4A /* 16 bits */
+#define PCI_SCRATCH_REG_6 0x4C /* 16 bits */
+#define PCI_SCRATCH_REG_7 0x4E /* 16 bits */
+
+/*** SCSI block ***/
+#define CTCLREG 0x00 /* r current transf. count, low byte */
+#define CTCMREG 0x04 /* r current transf. count, middle byte */
+#define CTCHREG 0x38 /* r current transf. count, high byte */
+#define STCLREG 0x00 /* w start transf. count, low byte */
+#define STCMREG 0x04 /* w start transf. count, middle byte */
+#define STCHREG 0x38 /* w start transf. count, high byte */
+#define FFREG 0x08 /* rw SCSI FIFO reg. */
+#define STIMREG 0x14 /* w SCSI timeout reg. */
+
+#define SDIDREG 0x10 /* w SCSI destination ID reg. */
+#define SDIREG_MASK 0x07 /* mask */
+
+#define STPREG 0x18 /* w synchronous transf. period reg. */
+#define STPREG_STP 0x1F /* synchr. transfer period */
+
+#define CLKFREG 0x24 /* w clock factor reg. */
+#define CLKFREG_MASK 0x07 /* mask */
+
+#define CMDREG 0x0C /* rw SCSI command reg. */
+#define CMDREG_DMA 0x80 /* set DMA mode (set together with opcodes below) */
+#define CMDREG_IT 0x10 /* information transfer */
+#define CMDREG_ICCS 0x11 /* initiator command complete steps */
+#define CMDREG_MA 0x12 /* message accepted */
+#define CMDREG_TPB 0x98 /* transfer pad bytes, DMA mode only */
+#define CMDREG_SATN 0x1A /* set ATN */
+#define CMDREG_RATN 0x1B /* reset ATN */
+#define CMDREG_SOAS 0x41 /* select without ATN steps */
+#define CMDREG_SAS 0x42 /* select with ATN steps (1 msg byte) */
+#define CMDREG_SASS 0x43 /* select with ATN and stop steps */
+#define CMDREG_ESR 0x44 /* enable selection/reselection */
+#define CMDREG_DSR 0x45 /* disable selection/reselection */
+#define CMDREG_SA3S 0x46 /* select with ATN 3 steps (3 msg bytes) */
+#define CMDREG_NOP 0x00 /* no operation */
+#define CMDREG_CFIFO 0x01 /* clear FIFO */
+#define CMDREG_RDEV 0x02 /* reset device */
+#define CMDREG_RBUS 0x03 /* reset SCSI bus */
+
+#define STATREG 0x10 /* r SCSI status reg. */
+#define STATREG_INT 0x80 /* SCSI interrupt condition detected */
+#define STATREG_IOE 0x40 /* SCSI illegal operation error detected */
+#define STATREG_PE 0x20 /* SCSI parity error detected */
+#define STATREG_CTZ 0x10 /* CTC reg decremented to zero */
+#define STATREG_MSG 0x04 /* SCSI MSG phase (latched?) */
+#define STATREG_CD 0x02 /* SCSI C/D phase (latched?) */
+#define STATREG_IO 0x01 /* SCSI I/O phase (latched?) */
+#define STATREG_PHASE 0x07 /* SCSI phase mask */
+
+#define INSTREG 0x14 /* r interrupt status reg. */
+#define INSTREG_SRST 0x80 /* SCSI reset detected */
+#define INSTREG_ICMD 0x40 /* SCSI invalid command detected */
+#define INSTREG_DIS 0x20 /* target disconnected or sel/resel timeout*/
+#define INSTREG_SR 0x10 /* device on bus has service request */
+#define INSTREG_SO 0x08 /* successful operation */
+#define INSTREG_RESEL 0x04 /* device reselected as initiator */
+
+#define ISREG 0x18 /* r internal state reg. */
+#define ISREG_SOF 0x08 /* synchronous offset flag (act. low) */
+#define ISREG_IS 0x07 /* status of intermediate op. */
+#define ISREG_OK_NO_STOP 0x04 /* selection successful */
+#define ISREG_OK_STOP 0x01 /* selection successful */
+
+#define CFIREG 0x1C /* r current FIFO/internal state reg. */
+#define CFIREG_IS 0xE0 /* status of intermediate op. */
+#define CFIREG_CF 0x1F /* number of bytes in SCSI FIFO */
+
+#define SOFREG 0x1C /* w synchr. offset reg. */
+#define SOFREG_RAD 0xC0 /* REQ/ACK deassertion delay (sync.) */
+#define SOFREG_RAA 0x30 /* REQ/ACK assertion delay (sync.) */
+#define SOFREG_SO 0x0F /* synch. offset (sync.) */
+
+#define CNTLREG1 0x20 /* rw control register one */
+#define CNTLREG1_ETM 0x80 /* set extended timing mode */
+#define CNTLREG1_DISR 0x40 /* disable interrupt on SCSI reset */
+#define CNTLREG1_PERE 0x10 /* enable parity error reporting */
+#define CNTLREG1_SID 0x07 /* host adapter SCSI ID */
+
+#define CNTLREG2 0x2C /* rw control register two */
+#define CNTLREG2_ENF 0x40 /* enable features */
+
+#define CNTLREG3 0x30 /* rw control register three */
+#define CNTLREG3_ADIDCHK 0x80 /* additional ID check */
+#define CNTLREG3_FASTSCSI 0x10 /* fast SCSI */
+#define CNTLREG3_FASTCLK 0x08 /* fast SCSI clocking */
+
+#define CNTLREG4 0x34 /* rw control register four */
+#define CNTLREG4_GLITCH 0xC0 /* glitch eater */
+#define CNTLREG4_PWD 0x20 /* reduced power feature */
+#define CNTLREG4_RAE 0x08 /* write only, active negot. ctrl. */
+#define CNTLREG4_RADE 0x04 /* active negot. ctrl. */
+#define CNTLREG4_RES 0x10 /* reserved bit, must be 1 */
+
+/*** DMA block ***/
+#define DMACMD 0x40 /* rw command */
+#define DMACMD_DIR 0x80 /* transfer direction (1=read from device) */
+#define DMACMD_INTE_D 0x40 /* DMA transfer interrupt enable */
+#define DMACMD_INTE_P 0x20 /* page transfer interrupt enable */
+#define DMACMD_MDL 0x10 /* map to memory descriptor list */
+#define DMACMD_DIAG 0x04 /* diagnostics, set to 0 */
+#define DMACMD_IDLE 0x00 /* idle cmd */
+#define DMACMD_BLAST 0x01 /* flush FIFO to memory */
+#define DMACMD_ABORT 0x02 /* terminate DMA */
+#define DMACMD_START 0x03 /* start DMA */
+
+#define DMASTATUS 0x54 /* r status register */
+#define DMASTATUS_BCMPLT 0x20 /* BLAST complete */
+#define DMASTATUS_SCSIINT 0x10 /* SCSI interrupt pending */
+#define DMASTATUS_DONE 0x08 /* DMA transfer terminated */
+#define DMASTATUS_ABORT 0x04 /* DMA transfer aborted */
+#define DMASTATUS_ERROR 0x02 /* DMA transfer error */
+#define DMASTATUS_PWDN 0x02 /* power down indicator */
+
+#define DMASTC 0x44 /* rw starting transfer count */
+#define DMASPA 0x48 /* rw starting physical address */
+#define DMAWBC 0x4C /* r working byte counter */
+#define DMAWAC 0x50 /* r working address counter */
+#define DMASMDLA 0x58 /* rw starting MDL address */
+#define DMAWMAC 0x5C /* r working MDL counter */
+
+/*** SCSI phases ***/
+#define PHASE_MSGIN 0x07
+#define PHASE_MSGOUT 0x06
+#define PHASE_RES_1 0x05
+#define PHASE_RES_0 0x04
+#define PHASE_STATIN 0x03
+#define PHASE_CMDOUT 0x02
+#define PHASE_DATAIN 0x01
+#define PHASE_DATAOUT 0x00
+
+struct AM53C974_hostdata {
+ volatile unsigned in_reset:1; /* flag, says bus reset pending */
+ volatile unsigned aborted:1; /* flag, says aborted */
+ volatile unsigned selecting:1; /* selection started, but not yet finished */
+ volatile unsigned disconnecting: 1; /* disconnection started, but not yet finished */
+ volatile unsigned dma_busy:1; /* dma busy when service request for info transfer received */
+ volatile unsigned char msgout[10]; /* message to output in MSGOUT_PHASE */
+ volatile unsigned char last_message[10]; /* last message OUT */
+ volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
+ volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
+ volatile Scsi_Cmnd *sel_cmd; /* command for selection */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile unsigned char busy[8]; /* index = target, bit = lun */
+ unsigned char sync_per[8]; /* synchronous transfer period (in effect) */
+ unsigned char sync_off[8]; /* synchronous offset (in effect) */
+ unsigned char sync_neg[8]; /* sync. negotiation performed (in effect) */
+ unsigned char sync_en[8]; /* sync. negotiation performed (in effect) */
+ unsigned char max_rate[8]; /* max. transfer rate (setup) */
+ unsigned char max_offset[8]; /* max. sync. offset (setup), only valid if corresponding sync_en is nonzero */
+ };
+
+#define AM53C974 { \
+ NULL, /* pointer to next in list */ \
+ NULL, /* long * usage_count */ \
+ NULL, /* struct proc_dir_entry *proc_dir */ \
+ NULL, /* int (*proc_info)(char *, char **, off_t, int, int, int); */ \
+ "AM53C974", /* name */ \
+ AM53C974_detect, /* int (* detect)(struct SHT *) */ \
+ NULL, /* int (*release)(struct Scsi_Host *) */ \
+ AM53C974_info, /* const char *(* info)(struct Scsi_Host *) */ \
+ AM53C974_command, /* int (* command)(Scsi_Cmnd *) */ \
+ AM53C974_queue_command, /* int (* queuecommand)(Scsi_Cmnd *, \
+ void (*done)(Scsi_Cmnd *)) */ \
+ AM53C974_abort, /* int (* abort)(Scsi_Cmnd *) */ \
+ AM53C974_reset, /* int (* reset)(Scsi_Cmnd *) */ \
+ NULL, /* int (* slave_attach)(int, int) */ \
+ scsicam_bios_param, /* int (* bios_param)(Disk *, int, int[]) */ \
+ 12, /* can_queue */ \
+ -1, /* this_id */ \
+ SG_ALL, /* sg_tablesize */ \
+ 1, /* cmd_per_lun */ \
+ 0, /* present, i.e. how many adapters of this kind */ \
+ 0, /* unchecked_isa_dma */ \
+ DISABLE_CLUSTERING /* use_clustering */ \
+ }
+
+void AM53C974_setup(char *str, int *ints);
+int AM53C974_detect(Scsi_Host_Template *tpnt);
+int AM53C974_biosparm(Disk *disk, int dev, int *info_array);
+const char *AM53C974_info(struct Scsi_Host *);
+int AM53C974_command(Scsi_Cmnd *SCpnt);
+int AM53C974_queue_command(Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+int AM53C974_abort(Scsi_Cmnd *cmd);
+int AM53C974_reset (Scsi_Cmnd *cmd);
+
+#define AM53C974_local_declare() unsigned long io_port
+#define AM53C974_setio(instance) io_port = instance->io_port
+#define AM53C974_read_8(addr) inb(io_port + (addr))
+#define AM53C974_write_8(addr,x) outb((x), io_port + (addr))
+#define AM53C974_read_16(addr) inw(io_port + (addr))
+#define AM53C974_write_16(addr,x) outw((x), io_port + (addr))
+#define AM53C974_read_32(addr) inl(io_port + (addr))
+#define AM53C974_write_32(addr,x) outl((x), io_port + (addr))
+
+#define AM53C974_poll_int() { do { statreg = AM53C974_read_8(STATREG); } \
+ while (!(statreg & STATREG_INT)) ; \
+ AM53C974_read_8(INSTREG) ; } /* clear int */
+#define AM53C974_cfifo() (AM53C974_read_8(CFIREG) & CFIREG_CF)
+
+/* These are "special" values for the tag parameter passed to AM53C974_select. */
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /* Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices */
+
+/************ LILO overrides *************/
+typedef struct _override_t {
+ int host_scsi_id; /* SCSI id of the bus controller */
+ int target_scsi_id; /* SCSI id of target */
+ int max_rate; /* max. transfer rate */
+ int max_offset; /* max. sync. offset, 0 = asynchronous */
+ } override_t;
+
+/************ PCI stuff *************/
+#define AM53C974_PCIREG_OPEN() outb(0xF1, 0xCF8); outb(0, 0xCFA)
+#define AM53C974_PCIREG_CLOSE() outb(0, 0xCF8)
+#define AM53C974_PCIREG_READ_BYTE(instance,a) ( inb((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_WORD(instance,a) ( inw((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_READ_DWORD(instance,a) ( inl((a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_BYTE(instance,x,a) ( outb((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_WORD(instance,x,a) ( outw((x), (a) + (instance)->io_port) )
+#define AM53C974_PCIREG_WRITE_DWORD(instance,x,a) ( outl((x), (a) + (instance)->io_port) )
+
+typedef struct _pci_config_t {
+ /* start of official PCI config space header */
+ union {
+ unsigned int device_vendor;
+ struct {
+ unsigned short vendor;
+ unsigned short device;
+ } dv;
+ } dv_id;
+#define _device_vendor dv_id.device_vendor
+#define _vendor dv_id.dv.vendor
+#define _device dv_id.dv.device
+ union {
+ unsigned int status_command;
+ struct {
+ unsigned short command;
+ unsigned short status;
+ } sc;
+ } stat_cmd;
+#define _status_command stat_cmd.status_command
+#define _command stat_cmd.sc.command
+#define _status stat_cmd.sc.status
+ union {
+ unsigned int class_revision;
+ struct {
+ unsigned char rev_id;
+ unsigned char prog_if;
+ unsigned char sub_class;
+ unsigned char base_class;
+ } cr;
+ } class_rev;
+#define _class_revision class_rev.class_revision
+#define _rev_id class_rev.cr.rev_id
+#define _prog_if class_rev.cr.prog_if
+#define _sub_class class_rev.cr.sub_class
+#define _base_class class_rev.cr.base_class
+ union {
+ unsigned int bist_header_latency_cache;
+ struct {
+ unsigned char cache_line_size;
+ unsigned char latency_timer;
+ unsigned char header_type;
+ unsigned char bist;
+ } bhlc;
+ } bhlc;
+#define _bist_header_latency_cache bhlc.bist_header_latency_cache
+#define _cache_line_size bhlc.bhlc.cache_line_size
+#define _latency_timer bhlc.bhlc.latency_timer
+#define _header_type bhlc.bhlc.header_type
+#define _bist bhlc.bhlc.bist
+ unsigned int _base0;
+ unsigned int _base1;
+ unsigned int _base2;
+ unsigned int _base3;
+ unsigned int _base4;
+ unsigned int _base5;
+ unsigned int rsvd1;
+ unsigned int rsvd2;
+ unsigned int _baserom;
+ unsigned int rsvd3;
+ unsigned int rsvd4;
+ union {
+ unsigned int max_min_ipin_iline;
+ struct {
+ unsigned char int_line;
+ unsigned char int_pin;
+ unsigned char min_gnt;
+ unsigned char max_lat;
+ } mmii;
+ } mmii;
+#define _max_min_ipin_iline mmii.max_min_ipin_iline
+#define _int_line mmii.mmii.int_line
+#define _int_pin mmii.mmii.int_pin
+#define _min_gnt mmii.mmii.min_gnt
+#define _max_lat mmii.mmii.max_lat
+ /* end of official PCI config space header */
+ unsigned short _ioaddr; /* config type 1 - private I/O addr */
+ unsigned int _pcibus; /* config type 2 - private bus id */
+ unsigned int _cardnum; /* config type 2 - private card number */
+} pci_config_t;
+
+#endif /* AM53C974_H */
diff --git a/i386/i386at/gpl/linux/scsi/BusLogic.c b/i386/i386at/gpl/linux/scsi/BusLogic.c
new file mode 100644
index 00000000..7472dc3d
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/BusLogic.c
@@ -0,0 +1,2779 @@
+/*
+
+ Linux Driver for BusLogic MultiMaster SCSI Host Adapters
+
+ Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ This program is free software; you may redistribute and/or modify it under
+ the terms of the GNU General Public License Version 2 as published by the
+ Free Software Foundation, provided that none of the source code or runtime
+ copyright notices are removed or modified.
+
+ 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 complete details.
+
+ The author respectfully requests that all modifications to this software be
+ sent directly to him for evaluation and testing.
+
+ Special thanks to Alex T. Win of BusLogic, whose advice has been invaluable,
+ to David B. Gentzel, for writing the original Linux BusLogic driver, and to
+ Paul Gortmaker, for being such a dedicated test site.
+
+*/
+
+
+#define BusLogic_DriverVersion "1.3.1"
+#define BusLogic_DriverDate "31 December 1995"
+
+
+#include <linux/module.h>
+#include <linux/config.h>
+#include <linux/types.h>
+#include <linux/blkdev.h>
+#include <linux/delay.h>
+#include <linux/ioport.h>
+#include <linux/kernel_stat.h>
+#include <linux/mm.h>
+#include <linux/sched.h>
+#include <linux/stat.h>
+#include <linux/pci.h>
+#include <linux/bios32.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "BusLogic.h"
+
+
+/*
+ BusLogic_CommandLineEntryCount is a count of the number of "BusLogic="
+ entries provided on the Linux Kernel Command Line.
+*/
+
+static int
+ BusLogic_CommandLineEntryCount = 0;
+
+
+/*
+ BusLogic_CommandLineEntries is an array of Command Line Entry structures
+ representing the "BusLogic=" entries provided on the Linux Kernel Command
+ Line.
+*/
+
+static BusLogic_CommandLineEntry_T
+ BusLogic_CommandLineEntries[BusLogic_MaxHostAdapters];
+
+
+/*
+ BusLogic_GlobalOptions is a bit mask of Global Options to be applied
+ across all Host Adapters.
+*/
+
+static int
+ BusLogic_GlobalOptions = 0;
+
+
+/*
+ BusLogic_RegisteredHostAdapters is a linked list of all the registered
+ BusLogic Host Adapters.
+*/
+
+static BusLogic_HostAdapter_T
+ *BusLogic_RegisteredHostAdapters = NULL;
+
+
+/*
+ BusLogic_Standard_IO_Addresses is the list of standard I/O Addresses at which
+ BusLogic Host Adapters may potentially be found.
+*/
+
+static unsigned short
+ BusLogic_IO_StandardAddresses[] =
+ { 0x330, 0x334, 0x230, 0x234, 0x130, 0x134, 0 };
+
+
+/*
+ BusLogic_IO_AddressProbeList is the list of I/O Addresses to be probed for
+ potential BusLogic Host Adapters. It is initialized by interrogating the
+ PCI Configuration Space on PCI machines as well as from the list of
+ standard BusLogic I/O Addresses.
+*/
+
+static unsigned short
+ BusLogic_IO_AddressProbeList[BusLogic_IO_MaxProbeAddresses+1] = { 0 };
+
+
+/*
+ BusLogic_IRQ_UsageCount stores a count of the number of Host Adapters using
+ a given IRQ Channel, which is necessary to support PCI, EISA, or MCA shared
+ interrupts. Only IRQ Channels 9, 10, 11, 12, 14, and 15 are supported by
+ BusLogic Host Adapters.
+*/
+
+static short
+ BusLogic_IRQ_UsageCount[7] = { 0 };
+
+
+/*
+ BusLogic_CommandFailureReason holds a string identifying the reason why a
+ call to BusLogic_Command failed. It is only valid when BusLogic_Command
+ returns a failure code.
+*/
+
+static char
+ *BusLogic_CommandFailureReason;
+
+
+/*
+ BusLogic_ProcDirectoryEntry is the BusLogic /proc/scsi directory entry.
+*/
+
+static struct proc_dir_entry
+ BusLogic_ProcDirectoryEntry =
+ { PROC_SCSI_BUSLOGIC, 8, "BusLogic", S_IFDIR | S_IRUGO | S_IXUGO, 2 };
+
+
+/*
+ BusLogic_AnnounceDriver announces the Driver Version and Date, Author's
+ Name, Copyright Notice, and Contact Address.
+*/
+
+static void BusLogic_AnnounceDriver(void)
+{
+ static boolean DriverAnnouncementPrinted = false;
+ if (DriverAnnouncementPrinted) return;
+ printk("scsi: ***** BusLogic SCSI Driver Version "
+ BusLogic_DriverVersion " of " BusLogic_DriverDate " *****\n");
+ printk("scsi: Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>\n");
+ DriverAnnouncementPrinted = true;
+}
+
+
+/*
+ BusLogic_DriverInfo returns the Board Name to identify this SCSI Driver
+ and Host Adapter.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *Host)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Host->hostdata;
+ return HostAdapter->BoardName;
+}
+
+
+/*
+ BusLogic_InitializeAddressProbeList initializes the list of I/O Addresses
+ to be probed for potential BusLogic SCSI Host Adapters by interrogating the
+ PCI Configuration Space on PCI machines as well as from the list of standard
+ BusLogic I/O Addresses.
+*/
+
+static void BusLogic_InitializeAddressProbeList(void)
+{
+ int DestinationIndex = 0, SourceIndex = 0;
+ /*
+ If BusLogic_Setup has been called, do not override the Kernel Command
+ Line specifications.
+ */
+ if (BusLogic_IO_AddressProbeList[0] != 0) return;
+#ifdef CONFIG_PCI
+ /*
+ Interrogate PCI Configuration Space for any BusLogic SCSI Host Adapters.
+ */
+ if (pcibios_present())
+ {
+ unsigned short Index = 0, VendorID;
+ unsigned char Bus, DeviceAndFunction;
+ unsigned int BaseAddress0;
+ while (pcibios_find_class(PCI_CLASS_STORAGE_SCSI<<8, Index++,
+ &Bus, &DeviceAndFunction) == 0)
+ if (pcibios_read_config_word(Bus, DeviceAndFunction,
+ PCI_VENDOR_ID, &VendorID) == 0 &&
+ VendorID == PCI_VENDOR_ID_BUSLOGIC &&
+ pcibios_read_config_dword(Bus, DeviceAndFunction,
+ PCI_BASE_ADDRESS_0, &BaseAddress0) == 0 &&
+ (BaseAddress0 & PCI_BASE_ADDRESS_SPACE) ==
+ PCI_BASE_ADDRESS_SPACE_IO)
+ {
+ BusLogic_IO_AddressProbeList[DestinationIndex++] =
+ BaseAddress0 & PCI_BASE_ADDRESS_IO_MASK;
+ }
+ }
+#endif
+ /*
+ Append the list of standard BusLogic I/O Addresses.
+ */
+ while (DestinationIndex < BusLogic_IO_MaxProbeAddresses &&
+ BusLogic_IO_StandardAddresses[SourceIndex] > 0)
+ BusLogic_IO_AddressProbeList[DestinationIndex++] =
+ BusLogic_IO_StandardAddresses[SourceIndex++];
+ BusLogic_IO_AddressProbeList[DestinationIndex] = 0;
+}
+
+
+/*
+ BusLogic_RegisterHostAdapter adds Host Adapter to the list of registered
+ BusLogic Host Adapters.
+*/
+
+static void BusLogic_RegisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ HostAdapter->Next = NULL;
+ if (BusLogic_RegisteredHostAdapters != NULL)
+ {
+ BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+ BusLogic_HostAdapter_T *NextHostAdapter;
+ while ((NextHostAdapter = LastHostAdapter->Next) != NULL)
+ LastHostAdapter = NextHostAdapter;
+ LastHostAdapter->Next = HostAdapter;
+ }
+ else BusLogic_RegisteredHostAdapters = HostAdapter;
+}
+
+
+/*
+ BusLogic_UnregisterHostAdapter removes Host Adapter from the list of
+ registered BusLogic Host Adapters.
+*/
+
+static void BusLogic_UnregisterHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ if (BusLogic_RegisteredHostAdapters != HostAdapter)
+ {
+ BusLogic_HostAdapter_T *LastHostAdapter = BusLogic_RegisteredHostAdapters;
+ while (LastHostAdapter != NULL && LastHostAdapter->Next != HostAdapter)
+ LastHostAdapter = LastHostAdapter->Next;
+ if (LastHostAdapter != NULL)
+ LastHostAdapter->Next = HostAdapter->Next;
+ }
+ else BusLogic_RegisteredHostAdapters = HostAdapter->Next;
+ HostAdapter->Next = NULL;
+}
+
+
+/*
+ BusLogic_CreateCCBs allocates the initial Command Control Blocks (CCBs)
+ for Host Adapter.
+*/
+
+static boolean BusLogic_CreateCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+ int i;
+ for (i = 0; i < BusLogic_InitialCCBs; i++)
+ {
+ BusLogic_CCB_T *CCB = (BusLogic_CCB_T *)
+ scsi_init_malloc(sizeof(BusLogic_CCB_T), GFP_ATOMIC | GFP_DMA);
+ if (CCB == NULL)
+ {
+ printk("scsi%d: UNABLE TO ALLOCATE CCB %d - DETACHING\n",
+ HostAdapter->HostNumber, i);
+ return false;
+ }
+ memset(CCB, 0, sizeof(BusLogic_CCB_T));
+ CCB->HostAdapter = HostAdapter;
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->Next = HostAdapter->Free_CCBs;
+ CCB->NextAll = HostAdapter->All_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+ HostAdapter->All_CCBs = CCB;
+ }
+ return true;
+}
+
+
+/*
+ BusLogic_DestroyCCBs deallocates the CCBs for Host Adapter.
+*/
+
+static void BusLogic_DestroyCCBs(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_CCB_T *NextCCB = HostAdapter->All_CCBs, *CCB;
+ HostAdapter->All_CCBs = NULL;
+ HostAdapter->Free_CCBs = NULL;
+ while ((CCB = NextCCB) != NULL)
+ {
+ NextCCB = CCB->NextAll;
+ scsi_init_free((char *) CCB, sizeof(BusLogic_CCB_T));
+ }
+}
+
+
+/*
+ BusLogic_AllocateCCB allocates a CCB from the Host Adapter's free list,
+ allocating more memory from the Kernel if necessary.
+*/
+
+static BusLogic_CCB_T *BusLogic_AllocateCCB(BusLogic_HostAdapter_T *HostAdapter)
+{
+ static unsigned int SerialNumber = 0;
+ BusLogic_CCB_T *CCB;
+ BusLogic_LockHostAdapter(HostAdapter);
+ CCB = HostAdapter->Free_CCBs;
+ if (CCB != NULL)
+ {
+ CCB->SerialNumber = ++SerialNumber;
+ HostAdapter->Free_CCBs = CCB->Next;
+ CCB->Next = NULL;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return CCB;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ CCB = (BusLogic_CCB_T *) scsi_init_malloc(sizeof(BusLogic_CCB_T),
+ GFP_ATOMIC | GFP_DMA);
+ if (CCB == NULL)
+ {
+ printk("scsi%d: Failed to allocate an additional CCB\n",
+ HostAdapter->HostNumber);
+ return NULL;
+ }
+ printk("scsi%d: Allocated an additional CCB\n", HostAdapter->HostNumber);
+ memset(CCB, 0, sizeof(BusLogic_CCB_T));
+ CCB->HostAdapter = HostAdapter;
+ CCB->Status = BusLogic_CCB_Free;
+ BusLogic_LockHostAdapter(HostAdapter);
+ CCB->SerialNumber = ++SerialNumber;
+ CCB->NextAll = HostAdapter->All_CCBs;
+ HostAdapter->All_CCBs = CCB;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return CCB;
+}
+
+
+/*
+ BusLogic_DeallocateCCB deallocates a CCB, returning it to the Host Adapter's
+ free list.
+*/
+
+static void BusLogic_DeallocateCCB(BusLogic_CCB_T *CCB)
+{
+ BusLogic_HostAdapter_T *HostAdapter = CCB->HostAdapter;
+ BusLogic_LockHostAdapter(HostAdapter);
+ CCB->Command = NULL;
+ CCB->Status = BusLogic_CCB_Free;
+ CCB->Next = HostAdapter->Free_CCBs;
+ HostAdapter->Free_CCBs = CCB;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+}
+
+
+/*
+ BusLogic_Command sends the command OperationCode to HostAdapter, optionally
+ providing ParameterLength bytes of ParameterData and receiving at most
+ ReplyLength bytes of ReplyData; any excess reply data is received but
+ discarded.
+
+ On success, this function returns the number of reply bytes read from
+ the Host Adapter (including any discarded data); on failure, it returns
+ -1 if the command was invalid, or -2 if a timeout occurred.
+
+ This function is only called during board detection and initialization, so
+ performance and latency are not critical, and exclusive access to the Host
+ Adapter hardware is assumed. Once the board and driver are initialized, the
+ only Host Adapter command that is issued is the single byte Start Mailbox
+ Scan command, which does not require waiting for the Host Adapter Ready bit
+ to be set in the Status Register.
+*/
+
+static int BusLogic_Command(BusLogic_HostAdapter_T *HostAdapter,
+ BusLogic_OperationCode_T OperationCode,
+ void *ParameterData,
+ int ParameterLength,
+ void *ReplyData,
+ int ReplyLength)
+{
+ unsigned char *ParameterPointer = (unsigned char *) ParameterData;
+ unsigned char *ReplyPointer = (unsigned char *) ReplyData;
+ unsigned char StatusRegister = 0, InterruptRegister;
+ long TimeoutCounter;
+ int ReplyBytes = 0;
+ /*
+ Clear out the Reply Data if provided.
+ */
+ if (ReplyLength > 0)
+ memset(ReplyData, 0, ReplyLength);
+ /*
+ Wait for the Host Adapter Ready bit to be set and the Command/Parameter
+ Register Busy bit to be reset in the Status Register.
+ */
+ TimeoutCounter = loops_per_sec >> 3;
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if ((StatusRegister & BusLogic_HostAdapterReady) &&
+ !(StatusRegister & BusLogic_CommandParameterRegisterBusy))
+ break;
+ }
+ BusLogic_CommandFailureReason = "Timeout waiting for Host Adapter Ready";
+ if (TimeoutCounter < 0) return -2;
+ /*
+ Write the OperationCode to the Command/Parameter Register.
+ */
+ HostAdapter->HostAdapterCommandCompleted = false;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, OperationCode);
+ /*
+ Write any additional Parameter Bytes.
+ */
+ TimeoutCounter = 10000;
+ while (ParameterLength > 0 && --TimeoutCounter >= 0)
+ {
+ /*
+ Wait 100 microseconds to give the Host Adapter enough time to determine
+ whether the last value written to the Command/Parameter Register was
+ valid or not. If the Command Complete bit is set in the Interrupt
+ Register, then the Command Invalid bit in the Status Register will be
+ reset if the Operation Code or Parameter was valid and the command
+ has completed, or set if the Operation Code or Parameter was invalid.
+ If the Data In Register Ready bit is set in the Status Register, then
+ the Operation Code was valid, and data is waiting to be read back
+ from the Host Adapter. Otherwise, wait for the Command/Parameter
+ Register Busy bit in the Status Register to be reset.
+ */
+ udelay(100);
+ InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister & BusLogic_CommandComplete) break;
+ if (HostAdapter->HostAdapterCommandCompleted) break;
+ if (StatusRegister & BusLogic_DataInRegisterReady) break;
+ if (StatusRegister & BusLogic_CommandParameterRegisterBusy) continue;
+ BusLogic_WriteCommandParameterRegister(HostAdapter, *ParameterPointer++);
+ ParameterLength--;
+ }
+ BusLogic_CommandFailureReason = "Timeout waiting for Parameter Acceptance";
+ if (TimeoutCounter < 0) return -2;
+ /*
+ The Modify I/O Address command does not cause a Command Complete Interrupt.
+ */
+ if (OperationCode == BusLogic_ModifyIOAddress)
+ {
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ BusLogic_CommandFailureReason = "Modify I/O Address Invalid";
+ if (StatusRegister & BusLogic_CommandInvalid) return -1;
+ BusLogic_CommandFailureReason = NULL;
+ return 0;
+ }
+ /*
+ Select an appropriate timeout value for awaiting command completion.
+ */
+ switch (OperationCode)
+ {
+ case BusLogic_InquireInstalledDevicesID0to7:
+ case BusLogic_InquireInstalledDevicesID8to15:
+ /* Approximately 60 seconds. */
+ TimeoutCounter = loops_per_sec << 2;
+ break;
+ default:
+ /* Approximately 1 second. */
+ TimeoutCounter = loops_per_sec >> 4;
+ break;
+ }
+ /*
+ Receive any Reply Bytes, waiting for either the Command Complete bit to
+ be set in the Interrupt Register, or for the Interrupt Handler to set the
+ Host Adapter Command Completed bit in the Host Adapter structure.
+ */
+ while (--TimeoutCounter >= 0)
+ {
+ InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (InterruptRegister & BusLogic_CommandComplete) break;
+ if (HostAdapter->HostAdapterCommandCompleted) break;
+ if (StatusRegister & BusLogic_DataInRegisterReady)
+ if (++ReplyBytes <= ReplyLength)
+ *ReplyPointer++ = BusLogic_ReadDataInRegister(HostAdapter);
+ else BusLogic_ReadDataInRegister(HostAdapter);
+ }
+ BusLogic_CommandFailureReason = "Timeout waiting for Command Complete";
+ if (TimeoutCounter < 0) return -2;
+ /*
+ If testing Command Complete Interrupts, wait a short while in case the
+ loop immediately above terminated due to the Command Complete bit being
+ set in the Interrupt Register, but the interrupt hasn't actually been
+ processed yet. Otherwise, acknowledging the interrupt here could prevent
+ the interrupt test from succeeding.
+ */
+ if (OperationCode == BusLogic_TestCommandCompleteInterrupt)
+ udelay(10000);
+ /*
+ Clear any pending Command Complete Interrupt.
+ */
+ BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+ if (BusLogic_GlobalOptions & BusLogic_TraceConfiguration)
+ if (OperationCode != BusLogic_TestCommandCompleteInterrupt)
+ {
+ int i;
+ printk("BusLogic_Command(%02X) Status = %02X: %2d ==> %2d:",
+ OperationCode, StatusRegister, ReplyLength, ReplyBytes);
+ if (ReplyLength > ReplyBytes) ReplyLength = ReplyBytes;
+ for (i = 0; i < ReplyLength; i++)
+ printk(" %02X", ((unsigned char *) ReplyData)[i]);
+ printk("\n");
+ }
+ /*
+ Process Command Invalid conditions.
+ */
+ if (StatusRegister & BusLogic_CommandInvalid)
+ {
+ /*
+ Some early BusLogic Host Adapters may not recover properly from
+ a Command Invalid condition, so if this appears to be the case,
+ a Soft Reset is issued to the Host Adapter. Potentially invalid
+ commands are never attempted after Mailbox Initialization is
+ performed, so there should be no Host Adapter state lost by a
+ Soft Reset in response to a Command Invalid condition.
+ */
+ udelay(1000);
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister != (BusLogic_HostAdapterReady |
+ BusLogic_InitializationRequired))
+ {
+ BusLogic_WriteControlRegister(HostAdapter, BusLogic_SoftReset);
+ udelay(1000);
+ }
+ BusLogic_CommandFailureReason = "Command Invalid";
+ return -1;
+ }
+ /*
+ Handle Excess Parameters Supplied conditions.
+ */
+ BusLogic_CommandFailureReason = "Excess Parameters Supplied";
+ if (ParameterLength > 0) return -1;
+ /*
+ Indicate the command completed successfully.
+ */
+ BusLogic_CommandFailureReason = NULL;
+ return ReplyBytes;
+}
+
+
+/*
+ BusLogic_Failure prints a standardized error message, and then returns false.
+*/
+
+static boolean BusLogic_Failure(BusLogic_HostAdapter_T *HostAdapter,
+ char *ErrorMessage)
+{
+ BusLogic_AnnounceDriver();
+ printk("While configuring BusLogic Host Adapter at I/O Address 0x%X:\n",
+ HostAdapter->IO_Address);
+ printk("%s FAILED - DETACHING\n", ErrorMessage);
+ if (BusLogic_CommandFailureReason != NULL)
+ printk("ADDITIONAL FAILURE INFO - %s\n", BusLogic_CommandFailureReason);
+ return false;
+}
+
+
+/*
+ BusLogic_ProbeHostAdapter probes for a BusLogic Host Adapter.
+*/
+
+static boolean BusLogic_ProbeHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ boolean TraceProbe = (BusLogic_GlobalOptions & BusLogic_TraceProbe);
+ unsigned char StatusRegister, GeometryRegister;
+ /*
+ Read the Status Register to test if there is an I/O port that responds. A
+ nonexistent I/O port will return 0xFF, in which case there is definitely no
+ BusLogic Host Adapter at this base I/O Address.
+ */
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (TraceProbe)
+ printk("BusLogic_Probe(0x%X): Status 0x%02X\n",
+ HostAdapter->IO_Address, StatusRegister);
+ if (StatusRegister == 0xFF) return false;
+ /*
+ Read the undocumented BusLogic Geometry Register to test if there is an I/O
+ port that responds. Adaptec Host Adapters do not implement the Geometry
+ Register, so this test helps serve to avoid incorrectly recognizing an
+ Adaptec 1542A or 1542B as a BusLogic. Unfortunately, the Adaptec 1542C
+ series does respond to the Geometry Register I/O port, but it will be
+ rejected later when the Inquire Extended Setup Information command is
+ issued in BusLogic_CheckHostAdapter. The AMI FastDisk Host Adapter is a
+ BusLogic clone that implements the same interface as earlier BusLogic
+ boards, including the undocumented commands, and is therefore supported by
+ this driver. However, the AMI FastDisk always returns 0x00 upon reading
+ the Geometry Register, so the extended translation option should always be
+ left disabled on the AMI FastDisk.
+ */
+ GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
+ if (TraceProbe)
+ printk("BusLogic_Probe(0x%X): Geometry 0x%02X\n",
+ HostAdapter->IO_Address, GeometryRegister);
+ if (GeometryRegister == 0xFF) return false;
+ /*
+ Indicate the Host Adapter Probe completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_HardResetHostAdapter issues a Hard Reset to the Host Adapter,
+ and waits for Host Adapter Diagnostics to complete.
+*/
+
+static boolean BusLogic_HardResetHostAdapter(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ boolean TraceHardReset = (BusLogic_GlobalOptions & BusLogic_TraceHardReset);
+ long TimeoutCounter = loops_per_sec >> 2;
+ unsigned char StatusRegister = 0;
+ /*
+ Issue a Hard Reset Command to the Host Adapter. The Host Adapter should
+ respond by setting Diagnostic Active in the Status Register.
+ */
+ BusLogic_WriteControlRegister(HostAdapter, BusLogic_HardReset);
+ /*
+ Wait until Diagnostic Active is set in the Status Register.
+ */
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if ((StatusRegister & BusLogic_DiagnosticActive)) break;
+ }
+ if (TraceHardReset)
+ printk("BusLogic_HardReset(0x%X): Diagnostic Active, Status 0x%02X\n",
+ HostAdapter->IO_Address, StatusRegister);
+ if (TimeoutCounter < 0) return false;
+ /*
+ Wait 100 microseconds to allow completion of any initial diagnostic
+ activity which might leave the contents of the Status Register
+ unpredictable.
+ */
+ udelay(100);
+ /*
+ Wait until Diagnostic Active is reset in the Status Register.
+ */
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (!(StatusRegister & BusLogic_DiagnosticActive)) break;
+ }
+ if (TraceHardReset)
+ printk("BusLogic_HardReset(0x%X): Diagnostic Completed, Status 0x%02X\n",
+ HostAdapter->IO_Address, StatusRegister);
+ if (TimeoutCounter < 0) return false;
+ /*
+ Wait until at least one of the Diagnostic Failure, Host Adapter Ready,
+ or Data In Register Ready bits is set in the Status Register.
+ */
+ while (--TimeoutCounter >= 0)
+ {
+ StatusRegister = BusLogic_ReadStatusRegister(HostAdapter);
+ if (StatusRegister & (BusLogic_DiagnosticFailure |
+ BusLogic_HostAdapterReady |
+ BusLogic_DataInRegisterReady))
+ break;
+ }
+ if (TraceHardReset)
+ printk("BusLogic_HardReset(0x%X): Host Adapter Ready, Status 0x%02X\n",
+ HostAdapter->IO_Address, StatusRegister);
+ if (TimeoutCounter < 0) return false;
+ /*
+ If Diagnostic Failure is set or Host Adapter Ready is reset, then an
+ error occurred during the Host Adapter diagnostics. If Data In Register
+ Ready is set, then there is an Error Code available.
+ */
+ if ((StatusRegister & BusLogic_DiagnosticFailure) ||
+ !(StatusRegister & BusLogic_HostAdapterReady))
+ {
+ BusLogic_CommandFailureReason = NULL;
+ BusLogic_Failure(HostAdapter, "HARD RESET DIAGNOSTICS");
+ printk("HOST ADAPTER STATUS REGISTER = %02X\n", StatusRegister);
+ if (StatusRegister & BusLogic_DataInRegisterReady)
+ {
+ unsigned char ErrorCode = BusLogic_ReadDataInRegister(HostAdapter);
+ printk("HOST ADAPTER ERROR CODE = %d\n", ErrorCode);
+ }
+ return false;
+ }
+ /*
+ Indicate the Host Adapter Hard Reset completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_CheckHostAdapter checks to be sure this really is a BusLogic
+ Host Adapter.
+*/
+
+static boolean BusLogic_CheckHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+ BusLogic_RequestedReplyLength_T RequestedReplyLength;
+ unsigned long ProcessorFlags;
+ int Result;
+ /*
+ Issue the Inquire Extended Setup Information command. Only genuine
+ BusLogic Host Adapters and true clones support this command. Adaptec 1542C
+ series Host Adapters that respond to the Geometry Register I/O port will
+ fail this command. Interrupts must be disabled around the call to
+ BusLogic_Command since a Command Complete interrupt could occur if the IRQ
+ Channel was previously enabled for another BusLogic Host Adapter sharing
+ the same IRQ Channel.
+ */
+ save_flags(ProcessorFlags);
+ cli();
+ RequestedReplyLength = sizeof(ExtendedSetupInformation);
+ Result = BusLogic_Command(HostAdapter,
+ BusLogic_InquireExtendedSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &ExtendedSetupInformation,
+ sizeof(ExtendedSetupInformation));
+ restore_flags(ProcessorFlags);
+ if (BusLogic_GlobalOptions & BusLogic_TraceProbe)
+ printk("BusLogic_Check(0x%X): Result %d\n",
+ HostAdapter->IO_Address, Result);
+ return (Result == sizeof(ExtendedSetupInformation));
+}
+
+
+/*
+ BusLogic_ReadHostAdapterConfiguration reads the Configuration Information
+ from Host Adapter.
+*/
+
+static boolean BusLogic_ReadHostAdapterConfiguration(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_BoardID_T BoardID;
+ BusLogic_Configuration_T Configuration;
+ BusLogic_SetupInformation_T SetupInformation;
+ BusLogic_ExtendedSetupInformation_T ExtendedSetupInformation;
+ BusLogic_BoardModelNumber_T BoardModelNumber;
+ BusLogic_FirmwareVersion3rdDigit_T FirmwareVersion3rdDigit;
+ BusLogic_FirmwareVersionLetter_T FirmwareVersionLetter;
+ BusLogic_RequestedReplyLength_T RequestedReplyLength;
+ unsigned char GeometryRegister, *TargetPointer, Character;
+ unsigned short AllTargetsMask, DisconnectPermitted;
+ unsigned short TaggedQueuingPermitted, TaggedQueuingPermittedDefault;
+ boolean CommonErrorRecovery;
+ int TargetID, i;
+ /*
+ Issue the Inquire Board ID command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardID, NULL, 0,
+ &BoardID, sizeof(BoardID)) != sizeof(BoardID))
+ return BusLogic_Failure(HostAdapter, "INQUIRE BOARD ID");
+ /*
+ Issue the Inquire Configuration command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireConfiguration, NULL, 0,
+ &Configuration, sizeof(Configuration))
+ != sizeof(Configuration))
+ return BusLogic_Failure(HostAdapter, "INQUIRE CONFIGURATION");
+ /*
+ Issue the Inquire Setup Information command.
+ */
+ RequestedReplyLength = sizeof(SetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &SetupInformation, sizeof(SetupInformation))
+ != sizeof(SetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ /*
+ Issue the Inquire Extended Setup Information command.
+ */
+ RequestedReplyLength = sizeof(ExtendedSetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireExtendedSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &ExtendedSetupInformation,
+ sizeof(ExtendedSetupInformation))
+ != sizeof(ExtendedSetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE EXTENDED SETUP INFORMATION");
+ /*
+ Issue the Inquire Board Model Number command.
+ */
+ if (!(BoardID.FirmwareVersion1stDigit == '2' &&
+ ExtendedSetupInformation.BusType == 'A'))
+ {
+ RequestedReplyLength = sizeof(BoardModelNumber);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireBoardModelNumber,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &BoardModelNumber, sizeof(BoardModelNumber))
+ != sizeof(BoardModelNumber))
+ return BusLogic_Failure(HostAdapter, "INQUIRE BOARD MODEL NUMBER");
+ }
+ else strcpy(BoardModelNumber, "542B");
+ /*
+ Issue the Inquire Firmware Version 3rd Digit command.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersion3rdDigit,
+ NULL, 0, &FirmwareVersion3rdDigit,
+ sizeof(FirmwareVersion3rdDigit))
+ != sizeof(FirmwareVersion3rdDigit))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE 3RD DIGIT");
+ /*
+ Issue the Inquire Firmware Version Letter command.
+ */
+ FirmwareVersionLetter = '\0';
+ if (BoardID.FirmwareVersion1stDigit > '3' ||
+ (BoardID.FirmwareVersion1stDigit == '3' &&
+ BoardID.FirmwareVersion2ndDigit >= '3'))
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireFirmwareVersionLetter,
+ NULL, 0, &FirmwareVersionLetter,
+ sizeof(FirmwareVersionLetter))
+ != sizeof(FirmwareVersionLetter))
+ return BusLogic_Failure(HostAdapter, "INQUIRE FIRMWARE VERSION LETTER");
+ /*
+ BusLogic Host Adapters can be identified by their model number and
+ the major version number of their firmware as follows:
+
+ 4.xx BusLogic "C" Series Host Adapters:
+ BT-946C/956C/956CD/747C/757C/757CD/445C/545C/540CF
+ 3.xx BusLogic "S" Series Host Adapters:
+ BT-747S/747D/757S/757D/445S/545S/542D
+ BT-542B/742A (revision H)
+ 2.xx BusLogic "A" Series Host Adapters:
+ BT-542B/742A (revision G and below)
+ 0.xx AMI FastDisk VLB/EISA BusLogic Clone Host Adapter
+ */
+ /*
+ Save the Model Name and Board Name in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->ModelName;
+ *TargetPointer++ = 'B';
+ *TargetPointer++ = 'T';
+ *TargetPointer++ = '-';
+ for (i = 0; i < sizeof(BoardModelNumber); i++)
+ {
+ Character = BoardModelNumber[i];
+ if (Character == ' ' || Character == '\0') break;
+ *TargetPointer++ = Character;
+ }
+ *TargetPointer++ = '\0';
+ strcpy(HostAdapter->BoardName, "BusLogic ");
+ strcat(HostAdapter->BoardName, HostAdapter->ModelName);
+ strcpy(HostAdapter->InterruptLabel, HostAdapter->BoardName);
+ /*
+ Save the Firmware Version in the Host Adapter structure.
+ */
+ TargetPointer = HostAdapter->FirmwareVersion;
+ *TargetPointer++ = BoardID.FirmwareVersion1stDigit;
+ *TargetPointer++ = '.';
+ *TargetPointer++ = BoardID.FirmwareVersion2ndDigit;
+ if (FirmwareVersion3rdDigit != ' ' && FirmwareVersion3rdDigit != '\0')
+ *TargetPointer++ = FirmwareVersion3rdDigit;
+ if (FirmwareVersionLetter != ' ' && FirmwareVersionLetter != '\0')
+ *TargetPointer++ = FirmwareVersionLetter;
+ *TargetPointer++ = '\0';
+ /*
+ Determine the IRQ Channel and save it in the Host Adapter structure.
+ */
+ if (Configuration.IRQ_Channel9)
+ HostAdapter->IRQ_Channel = 9;
+ else if (Configuration.IRQ_Channel10)
+ HostAdapter->IRQ_Channel = 10;
+ else if (Configuration.IRQ_Channel11)
+ HostAdapter->IRQ_Channel = 11;
+ else if (Configuration.IRQ_Channel12)
+ HostAdapter->IRQ_Channel = 12;
+ else if (Configuration.IRQ_Channel14)
+ HostAdapter->IRQ_Channel = 14;
+ else if (Configuration.IRQ_Channel15)
+ HostAdapter->IRQ_Channel = 15;
+ /*
+ Determine the DMA Channel and save it in the Host Adapter structure.
+ */
+ if (Configuration.DMA_Channel5)
+ HostAdapter->DMA_Channel = 5;
+ else if (Configuration.DMA_Channel6)
+ HostAdapter->DMA_Channel = 6;
+ else if (Configuration.DMA_Channel7)
+ HostAdapter->DMA_Channel = 7;
+ /*
+ Save the Host Adapter SCSI ID in the Host Adapter structure.
+ */
+ HostAdapter->SCSI_ID = Configuration.HostAdapterID;
+ /*
+ Save the Synchronous Initiation flag and SCSI Parity Checking flag
+ in the Host Adapter structure.
+ */
+ HostAdapter->SynchronousInitiation =
+ SetupInformation.SynchronousInitiationEnabled;
+ HostAdapter->ParityChecking = SetupInformation.ParityCheckEnabled;
+ /*
+ Determine the Bus Type and save it in the Host Adapter structure,
+ overriding the DMA Channel if it is inappropriate for the bus type.
+ */
+ if (ExtendedSetupInformation.BusType == 'A')
+ HostAdapter->BusType = BusLogic_ISA_Bus;
+ else
+ switch (HostAdapter->ModelName[3])
+ {
+ case '4':
+ HostAdapter->BusType = BusLogic_VESA_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ case '5':
+ HostAdapter->BusType = BusLogic_ISA_Bus;
+ break;
+ case '6':
+ HostAdapter->BusType = BusLogic_MCA_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ case '7':
+ HostAdapter->BusType = BusLogic_EISA_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ case '9':
+ HostAdapter->BusType = BusLogic_PCI_Bus;
+ HostAdapter->DMA_Channel = 0;
+ break;
+ }
+ /*
+ Determine whether Extended Translation is enabled and save it in
+ the Host Adapter structure.
+ */
+ GeometryRegister = BusLogic_ReadGeometryRegister(HostAdapter);
+ if (GeometryRegister & BusLogic_ExtendedTranslationEnabled)
+ HostAdapter->ExtendedTranslation = true;
+ /*
+ Save the Disconnect/Reconnect Permitted flag bits in the Host Adapter
+ structure. The Disconnect Permitted information is only valid on "C"
+ Series boards, but Disconnect/Reconnect is always permitted on "S" and
+ "A" Series boards.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '4')
+ HostAdapter->DisconnectPermitted =
+ (SetupInformation.DisconnectPermittedID8to15 << 8)
+ | SetupInformation.DisconnectPermittedID0to7;
+ else HostAdapter->DisconnectPermitted = 0xFF;
+ /*
+ Save the Scatter Gather Limits, Level Sensitive Interrupts flag,
+ Wide SCSI flag, and Differential SCSI flag in the Host Adapter structure.
+ */
+ HostAdapter->HostAdapterScatterGatherLimit =
+ ExtendedSetupInformation.ScatterGatherLimit;
+ HostAdapter->DriverScatterGatherLimit =
+ HostAdapter->HostAdapterScatterGatherLimit;
+ if (HostAdapter->HostAdapterScatterGatherLimit > BusLogic_ScatterGatherLimit)
+ HostAdapter->DriverScatterGatherLimit = BusLogic_ScatterGatherLimit;
+ if (ExtendedSetupInformation.Misc.LevelSensitiveInterrupts)
+ HostAdapter->LevelSensitiveInterrupts = true;
+ if (ExtendedSetupInformation.HostWideSCSI)
+ {
+ HostAdapter->HostWideSCSI = true;
+ HostAdapter->MaxTargetIDs = 16;
+ HostAdapter->MaxLogicalUnits = 64;
+ }
+ else
+ {
+ HostAdapter->HostWideSCSI = false;
+ HostAdapter->MaxTargetIDs = 8;
+ HostAdapter->MaxLogicalUnits = 8;
+ }
+ HostAdapter->HostDifferentialSCSI =
+ ExtendedSetupInformation.HostDifferentialSCSI;
+ /*
+ Determine the Host Adapter BIOS Address if the BIOS is enabled and
+ save it in the Host Adapter structure. The BIOS is disabled if the
+ BIOS_Address is 0.
+ */
+ HostAdapter->BIOS_Address = ExtendedSetupInformation.BIOS_Address << 12;
+ /*
+ BusLogic BT-445S Host Adapters prior to board revision D have a hardware
+ bug whereby when the BIOS is enabled, transfers to/from the same address
+ range the BIOS occupies modulo 16MB are handled incorrectly. Only properly
+ functioning BT-445S boards have firmware version 3.37, so we require that
+ ISA bounce buffers be used for the buggy BT-445S models as well as for all
+ ISA models.
+ */
+ if (HostAdapter->BusType == BusLogic_ISA_Bus ||
+ (HostAdapter->BIOS_Address > 0 &&
+ strcmp(HostAdapter->ModelName, "BT-445S") == 0 &&
+ strcmp(HostAdapter->FirmwareVersion, "3.37") < 0))
+ HostAdapter->BounceBuffersRequired = true;
+ /*
+ Select an appropriate value for Concurrency (Commands per Logical Unit)
+ either from a Command Line Entry, or based on whether this Host Adapter
+ requires that ISA bounce buffers be used.
+ */
+ if (HostAdapter->CommandLineEntry != NULL &&
+ HostAdapter->CommandLineEntry->Concurrency > 0)
+ HostAdapter->Concurrency = HostAdapter->CommandLineEntry->Concurrency;
+ else if (HostAdapter->BounceBuffersRequired)
+ HostAdapter->Concurrency = BusLogic_Concurrency_BB;
+ else HostAdapter->Concurrency = BusLogic_Concurrency;
+ /*
+ Select an appropriate value for Bus Settle Time either from a Command
+ Line Entry, or from BusLogic_DefaultBusSettleTime.
+ */
+ if (HostAdapter->CommandLineEntry != NULL &&
+ HostAdapter->CommandLineEntry->BusSettleTime > 0)
+ HostAdapter->BusSettleTime = HostAdapter->CommandLineEntry->BusSettleTime;
+ else HostAdapter->BusSettleTime = BusLogic_DefaultBusSettleTime;
+ /*
+ Select an appropriate value for Local Options from a Command Line Entry.
+ */
+ if (HostAdapter->CommandLineEntry != NULL)
+ HostAdapter->LocalOptions = HostAdapter->CommandLineEntry->LocalOptions;
+ /*
+ Select appropriate values for the Error Recovery Option array either from
+ a Command Line Entry, or using BusLogic_ErrorRecoveryDefault.
+ */
+ if (HostAdapter->CommandLineEntry != NULL)
+ memcpy(HostAdapter->ErrorRecoveryOption,
+ HostAdapter->CommandLineEntry->ErrorRecoveryOption,
+ sizeof(HostAdapter->ErrorRecoveryOption));
+ else memset(HostAdapter->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryDefault,
+ sizeof(HostAdapter->ErrorRecoveryOption));
+ /*
+ Tagged Queuing support is available and operates properly only on "C"
+ Series boards with firmware version 4.22 and above and on "S" Series
+ boards with firmware version 3.35 and above. Tagged Queuing is disabled
+ by default when the Concurrency value is 1 since queuing multiple commands
+ is not possible.
+ */
+ TaggedQueuingPermittedDefault = 0;
+ if (HostAdapter->Concurrency > 1)
+ switch (HostAdapter->FirmwareVersion[0])
+ {
+ case '5':
+ TaggedQueuingPermittedDefault = 0xFFFF;
+ break;
+ case '4':
+ if (strcmp(HostAdapter->FirmwareVersion, "4.22") >= 0)
+ TaggedQueuingPermittedDefault = 0xFFFF;
+ break;
+ case '3':
+ if (strcmp(HostAdapter->FirmwareVersion, "3.35") >= 0)
+ TaggedQueuingPermittedDefault = 0xFFFF;
+ break;
+ }
+ /*
+ Tagged Queuing is only useful if Disconnect/Reconnect is permitted.
+ Therefore, mask the Tagged Queuing Permitted Default bits with the
+ Disconnect/Reconnect Permitted bits.
+ */
+ TaggedQueuingPermittedDefault &= HostAdapter->DisconnectPermitted;
+ /*
+ Combine the default Tagged Queuing Permitted Default bits with any
+ Command Line Entry Tagged Queuing specification.
+ */
+ if (HostAdapter->CommandLineEntry != NULL)
+ HostAdapter->TaggedQueuingPermitted =
+ (HostAdapter->CommandLineEntry->TaggedQueuingPermitted &
+ HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask) |
+ (TaggedQueuingPermittedDefault &
+ ~HostAdapter->CommandLineEntry->TaggedQueuingPermittedMask);
+ else HostAdapter->TaggedQueuingPermitted = TaggedQueuingPermittedDefault;
+ /*
+ Announce the Host Adapter Configuration.
+ */
+ printk("scsi%d: Configuring BusLogic Model %s %s%s%s SCSI Host Adapter\n",
+ HostAdapter->HostNumber, HostAdapter->ModelName,
+ BusLogic_BusNames[HostAdapter->BusType],
+ (HostAdapter->HostWideSCSI ? " Wide" : ""),
+ (HostAdapter->HostDifferentialSCSI ? " Differential" : ""));
+ printk("scsi%d: Firmware Version: %s, I/O Address: 0x%X, "
+ "IRQ Channel: %d/%s\n",
+ HostAdapter->HostNumber, HostAdapter->FirmwareVersion,
+ HostAdapter->IO_Address, HostAdapter->IRQ_Channel,
+ (HostAdapter->LevelSensitiveInterrupts ? "Level" : "Edge"));
+ printk("scsi%d: DMA Channel: ", HostAdapter->HostNumber);
+ if (HostAdapter->DMA_Channel > 0)
+ printk("%d, ", HostAdapter->DMA_Channel);
+ else printk("None, ");
+ if (HostAdapter->BIOS_Address > 0)
+ printk("BIOS Address: 0x%lX, ", HostAdapter->BIOS_Address);
+ else printk("BIOS Address: None, ");
+ printk("Host Adapter SCSI ID: %d\n", HostAdapter->SCSI_ID);
+ printk("scsi%d: Scatter/Gather Limit: %d segments, "
+ "Synchronous Initiation: %s\n", HostAdapter->HostNumber,
+ HostAdapter->HostAdapterScatterGatherLimit,
+ (HostAdapter->SynchronousInitiation ? "Enabled" : "Disabled"));
+ printk("scsi%d: SCSI Parity Checking: %s, "
+ "Extended Disk Translation: %s\n", HostAdapter->HostNumber,
+ (HostAdapter->ParityChecking ? "Enabled" : "Disabled"),
+ (HostAdapter->ExtendedTranslation ? "Enabled" : "Disabled"));
+ AllTargetsMask = (1 << HostAdapter->MaxTargetIDs) - 1;
+ DisconnectPermitted = HostAdapter->DisconnectPermitted & AllTargetsMask;
+ printk("scsi%d: Disconnect/Reconnect: ", HostAdapter->HostNumber);
+ if (DisconnectPermitted == 0)
+ printk("Disabled");
+ else if (DisconnectPermitted == AllTargetsMask)
+ printk("Enabled");
+ else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ printk("%c", (DisconnectPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ printk(", Tagged Queuing: ");
+ TaggedQueuingPermitted =
+ HostAdapter->TaggedQueuingPermitted & AllTargetsMask;
+ if (TaggedQueuingPermitted == 0)
+ printk("Disabled");
+ else if (TaggedQueuingPermitted == AllTargetsMask)
+ printk("Enabled");
+ else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ printk("%c", (TaggedQueuingPermitted & (1 << TargetID)) ? 'Y' : 'N');
+ printk("\n");
+ CommonErrorRecovery = true;
+ for (TargetID = 1; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ if (HostAdapter->ErrorRecoveryOption[TargetID] !=
+ HostAdapter->ErrorRecoveryOption[0])
+ {
+ CommonErrorRecovery = false;
+ break;
+ }
+ printk("scsi%d: Error Recovery: ", HostAdapter->HostNumber);
+ if (CommonErrorRecovery)
+ printk("%s", BusLogic_ErrorRecoveryOptions[
+ HostAdapter->ErrorRecoveryOption[0]]);
+ else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ printk("%s", BusLogic_ErrorRecoveryOptions2[
+ HostAdapter->ErrorRecoveryOption[TargetID]]);
+ printk(", Mailboxes: %d, Initial CCBs: %d\n",
+ BusLogic_MailboxCount, BusLogic_InitialCCBs);
+ printk("scsi%d: Driver Scatter/Gather Limit: %d segments, "
+ "Concurrency: %d\n", HostAdapter->HostNumber,
+ HostAdapter->DriverScatterGatherLimit, HostAdapter->Concurrency);
+ /*
+ Indicate reading the Host Adapter Configuration completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_AcquireResources acquires the system resources necessary to use Host
+ Adapter, and initializes the fields in the SCSI Host structure. The base,
+ io_port, n_io_ports, irq, and dma_channel fields in the SCSI Host structure
+ are intentionally left uninitialized, as this driver handles acquisition and
+ release of these resources explicitly, as well as ensuring exclusive access
+ to the Host Adapter hardware and data structures through explicit locking.
+*/
+
+static boolean BusLogic_AcquireResources(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Host_T *Host)
+{
+ /*
+ Acquire exclusive or shared access to the IRQ Channel. A usage count is
+ maintained so that PCI, EISA, or MCA shared Interrupts can be supported.
+ */
+ if (BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]++ == 0)
+ {
+ if (request_irq(HostAdapter->IRQ_Channel, BusLogic_InterruptHandler,
+ SA_INTERRUPT, HostAdapter->InterruptLabel) < 0)
+ {
+ BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9]--;
+ printk("scsi%d: UNABLE TO ACQUIRE IRQ CHANNEL %d - DETACHING\n",
+ HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+ return false;
+ }
+ }
+ else
+ {
+ BusLogic_HostAdapter_T *FirstHostAdapter =
+ BusLogic_RegisteredHostAdapters;
+ while (FirstHostAdapter != NULL)
+ {
+ if (FirstHostAdapter->IRQ_Channel == HostAdapter->IRQ_Channel)
+ {
+ if (strlen(FirstHostAdapter->InterruptLabel) + 11
+ < sizeof(FirstHostAdapter->InterruptLabel))
+ {
+ strcat(FirstHostAdapter->InterruptLabel, " + ");
+ strcat(FirstHostAdapter->InterruptLabel,
+ HostAdapter->ModelName);
+ }
+ break;
+ }
+ FirstHostAdapter = FirstHostAdapter->Next;
+ }
+ }
+ HostAdapter->IRQ_ChannelAcquired = true;
+ /*
+ Acquire exclusive access to the DMA Channel.
+ */
+ if (HostAdapter->DMA_Channel > 0)
+ {
+ if (request_dma(HostAdapter->DMA_Channel, HostAdapter->BoardName) < 0)
+ {
+ printk("scsi%d: UNABLE TO ACQUIRE DMA CHANNEL %d - DETACHING\n",
+ HostAdapter->HostNumber, HostAdapter->DMA_Channel);
+ return false;
+ }
+ set_dma_mode(HostAdapter->DMA_Channel, DMA_MODE_CASCADE);
+ enable_dma(HostAdapter->DMA_Channel);
+ HostAdapter->DMA_ChannelAcquired = true;
+ }
+ /*
+ Initialize necessary fields in the SCSI Host structure.
+ */
+ Host->max_id = HostAdapter->MaxTargetIDs;
+ Host->max_lun = HostAdapter->MaxLogicalUnits;
+ Host->max_channel = 0;
+ Host->this_id = HostAdapter->SCSI_ID;
+ Host->can_queue = BusLogic_MailboxCount;
+ Host->cmd_per_lun = HostAdapter->Concurrency;
+ Host->sg_tablesize = HostAdapter->DriverScatterGatherLimit;
+ Host->unchecked_isa_dma = HostAdapter->BounceBuffersRequired;
+ /*
+ Indicate the System Resource Acquisition completed successfully,
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_ReleaseResources releases any system resources previously acquired
+ by BusLogic_AcquireResources.
+*/
+
+static void BusLogic_ReleaseResources(BusLogic_HostAdapter_T *HostAdapter)
+{
+ /*
+ Release exclusive or shared access to the IRQ Channel.
+ */
+ if (HostAdapter->IRQ_ChannelAcquired)
+ if (--BusLogic_IRQ_UsageCount[HostAdapter->IRQ_Channel - 9] == 0)
+ free_irq(HostAdapter->IRQ_Channel);
+ /*
+ Release exclusive access to the DMA Channel.
+ */
+ if (HostAdapter->DMA_ChannelAcquired)
+ free_dma(HostAdapter->DMA_Channel);
+}
+
+
+/*
+ BusLogic_TestInterrupts tests for proper functioning of the Host Adapter
+ Interrupt Register and that interrupts generated by the Host Adapter are
+ getting through to the Interrupt Handler. A large proportion of initial
+ problems with installing PCI Host Adapters are due to configuration problems
+ where either the Host Adapter or Motherboard is configured incorrectly, and
+ interrupts do not get through as a result.
+*/
+
+static boolean BusLogic_TestInterrupts(BusLogic_HostAdapter_T *HostAdapter)
+{
+ unsigned int InitialInterruptCount, FinalInterruptCount;
+ int TestCount = 5, i;
+ InitialInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+ /*
+ Issue the Test Command Complete Interrupt commands.
+ */
+ for (i = 0; i < TestCount; i++)
+ BusLogic_Command(HostAdapter, BusLogic_TestCommandCompleteInterrupt,
+ NULL, 0, NULL, 0);
+ /*
+ Verify that BusLogic_InterruptHandler was called at least TestCount times.
+ Shared IRQ Channels could cause more than TestCount interrupts to occur,
+ but there should never be fewer than TestCount.
+ */
+ FinalInterruptCount = kstat.interrupts[HostAdapter->IRQ_Channel];
+ if (FinalInterruptCount < InitialInterruptCount + TestCount)
+ {
+ BusLogic_Failure(HostAdapter, "HOST ADAPTER INTERRUPT TEST");
+ printk("\n\
+Interrupts are not getting through from the Host Adapter to the BusLogic\n\
+Driver Interrupt Handler. The most likely cause is that either the Host\n\
+Adapter or Motherboard is configured incorrectly. Please check the Host\n\
+Adapter configuration with AutoSCSI or by examining any dip switch and\n\
+jumper settings on the Host Adapter, and verify that no other device is\n\
+attempting to use the same IRQ Channel. For PCI Host Adapters, it may also\n\
+be necessary to investigate and manually set the PCI interrupt assignments\n\
+and edge/level interrupt type selection in the BIOS Setup Program or with\n\
+Motherboard jumpers.\n\n");
+ return false;
+ }
+ /*
+ Indicate the Host Adapter Interrupt Test completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_InitializeHostAdapter initializes Host Adapter. This is the only
+ function called during SCSI Host Adapter detection which modifies the state
+ of the Host Adapter from its initial power on or hard reset state.
+*/
+
+static boolean BusLogic_InitializeHostAdapter(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_ExtendedMailboxRequest_T ExtendedMailboxRequest;
+ BusLogic_RoundRobinModeRequest_T RoundRobinModeRequest;
+ BusLogic_WideModeCCBRequest_T WideModeCCBRequest;
+ BusLogic_ModifyIOAddressRequest_T ModifyIOAddressRequest;
+ /*
+ Initialize the Command Successful Flag, Read/Write Operation Count,
+ and Queued Operation Count for each Target.
+ */
+ memset(HostAdapter->CommandSuccessfulFlag, false,
+ sizeof(HostAdapter->CommandSuccessfulFlag));
+ memset(HostAdapter->ReadWriteOperationCount, 0,
+ sizeof(HostAdapter->ReadWriteOperationCount));
+ memset(HostAdapter->QueuedOperationCount, 0,
+ sizeof(HostAdapter->QueuedOperationCount));
+ /*
+ Initialize the Outgoing and Incoming Mailbox structures.
+ */
+ memset(HostAdapter->OutgoingMailboxes, 0,
+ sizeof(HostAdapter->OutgoingMailboxes));
+ memset(HostAdapter->IncomingMailboxes, 0,
+ sizeof(HostAdapter->IncomingMailboxes));
+ /*
+ Initialize the pointers to the First, Last, and Next Mailboxes.
+ */
+ HostAdapter->FirstOutgoingMailbox = &HostAdapter->OutgoingMailboxes[0];
+ HostAdapter->LastOutgoingMailbox =
+ &HostAdapter->OutgoingMailboxes[BusLogic_MailboxCount-1];
+ HostAdapter->NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->FirstIncomingMailbox = &HostAdapter->IncomingMailboxes[0];
+ HostAdapter->LastIncomingMailbox =
+ &HostAdapter->IncomingMailboxes[BusLogic_MailboxCount-1];
+ HostAdapter->NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ /*
+ Initialize the Host Adapter's Pointer to the Outgoing/Incoming Mailboxes.
+ */
+ ExtendedMailboxRequest.MailboxCount = BusLogic_MailboxCount;
+ ExtendedMailboxRequest.BaseMailboxAddress = HostAdapter->OutgoingMailboxes;
+ if (BusLogic_Command(HostAdapter, BusLogic_InitializeExtendedMailbox,
+ &ExtendedMailboxRequest,
+ sizeof(ExtendedMailboxRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "MAILBOX INITIALIZATION");
+ /*
+ Enable Strict Round Robin Mode if supported by the Host Adapter. In Strict
+ Round Robin Mode, the Host Adapter only looks at the next Outgoing Mailbox
+ for each new command, rather than scanning through all the Outgoing
+ Mailboxes to find any that have new commands in them. BusLogic indicates
+ that Strict Round Robin Mode is significantly more efficient.
+ */
+ if (strcmp(HostAdapter->FirmwareVersion, "3.31") >= 0)
+ {
+ RoundRobinModeRequest = BusLogic_StrictRoundRobinMode;
+ if (BusLogic_Command(HostAdapter, BusLogic_EnableStrictRoundRobinMode,
+ &RoundRobinModeRequest,
+ sizeof(RoundRobinModeRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "ENABLE STRICT ROUND ROBIN MODE");
+ }
+ /*
+ For Wide SCSI Host Adapters, issue the Enable Wide Mode CCB command to
+ allow more than 8 Logical Units per Target to be supported.
+ */
+ if (HostAdapter->HostWideSCSI)
+ {
+ WideModeCCBRequest = BusLogic_WideModeCCB;
+ if (BusLogic_Command(HostAdapter, BusLogic_EnableWideModeCCB,
+ &WideModeCCBRequest,
+ sizeof(WideModeCCBRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "ENABLE WIDE MODE CCB");
+ }
+ /*
+ For PCI Host Adapters being accessed through the PCI compliant I/O
+ Address, disable the ISA compatible I/O Address to avoid detecting the
+ same Host Adapter at both I/O Addresses.
+ */
+ if (HostAdapter->BusType == BusLogic_PCI_Bus)
+ {
+ int Index;
+ for (Index = 0; BusLogic_IO_StandardAddresses[Index] > 0; Index++)
+ if (HostAdapter->IO_Address == BusLogic_IO_StandardAddresses[Index])
+ break;
+ if (BusLogic_IO_StandardAddresses[Index] == 0)
+ {
+ ModifyIOAddressRequest = BusLogic_ModifyIO_Disable;
+ if (BusLogic_Command(HostAdapter, BusLogic_ModifyIOAddress,
+ &ModifyIOAddressRequest,
+ sizeof(ModifyIOAddressRequest), NULL, 0) < 0)
+ return BusLogic_Failure(HostAdapter, "MODIFY I/O ADDRESS");
+ }
+ }
+ /*
+ Announce Successful Initialization.
+ */
+ printk("scsi%d: *** %s Initialized Successfully ***\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName);
+ /*
+ Indicate the Host Adapter Initialization completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_InquireTargetDevices inquires about the Target Devices accessible
+ through Host Adapter and reports on the results.
+*/
+
+static boolean BusLogic_InquireTargetDevices(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ BusLogic_InstalledDevices8_T InstalledDevicesID0to7;
+ BusLogic_InstalledDevices8_T InstalledDevicesID8to15;
+ BusLogic_SetupInformation_T SetupInformation;
+ BusLogic_SynchronousPeriod_T SynchronousPeriod;
+ BusLogic_RequestedReplyLength_T RequestedReplyLength;
+ int TargetDevicesFound = 0, TargetID;
+ /*
+ Wait a few seconds between the Host Adapter Hard Reset which initiates
+ a SCSI Bus Reset and issuing any SCSI commands. Some SCSI devices get
+ confused if they receive SCSI commands too soon after a SCSI Bus Reset.
+ */
+ BusLogic_Delay(HostAdapter->BusSettleTime);
+ /*
+ Inhibit the Target Devices Inquiry if requested.
+ */
+ if (HostAdapter->LocalOptions & BusLogic_InhibitTargetInquiry)
+ {
+ printk("scsi%d: Target Device Inquiry Inhibited\n",
+ HostAdapter->HostNumber);
+ return true;
+ }
+ /*
+ Issue the Inquire Installed Devices ID 0 to 7 command, and for Wide SCSI
+ Host Adapters the Inquire Installed Devices ID 8 to 15 command. This is
+ necessary to force Synchronous Transfer Negotiation so that the Inquire
+ Setup Information and Inquire Synchronous Period commands will return
+ valid data.
+ */
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID0to7,
+ NULL, 0, &InstalledDevicesID0to7,
+ sizeof(InstalledDevicesID0to7))
+ != sizeof(InstalledDevicesID0to7))
+ return BusLogic_Failure(HostAdapter, "INQUIRE INSTALLED DEVICES ID 0 TO 7");
+ if (HostAdapter->HostWideSCSI)
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireInstalledDevicesID8to15,
+ NULL, 0, &InstalledDevicesID8to15,
+ sizeof(InstalledDevicesID8to15))
+ != sizeof(InstalledDevicesID8to15))
+ return BusLogic_Failure(HostAdapter,
+ "INQUIRE INSTALLED DEVICES ID 8 TO 15");
+ /*
+ Issue the Inquire Setup Information command.
+ */
+ RequestedReplyLength = sizeof(SetupInformation);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSetupInformation,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &SetupInformation, sizeof(SetupInformation))
+ != sizeof(SetupInformation))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SETUP INFORMATION");
+ /*
+ Issue the Inquire Synchronous Period command.
+ */
+ if (HostAdapter->FirmwareVersion[0] >= '3')
+ {
+ RequestedReplyLength = sizeof(SynchronousPeriod);
+ if (BusLogic_Command(HostAdapter, BusLogic_InquireSynchronousPeriod,
+ &RequestedReplyLength, sizeof(RequestedReplyLength),
+ &SynchronousPeriod, sizeof(SynchronousPeriod))
+ != sizeof(SynchronousPeriod))
+ return BusLogic_Failure(HostAdapter, "INQUIRE SYNCHRONOUS PERIOD");
+ }
+ else
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ if (SetupInformation.SynchronousValuesID0to7[TargetID].Offset > 0)
+ SynchronousPeriod[TargetID] =
+ 20 + 5 * SetupInformation.SynchronousValuesID0to7[TargetID]
+ .TransferPeriod;
+ else SynchronousPeriod[TargetID] = 0;
+ /*
+ Save the Installed Devices, Synchronous Values, and Synchronous Period
+ information in the Host Adapter structure.
+ */
+ memcpy(HostAdapter->InstalledDevices, InstalledDevicesID0to7,
+ sizeof(BusLogic_InstalledDevices8_T));
+ memcpy(HostAdapter->SynchronousValues,
+ SetupInformation.SynchronousValuesID0to7,
+ sizeof(BusLogic_SynchronousValues8_T));
+ if (HostAdapter->HostWideSCSI)
+ {
+ memcpy(&HostAdapter->InstalledDevices[8], InstalledDevicesID8to15,
+ sizeof(BusLogic_InstalledDevices8_T));
+ memcpy(&HostAdapter->SynchronousValues[8],
+ SetupInformation.SynchronousValuesID8to15,
+ sizeof(BusLogic_SynchronousValues8_T));
+ }
+ memcpy(HostAdapter->SynchronousPeriod, SynchronousPeriod,
+ sizeof(BusLogic_SynchronousPeriod_T));
+ for (TargetID = 0; TargetID < HostAdapter->MaxTargetIDs; TargetID++)
+ if (HostAdapter->InstalledDevices[TargetID] != 0)
+ {
+ int SynchronousPeriod = HostAdapter->SynchronousPeriod[TargetID];
+ if (SynchronousPeriod > 10)
+ {
+ int SynchronousTransferRate = 100000000 / SynchronousPeriod;
+ int RoundedSynchronousTransferRate =
+ (SynchronousTransferRate + 5000) / 10000;
+ printk("scsi%d: Target %d: Synchronous at "
+ "%d.%02d mega-transfers/second, offset %d\n",
+ HostAdapter->HostNumber, TargetID,
+ RoundedSynchronousTransferRate / 100,
+ RoundedSynchronousTransferRate % 100,
+ HostAdapter->SynchronousValues[TargetID].Offset);
+ }
+ else if (SynchronousPeriod > 0)
+ {
+ int SynchronousTransferRate = 100000000 / SynchronousPeriod;
+ int RoundedSynchronousTransferRate =
+ (SynchronousTransferRate + 50000) / 100000;
+ printk("scsi%d: Target %d: Synchronous at "
+ "%d.%01d mega-transfers/second, offset %d\n",
+ HostAdapter->HostNumber, TargetID,
+ RoundedSynchronousTransferRate / 10,
+ RoundedSynchronousTransferRate % 10,
+ HostAdapter->SynchronousValues[TargetID].Offset);
+ }
+ else printk("scsi%d: Target %d: Asynchronous\n",
+ HostAdapter->HostNumber, TargetID);
+ TargetDevicesFound++;
+ }
+ if (TargetDevicesFound == 0)
+ printk("scsi%d: No Target Devices Found\n", HostAdapter->HostNumber);
+ /*
+ Indicate the Target Device Inquiry completed successfully.
+ */
+ return true;
+}
+
+
+/*
+ BusLogic_DetectHostAdapter probes for BusLogic Host Adapters at the standard
+ I/O Addresses where they may be located, initializing, registering, and
+ reporting the configuration of each BusLogic Host Adapter it finds. It
+ returns the number of BusLogic Host Adapters successfully initialized and
+ registered.
+*/
+
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *HostTemplate)
+{
+ int BusLogicHostAdapterCount = 0, CommandLineEntryIndex = 0;
+ int AddressProbeIndex = 0;
+ BusLogic_InitializeAddressProbeList();
+ while (BusLogic_IO_AddressProbeList[AddressProbeIndex] > 0)
+ {
+ BusLogic_HostAdapter_T HostAdapterPrototype;
+ BusLogic_HostAdapter_T *HostAdapter = &HostAdapterPrototype;
+ SCSI_Host_T *Host;
+ memset(HostAdapter, 0, sizeof(BusLogic_HostAdapter_T));
+ HostAdapter->IO_Address =
+ BusLogic_IO_AddressProbeList[AddressProbeIndex++];
+ /*
+ Initialize the Command Line Entry field if an explicit I/O Address
+ was specified.
+ */
+ if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
+ BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address ==
+ HostAdapter->IO_Address)
+ HostAdapter->CommandLineEntry =
+ &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
+ /*
+ Check whether the I/O Address range is already in use.
+ */
+ if (check_region(HostAdapter->IO_Address, BusLogic_IO_PortCount) < 0)
+ continue;
+ /*
+ Probe the Host Adapter. If unsuccessful, abort further initialization.
+ */
+ if (!BusLogic_ProbeHostAdapter(HostAdapter)) continue;
+ /*
+ Hard Reset the Host Adapter. If unsuccessful, abort further
+ initialization.
+ */
+ if (!BusLogic_HardResetHostAdapter(HostAdapter)) continue;
+ /*
+ Check the Host Adapter. If unsuccessful, abort further initialization.
+ */
+ if (!BusLogic_CheckHostAdapter(HostAdapter)) continue;
+ /*
+ Initialize the Command Line Entry field if an explicit I/O Address
+ was not specified.
+ */
+ if (CommandLineEntryIndex < BusLogic_CommandLineEntryCount &&
+ BusLogic_CommandLineEntries[CommandLineEntryIndex].IO_Address == 0)
+ HostAdapter->CommandLineEntry =
+ &BusLogic_CommandLineEntries[CommandLineEntryIndex++];
+ /*
+ Announce the Driver Version and Date, Author's Name, Copyright Notice,
+ and Contact Address.
+ */
+ BusLogic_AnnounceDriver();
+ /*
+ Register usage of the I/O Address range. From this point onward, any
+ failure will be assumed to be due to a problem with the Host Adapter,
+ rather than due to having mistakenly identified this port as belonging
+ to a BusLogic Host Adapter. The I/O Address range will not be
+ released, thereby preventing it from being incorrectly identified as
+ any other type of Host Adapter.
+ */
+ request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+ "BusLogic");
+ /*
+ Register the SCSI Host structure.
+ */
+ HostTemplate->proc_dir = &BusLogic_ProcDirectoryEntry;
+ Host = scsi_register(HostTemplate, sizeof(BusLogic_HostAdapter_T));
+ HostAdapter = (BusLogic_HostAdapter_T *) Host->hostdata;
+ memcpy(HostAdapter, &HostAdapterPrototype,
+ sizeof(BusLogic_HostAdapter_T));
+ HostAdapter->SCSI_Host = Host;
+ HostAdapter->HostNumber = Host->host_no;
+ /*
+ Add Host Adapter to the end of the list of registered BusLogic
+ Host Adapters. In order for Command Complete Interrupts to be
+ properly dismissed by BusLogic_InterruptHandler, the Host Adapter
+ must be registered. This must be done before the IRQ Channel is
+ acquired, and in a shared IRQ Channel environment, must be done
+ before any Command Complete Interrupts occur, since the IRQ Channel
+ may have already been acquired by a previous BusLogic Host Adapter.
+ */
+ BusLogic_RegisterHostAdapter(HostAdapter);
+ /*
+ Read the Host Adapter Configuration, Acquire the System Resources
+ necessary to use Host Adapter and initialize the fields in the SCSI
+ Host structure, then Test Interrupts, Create the CCBs, Initialize
+ the Host Adapter, and finally Inquire about the Target Devices.
+ */
+ if (BusLogic_ReadHostAdapterConfiguration(HostAdapter) &&
+ BusLogic_AcquireResources(HostAdapter, Host) &&
+ BusLogic_TestInterrupts(HostAdapter) &&
+ BusLogic_CreateCCBs(HostAdapter) &&
+ BusLogic_InitializeHostAdapter(HostAdapter) &&
+ BusLogic_InquireTargetDevices(HostAdapter))
+ {
+ /*
+ Initialization has been completed successfully. Release and
+ re-register usage of the I/O Address range so that the Model
+ Name of the Host Adapter will appear.
+ */
+ release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+ request_region(HostAdapter->IO_Address, BusLogic_IO_PortCount,
+ HostAdapter->BoardName);
+ BusLogicHostAdapterCount++;
+ }
+ else
+ {
+ /*
+ An error occurred during Host Adapter Configuration Querying,
+ Resource Acquisition, Interrupt Testing, CCB Creation, Host
+ Adapter Initialization, or Target Device Inquiry, so remove
+ Host Adapter from the list of registered BusLogic Host Adapters,
+ destroy the CCBs, Release the System Resources, and Unregister
+ the SCSI Host.
+ */
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ BusLogic_UnregisterHostAdapter(HostAdapter);
+ scsi_unregister(Host);
+ }
+ }
+ return BusLogicHostAdapterCount;
+}
+
+
+/*
+ BusLogic_ReleaseHostAdapter releases all resources previously acquired to
+ support a specific Host Adapter, including the I/O Address range, and
+ unregisters the BusLogic Host Adapter.
+*/
+
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *Host)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Host->hostdata;
+ /*
+ Destroy the CCBs and release any system resources acquired to use
+ Host Adapter.
+ */
+ BusLogic_DestroyCCBs(HostAdapter);
+ BusLogic_ReleaseResources(HostAdapter);
+ /*
+ Release usage of the I/O Address range.
+ */
+ release_region(HostAdapter->IO_Address, BusLogic_IO_PortCount);
+ /*
+ Remove Host Adapter from the list of registered BusLogic Host Adapters.
+ */
+ BusLogic_UnregisterHostAdapter(HostAdapter);
+ return 0;
+}
+
+
+/*
+ BusLogic_ComputeResultCode computes a SCSI Subsystem Result Code from
+ the Host Adapter Status and Target Device Status.
+*/
+
+static int BusLogic_ComputeResultCode(BusLogic_HostAdapterStatus_T
+ HostAdapterStatus,
+ BusLogic_TargetDeviceStatus_T
+ TargetDeviceStatus)
+{
+ int HostStatus;
+ switch (HostAdapterStatus)
+ {
+ case BusLogic_CommandCompletedNormally:
+ case BusLogic_LinkedCommandCompleted:
+ case BusLogic_LinkedCommandCompletedWithFlag:
+ HostStatus = DID_OK;
+ break;
+ case BusLogic_SCSISelectionTimeout:
+ HostStatus = DID_TIME_OUT;
+ break;
+ case BusLogic_InvalidOutgoingMailboxActionCode:
+ case BusLogic_InvalidCommandOperationCode:
+ case BusLogic_InvalidCommandParameter:
+ printk("BusLogic: BusLogic Driver Protocol Error 0x%02X\n",
+ HostAdapterStatus);
+ case BusLogic_DataOverUnderRun:
+ case BusLogic_UnexpectedBusFree:
+ case BusLogic_LinkedCCBhasInvalidLUN:
+ case BusLogic_AutoRequestSenseFailed:
+ case BusLogic_TaggedQueuingMessageRejected:
+ case BusLogic_UnsupportedMessageReceived:
+ case BusLogic_HostAdapterHardwareFailed:
+ case BusLogic_TargetDeviceReconnectedImproperly:
+ case BusLogic_AbortQueueGenerated:
+ case BusLogic_HostAdapterSoftwareError:
+ case BusLogic_HostAdapterHardwareTimeoutError:
+ case BusLogic_SCSIParityErrorDetected:
+ HostStatus = DID_ERROR;
+ break;
+ case BusLogic_InvalidBusPhaseRequested:
+ case BusLogic_TargetFailedResponseToATN:
+ case BusLogic_HostAdapterAssertedRST:
+ case BusLogic_OtherDeviceAssertedRST:
+ case BusLogic_HostAdapterAssertedBusDeviceReset:
+ HostStatus = DID_RESET;
+ break;
+ default:
+ printk("BusLogic: unknown Host Adapter Status 0x%02X\n",
+ HostAdapterStatus);
+ HostStatus = DID_ERROR;
+ break;
+ }
+ return (HostStatus << 16) | TargetDeviceStatus;
+}
+
+
+/*
+ BusLogic_InterruptHandler handles hardware interrupts from BusLogic Host
+ Adapters. To simplify handling shared IRQ Channels, all installed BusLogic
+ Host Adapters are scanned whenever any one of them signals a hardware
+ interrupt.
+*/
+
+static void BusLogic_InterruptHandler(int IRQ_Channel,
+ Registers_T *InterruptRegisters)
+{
+ BusLogic_CCB_T *FirstCompletedCCB = NULL, *LastCompletedCCB = NULL;
+ BusLogic_HostAdapter_T *HostAdapter;
+ int HostAdapterResetPendingCount = 0;
+ /*
+ Iterate over the installed BusLogic Host Adapters accepting any Incoming
+ Mailbox entries and saving the completed CCBs for processing. This
+ interrupt handler is installed with SA_INTERRUPT, so interrupts are
+ disabled when the interrupt handler is entered.
+ */
+ for (HostAdapter = BusLogic_RegisteredHostAdapters;
+ HostAdapter != NULL;
+ HostAdapter = HostAdapter->Next)
+ {
+ unsigned char InterruptRegister;
+ /*
+ Acquire exclusive access to Host Adapter.
+ */
+ BusLogic_LockHostAdapterID(HostAdapter);
+ /*
+ Read the Host Adapter Interrupt Register.
+ */
+ InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+ if (InterruptRegister & BusLogic_InterruptValid)
+ {
+ /*
+ Acknowledge the interrupt and reset the Host Adapter
+ Interrupt Register.
+ */
+ BusLogic_WriteControlRegister(HostAdapter, BusLogic_InterruptReset);
+ /*
+ Process valid SCSI Reset State and Incoming Mailbox Loaded
+ interrupts. Command Complete interrupts are noted, and
+ Outgoing Mailbox Available interrupts are ignored, as they
+ are never enabled.
+ */
+ if (InterruptRegister & BusLogic_SCSIResetState)
+ {
+ HostAdapter->HostAdapterResetPending = true;
+ HostAdapterResetPendingCount++;
+ }
+ else if (InterruptRegister & BusLogic_IncomingMailboxLoaded)
+ {
+ /*
+ Scan through the Incoming Mailboxes in Strict Round Robin
+ fashion, saving any completed CCBs for further processing.
+ It is essential that for each CCB and SCSI Command issued,
+ command completion processing is performed exactly once.
+ Therefore, only Incoming Mailboxes with completion code
+ Command Completed Without Error, Command Completed With
+ Error, or Command Aborted At Host Request are saved for
+ completion processing. When an Incoming Mailbox has a
+ completion code of Aborted Command Not Found, the CCB had
+ already completed or been aborted before the current Abort
+ request was processed, and so completion processing has
+ already occurred and no further action should be taken.
+ */
+ BusLogic_IncomingMailbox_T *NextIncomingMailbox =
+ HostAdapter->NextIncomingMailbox;
+ BusLogic_CompletionCode_T MailboxCompletionCode;
+ while ((MailboxCompletionCode =
+ NextIncomingMailbox->CompletionCode) !=
+ BusLogic_IncomingMailboxFree)
+ {
+ BusLogic_CCB_T *CCB = NextIncomingMailbox->CCB;
+ if (MailboxCompletionCode != BusLogic_AbortedCommandNotFound)
+ if (CCB->Status == BusLogic_CCB_Active)
+ {
+ /*
+ Mark this CCB as completed and add it to the end
+ of the list of completed CCBs.
+ */
+ CCB->Status = BusLogic_CCB_Completed;
+ CCB->MailboxCompletionCode = MailboxCompletionCode;
+ CCB->Next = NULL;
+ if (FirstCompletedCCB == NULL)
+ {
+ FirstCompletedCCB = CCB;
+ LastCompletedCCB = CCB;
+ }
+ else
+ {
+ LastCompletedCCB->Next = CCB;
+ LastCompletedCCB = CCB;
+ }
+ HostAdapter->QueuedOperationCount[CCB->TargetID]--;
+ }
+ else
+ {
+ /*
+ If a CCB ever appears in an Incoming Mailbox and
+ is not marked as status Active, then there is
+ most likely a bug in the Host Adapter firmware.
+ */
+ printk("scsi%d: Illegal CCB #%d status %d in "
+ "Incoming Mailbox\n", HostAdapter->HostNumber,
+ CCB->SerialNumber, CCB->Status);
+ }
+ else printk("scsi%d: Aborted CCB #%d to Target %d "
+ "Not Found\n", HostAdapter->HostNumber,
+ CCB->SerialNumber, CCB->TargetID);
+ NextIncomingMailbox->CompletionCode =
+ BusLogic_IncomingMailboxFree;
+ if (++NextIncomingMailbox > HostAdapter->LastIncomingMailbox)
+ NextIncomingMailbox = HostAdapter->FirstIncomingMailbox;
+ }
+ HostAdapter->NextIncomingMailbox = NextIncomingMailbox;
+ }
+ else if (InterruptRegister & BusLogic_CommandComplete)
+ HostAdapter->HostAdapterCommandCompleted = true;
+ }
+ /*
+ Release exclusive access to Host Adapter.
+ */
+ BusLogic_UnlockHostAdapterID(HostAdapter);
+ }
+ /*
+ Enable interrupts while the completed CCBs are processed.
+ */
+ sti();
+ /*
+ Iterate over the Host Adapters performing any pending Host Adapter Resets.
+ */
+ if (HostAdapterResetPendingCount > 0)
+ for (HostAdapter = BusLogic_RegisteredHostAdapters;
+ HostAdapter != NULL;
+ HostAdapter = HostAdapter->Next)
+ if (HostAdapter->HostAdapterResetPending)
+ {
+ BusLogic_ResetHostAdapter(HostAdapter, NULL);
+ HostAdapter->HostAdapterResetPending = false;
+ scsi_mark_host_bus_reset(HostAdapter->SCSI_Host);
+ }
+ /*
+ Iterate over the completed CCBs setting the SCSI Command Result Codes,
+ deallocating the CCBs, and calling the Completion Routines.
+ */
+ while (FirstCompletedCCB != NULL)
+ {
+ BusLogic_CCB_T *CCB = FirstCompletedCCB;
+ SCSI_Command_T *Command = CCB->Command;
+ FirstCompletedCCB = FirstCompletedCCB->Next;
+ HostAdapter = CCB->HostAdapter;
+ /*
+ Bus Device Reset CCBs have the Command field non-NULL only when a Bus
+ Device Reset was requested for a command that was not currently active
+ in the Host Adapter, and hence would not have its Completion Routine
+ called otherwise.
+ */
+ if (CCB->Opcode == BusLogic_SCSIBusDeviceReset)
+ {
+ printk("scsi%d: Bus Device Reset CCB #%d to Target %d Completed\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ if (Command != NULL) Command->result = DID_RESET << 16;
+ }
+ else
+ /*
+ Translate the Mailbox Completion Code, Host Adapter Status, and
+ Target Device Status into a SCSI Subsystem Result Code.
+ */
+ switch (CCB->MailboxCompletionCode)
+ {
+ case BusLogic_IncomingMailboxFree:
+ case BusLogic_AbortedCommandNotFound:
+ printk("scsi%d: CCB #%d to Target %d Impossible State\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ break;
+ case BusLogic_CommandCompletedWithoutError:
+ HostAdapter->CommandSuccessfulFlag[CCB->TargetID] = true;
+ Command->result = DID_OK << 16;
+ break;
+ case BusLogic_CommandAbortedAtHostRequest:
+ printk("scsi%d: CCB #%d to Target %d Aborted\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ Command->result = DID_ABORT << 16;
+ break;
+ case BusLogic_CommandCompletedWithError:
+ Command->result =
+ BusLogic_ComputeResultCode(CCB->HostAdapterStatus,
+ CCB->TargetDeviceStatus);
+ if (BusLogic_GlobalOptions & BusLogic_TraceErrors)
+ if (CCB->HostAdapterStatus != BusLogic_SCSISelectionTimeout)
+ {
+ int i;
+ printk("scsi%d: CCB #%d Target %d: Result %X "
+ "Host Adapter Status %02X Target Status %02X\n",
+ HostAdapter->HostNumber, CCB->SerialNumber,
+ CCB->TargetID, Command->result,
+ CCB->HostAdapterStatus, CCB->TargetDeviceStatus);
+ printk("scsi%d: CDB ", HostAdapter->HostNumber);
+ for (i = 0; i < CCB->CDB_Length; i++)
+ printk(" %02X", CCB->CDB[i]);
+ printk("\n");
+ printk("scsi%d: Sense ", HostAdapter->HostNumber);
+ for (i = 0; i < CCB->SenseDataLength; i++)
+ printk(" %02X", (*CCB->SenseDataPointer)[i]);
+ printk("\n");
+ }
+ break;
+ }
+ /*
+ Place CCB back on the Host Adapter's free list.
+ */
+ BusLogic_DeallocateCCB(CCB);
+ /*
+ Call the SCSI Command Completion Routine if appropriate.
+ */
+ if (Command != NULL) Command->scsi_done(Command);
+ }
+}
+
+
+/*
+ BusLogic_WriteOutgoingMailbox places CCB and Action Code into an Outgoing
+ Mailbox for execution by Host Adapter.
+*/
+
+static boolean BusLogic_WriteOutgoingMailbox(BusLogic_HostAdapter_T
+ *HostAdapter,
+ BusLogic_ActionCode_T ActionCode,
+ BusLogic_CCB_T *CCB)
+{
+ BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+ boolean Result = false;
+ BusLogic_LockHostAdapter(HostAdapter);
+ NextOutgoingMailbox = HostAdapter->NextOutgoingMailbox;
+ if (NextOutgoingMailbox->ActionCode == BusLogic_OutgoingMailboxFree)
+ {
+ CCB->Status = BusLogic_CCB_Active;
+ /*
+ The CCB field must be written before the Action Code field since
+ the Host Adapter is operating asynchronously and the locking code
+ does not protect against simultaneous access by the Host Adapter.
+ */
+ NextOutgoingMailbox->CCB = CCB;
+ NextOutgoingMailbox->ActionCode = ActionCode;
+ BusLogic_StartMailboxScan(HostAdapter);
+ if (++NextOutgoingMailbox > HostAdapter->LastOutgoingMailbox)
+ NextOutgoingMailbox = HostAdapter->FirstOutgoingMailbox;
+ HostAdapter->NextOutgoingMailbox = NextOutgoingMailbox;
+ if (ActionCode == BusLogic_MailboxStartCommand)
+ HostAdapter->QueuedOperationCount[CCB->TargetID]++;
+ Result = true;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return Result;
+}
+
+
+/*
+ BusLogic_QueueCommand creates a CCB for Command and places it into an
+ Outgoing Mailbox for execution by the associated Host Adapter.
+*/
+
+int BusLogic_QueueCommand(SCSI_Command_T *Command,
+ void (*CompletionRoutine)(SCSI_Command_T *))
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ unsigned char *CDB = Command->cmnd;
+ unsigned char CDB_Length = Command->cmd_len;
+ unsigned char TargetID = Command->target;
+ unsigned char LogicalUnit = Command->lun;
+ void *BufferPointer = Command->request_buffer;
+ int BufferLength = Command->request_bufflen;
+ int SegmentCount = Command->use_sg;
+ BusLogic_CCB_T *CCB;
+ long EnableTQ;
+ /*
+ SCSI REQUEST_SENSE commands will be executed automatically by the Host
+ Adapter for any errors, so they should not be executed explicitly unless
+ the Sense Data is zero indicating that no error occurred.
+ */
+ if (CDB[0] == REQUEST_SENSE && Command->sense_buffer[0] != 0)
+ {
+ Command->result = DID_OK << 16;
+ CompletionRoutine(Command);
+ return 0;
+ }
+ /*
+ Allocate a CCB from the Host Adapter's free list. If there are none
+ available and memory allocation fails, return a result code of Bus Busy
+ so that this Command will be retried.
+ */
+ CCB = BusLogic_AllocateCCB(HostAdapter);
+ if (CCB == NULL)
+ {
+ Command->result = DID_BUS_BUSY << 16;
+ CompletionRoutine(Command);
+ return 0;
+ }
+ /*
+ Initialize the fields in the BusLogic Command Control Block (CCB).
+ */
+ if (SegmentCount == 0)
+ {
+ CCB->Opcode = BusLogic_InitiatorCCB;
+ CCB->DataLength = BufferLength;
+ CCB->DataPointer = BufferPointer;
+ }
+ else
+ {
+ SCSI_ScatterList_T *ScatterList = (SCSI_ScatterList_T *) BufferPointer;
+ int Segment;
+ CCB->Opcode = BusLogic_InitiatorCCB_ScatterGather;
+ CCB->DataLength = SegmentCount * sizeof(BusLogic_ScatterGatherSegment_T);
+ CCB->DataPointer = CCB->ScatterGatherList;
+ for (Segment = 0; Segment < SegmentCount; Segment++)
+ {
+ CCB->ScatterGatherList[Segment].SegmentByteCount =
+ ScatterList[Segment].length;
+ CCB->ScatterGatherList[Segment].SegmentDataPointer =
+ ScatterList[Segment].address;
+ }
+ }
+ switch (CDB[0])
+ {
+ case READ_6:
+ case READ_10:
+ CCB->DataDirection = BusLogic_DataInLengthChecked;
+ HostAdapter->ReadWriteOperationCount[TargetID]++;
+ break;
+ case WRITE_6:
+ case WRITE_10:
+ CCB->DataDirection = BusLogic_DataOutLengthChecked;
+ HostAdapter->ReadWriteOperationCount[TargetID]++;
+ break;
+ default:
+ CCB->DataDirection = BusLogic_UncheckedDataTransfer;
+ break;
+ }
+ CCB->CDB_Length = CDB_Length;
+ CCB->SenseDataLength = sizeof(Command->sense_buffer);
+ CCB->HostAdapterStatus = 0;
+ CCB->TargetDeviceStatus = 0;
+ CCB->TargetID = TargetID;
+ CCB->LogicalUnit = LogicalUnit;
+ /*
+ For Wide SCSI Host Adapters, Wide Mode CCBs are used to support more than
+ 8 Logical Units per Target, and this requires setting the overloaded
+ TagEnable field to Logical Unit bit 5.
+ */
+ if (HostAdapter->HostWideSCSI)
+ {
+ CCB->TagEnable = LogicalUnit >> 5;
+ CCB->WideModeTagEnable = false;
+ }
+ else CCB->TagEnable = false;
+ /*
+ BusLogic recommends that after a Reset the first couple of commands that
+ are sent to a Target be sent in a non Tagged Queue fashion so that the Host
+ Adapter and Target can establish Synchronous Transfer before Queue Tag
+ messages can interfere with the Synchronous Negotiation message. By
+ waiting to enable tagged Queuing until after the first 16 read/write
+ commands have been sent, it is assured that the Tagged Queuing message
+ will not occur while the partition table is printed.
+ */
+ if ((HostAdapter->TaggedQueuingPermitted & (1 << TargetID)) &&
+ Command->device->tagged_supported &&
+ (EnableTQ = HostAdapter->ReadWriteOperationCount[TargetID] - 16) >= 0)
+ {
+ BusLogic_QueueTag_T QueueTag = BusLogic_SimpleQueueTag;
+ unsigned long CurrentTime = jiffies;
+ if (EnableTQ == 0)
+ printk("scsi%d: Tagged Queuing now active for Target %d\n",
+ HostAdapter->HostNumber, TargetID);
+ /*
+ When using Tagged Queuing with Simple Queue Tags, it appears that disk
+ drive controllers do not guarantee that a queued command will not
+ remain in a disconnected state indefinitely if commands that read or
+ write nearer the head position continue to arrive without interruption.
+ Therefore, for each Target Device this driver keeps track of the last
+ time either the queue was empty or an Ordered Queue Tag was issued. If
+ more than 2 seconds have elapsed since this last sequence point, this
+ command will be issued with an Ordered Queue Tag rather than a Simple
+ Queue Tag, which forces the Target Device to complete all previously
+ queued commands before this command may be executed.
+ */
+ if (HostAdapter->QueuedOperationCount[TargetID] == 0)
+ HostAdapter->LastSequencePoint[TargetID] = CurrentTime;
+ else if (CurrentTime - HostAdapter->LastSequencePoint[TargetID] > 2*HZ)
+ {
+ HostAdapter->LastSequencePoint[TargetID] = CurrentTime;
+ QueueTag = BusLogic_OrderedQueueTag;
+ }
+ if (HostAdapter->HostWideSCSI)
+ {
+ CCB->WideModeTagEnable = true;
+ CCB->WideModeQueueTag = QueueTag;
+ }
+ else
+ {
+ CCB->TagEnable = true;
+ CCB->QueueTag = QueueTag;
+ }
+ }
+ memcpy(CCB->CDB, CDB, CDB_Length);
+ CCB->SenseDataPointer = (SCSI_SenseData_T *) &Command->sense_buffer;
+ CCB->Command = Command;
+ Command->scsi_done = CompletionRoutine;
+ /*
+ Place the CCB in an Outgoing Mailbox. If there are no Outgoing
+ Mailboxes available, return a result code of Bus Busy so that this
+ Command will be retried.
+ */
+ if (!(BusLogic_WriteOutgoingMailbox(HostAdapter,
+ BusLogic_MailboxStartCommand, CCB)))
+ {
+ printk("scsi%d: cannot write Outgoing Mailbox\n",
+ HostAdapter->HostNumber);
+ BusLogic_DeallocateCCB(CCB);
+ Command->result = DID_BUS_BUSY << 16;
+ CompletionRoutine(Command);
+ }
+ return 0;
+}
+
+
+/*
+ BusLogic_AbortCommand aborts Command if possible.
+*/
+
+int BusLogic_AbortCommand(SCSI_Command_T *Command)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ unsigned long CommandPID = Command->pid;
+ unsigned char InterruptRegister;
+ BusLogic_CCB_T *CCB;
+ int Result;
+ /*
+ If the Host Adapter has posted an interrupt but the Interrupt Handler
+ has not been called for some reason (i.e. the interrupt was lost), try
+ calling the Interrupt Handler directly to process the commands that
+ have been completed.
+ */
+ InterruptRegister = BusLogic_ReadInterruptRegister(HostAdapter);
+ if (InterruptRegister & BusLogic_InterruptValid)
+ {
+ unsigned long ProcessorFlags;
+ printk("scsi%d: Recovering Lost/Delayed Interrupt for IRQ Channel %d\n",
+ HostAdapter->HostNumber, HostAdapter->IRQ_Channel);
+ save_flags(ProcessorFlags);
+ cli();
+ BusLogic_InterruptHandler(HostAdapter->IRQ_Channel, NULL);
+ restore_flags(ProcessorFlags);
+ return SCSI_ABORT_SNOOZE;
+ }
+ /*
+ Find the CCB to be aborted if possible.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Command == Command) break;
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ if (CCB == NULL)
+ {
+ printk("scsi%d: Unable to Abort Command to Target %d - No CCB Found\n",
+ HostAdapter->HostNumber, Command->target);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+ /*
+ Briefly pause to see if this command will complete.
+ */
+ printk("scsi%d: Pausing briefly to see if CCB #%d "
+ "to Target %d will complete\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ BusLogic_Delay(2);
+ /*
+ If this CCB is still Active and still refers to the same Command, then
+ actually aborting this Command is necessary.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ Result = SCSI_ABORT_NOT_RUNNING;
+ if (CCB->Status == BusLogic_CCB_Active &&
+ CCB->Command == Command && Command->pid == CommandPID)
+ {
+ /*
+ Attempt to abort this CCB.
+ */
+ if (BusLogic_WriteOutgoingMailbox(HostAdapter,
+ BusLogic_MailboxAbortCommand, CCB))
+ {
+ printk("scsi%d: Aborting CCB #%d to Target %d\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ Result = SCSI_ABORT_PENDING;
+ }
+ else
+ {
+ printk("scsi%d: Unable to Abort CCB #%d to Target %d - "
+ "No Outgoing Mailboxes\n", HostAdapter->HostNumber,
+ CCB->SerialNumber, CCB->TargetID);
+ Result = SCSI_ABORT_BUSY;
+ }
+ }
+ else printk("scsi%d: CCB #%d to Target %d completed\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, CCB->TargetID);
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return Result;
+}
+
+
+/*
+ BusLogic_ResetHostAdapter resets Host Adapter if possible, marking all
+ currently executing SCSI commands as having been reset, as well as
+ the specified Command if non-NULL.
+*/
+
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Command_T *Command)
+{
+ BusLogic_CCB_T *CCB;
+ if (Command == NULL)
+ printk("scsi%d: Resetting %s due to SCSI Reset State Interrupt\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName);
+ else printk("scsi%d: Resetting %s due to Target %d\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName, Command->target);
+ /*
+ Attempt to Reset and Reinitialize the Host Adapter.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ if (!(BusLogic_HardResetHostAdapter(HostAdapter) &&
+ BusLogic_InitializeHostAdapter(HostAdapter)))
+ {
+ printk("scsi%d: Resetting %s Failed\n",
+ HostAdapter->HostNumber, HostAdapter->BoardName);
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ return SCSI_RESET_ERROR;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ /*
+ Wait a few seconds between the Host Adapter Hard Reset which initiates
+ a SCSI Bus Reset and issuing any SCSI commands. Some SCSI devices get
+ confused if they receive SCSI commands too soon after a SCSI Bus Reset.
+ */
+ BusLogic_Delay(HostAdapter->BusSettleTime);
+ /*
+ Mark all currently executing CCBs as having been reset.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Active)
+ {
+ CCB->Status = BusLogic_CCB_Reset;
+ if (CCB->Command == Command)
+ {
+ CCB->Command = NULL;
+ /*
+ Disable Tagged Queuing if it was active for this Target Device.
+ */
+ if (((HostAdapter->HostWideSCSI && CCB->WideModeTagEnable) ||
+ (!HostAdapter->HostWideSCSI && CCB->TagEnable)) &&
+ (HostAdapter->TaggedQueuingPermitted & (1 << CCB->TargetID)))
+ {
+ HostAdapter->TaggedQueuingPermitted &= ~(1 << CCB->TargetID);
+ printk("scsi%d: Tagged Queuing now disabled for Target %d\n",
+ HostAdapter->HostNumber, CCB->TargetID);
+ }
+ }
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ /*
+ Perform completion processing for the Command being Reset.
+ */
+ if (Command != NULL)
+ {
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ }
+ /*
+ Perform completion processing for any other active CCBs.
+ */
+ for (CCB = HostAdapter->All_CCBs; CCB != NULL; CCB = CCB->NextAll)
+ if (CCB->Status == BusLogic_CCB_Reset)
+ {
+ Command = CCB->Command;
+ BusLogic_DeallocateCCB(CCB);
+ if (Command != NULL)
+ {
+ Command->result = DID_RESET << 16;
+ Command->scsi_done(Command);
+ }
+ }
+ return SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET;
+}
+
+
+/*
+ BusLogic_BusDeviceReset sends a Bus Device Reset to the Target
+ associated with Command.
+*/
+
+static int BusLogic_BusDeviceReset(BusLogic_HostAdapter_T *HostAdapter,
+ SCSI_Command_T *Command)
+{
+ BusLogic_CCB_T *CCB = BusLogic_AllocateCCB(HostAdapter), *XCCB;
+ unsigned char TargetID = Command->target;
+ /*
+ If sending a Bus Device Reset is impossible, attempt a full Host
+ Adapter Hard Reset and SCSI Bus Reset.
+ */
+ if (CCB == NULL)
+ return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ printk("scsi%d: Sending Bus Device Reset CCB #%d to Target %d\n",
+ HostAdapter->HostNumber, CCB->SerialNumber, TargetID);
+ CCB->Opcode = BusLogic_SCSIBusDeviceReset;
+ CCB->TargetID = TargetID;
+ CCB->Command = Command;
+ /*
+ If there is a currently executing CCB in the Host Adapter for this Command,
+ then an Incoming Mailbox entry will be made with a completion code of
+ BusLogic_HostAdapterAssertedBusDeviceReset. Otherwise, the CCB's Command
+ field will be left pointing to the Command so that the interrupt for the
+ completion of the Bus Device Reset can call the Completion Routine for the
+ Command.
+ */
+ BusLogic_LockHostAdapter(HostAdapter);
+ for (XCCB = HostAdapter->All_CCBs; XCCB != NULL; XCCB = XCCB->NextAll)
+ if (XCCB->Command == Command && XCCB->Status == BusLogic_CCB_Active)
+ {
+ CCB->Command = NULL;
+ /*
+ Disable Tagged Queuing if it was active for this Target Device.
+ */
+ if (((HostAdapter->HostWideSCSI && XCCB->WideModeTagEnable) ||
+ (!HostAdapter->HostWideSCSI && XCCB->TagEnable)) &&
+ (HostAdapter->TaggedQueuingPermitted & (1 << TargetID)))
+ {
+ HostAdapter->TaggedQueuingPermitted &= ~(1 << TargetID);
+ printk("scsi%d: Tagged Queuing now disabled for Target %d\n",
+ HostAdapter->HostNumber, TargetID);
+ }
+ break;
+ }
+ BusLogic_UnlockHostAdapter(HostAdapter);
+ /*
+ Attempt to write an Outgoing Mailbox with the Bus Device Reset CCB.
+ If sending a Bus Device Reset is impossible, attempt a full Host
+ Adapter Hard Reset and SCSI Bus Reset.
+ */
+ if (!(BusLogic_WriteOutgoingMailbox(HostAdapter,
+ BusLogic_MailboxStartCommand, CCB)))
+ {
+ printk("scsi%d: cannot write Outgoing Mailbox for Bus Device Reset\n",
+ HostAdapter->HostNumber);
+ BusLogic_DeallocateCCB(CCB);
+ return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ }
+ HostAdapter->ReadWriteOperationCount[TargetID] = 0;
+ HostAdapter->QueuedOperationCount[TargetID] = 0;
+ return SCSI_RESET_PENDING;
+}
+
+
+/*
+ BusLogic_ResetCommand takes appropriate action to reset Command.
+*/
+
+int BusLogic_ResetCommand(SCSI_Command_T *Command)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Command->host->hostdata;
+ unsigned char TargetID = Command->target;
+ unsigned char ErrorRecoveryOption =
+ HostAdapter->ErrorRecoveryOption[TargetID];
+ if (ErrorRecoveryOption == BusLogic_ErrorRecoveryDefault)
+ if (Command->host->suggest_bus_reset)
+ ErrorRecoveryOption = BusLogic_ErrorRecoveryHardReset;
+ else ErrorRecoveryOption = BusLogic_ErrorRecoveryBusDeviceReset;
+ switch (ErrorRecoveryOption)
+ {
+ case BusLogic_ErrorRecoveryHardReset:
+ return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ case BusLogic_ErrorRecoveryBusDeviceReset:
+ if (HostAdapter->CommandSuccessfulFlag[TargetID])
+ {
+ HostAdapter->CommandSuccessfulFlag[TargetID] = false;
+ return BusLogic_BusDeviceReset(HostAdapter, Command);
+ }
+ else return BusLogic_ResetHostAdapter(HostAdapter, Command);
+ }
+ printk("scsi%d: Error Recovery for Target %d Suppressed\n",
+ HostAdapter->HostNumber, TargetID);
+ return SCSI_RESET_PUNT;
+}
+
+
+/*
+ BusLogic_BIOSDiskParameters returns the Heads/Sectors/Cylinders BIOS Disk
+ Parameters for Disk. The default disk geometry is 64 heads, 32 sectors, and
+ the appropriate number of cylinders so as not to exceed drive capacity. In
+ order for disks equal to or larger than 1 GB to be addressable by the BIOS
+ without exceeding the BIOS limitation of 1024 cylinders, Extended Translation
+ may be enabled in AutoSCSI on "C" Series boards or by a dip switch setting
+ on older boards. With Extended Translation enabled, drives between 1 GB
+ inclusive and 2 GB exclusive are given a disk geometry of 128 heads and 32
+ sectors, and drives between 2 GB inclusive and 8 GB exclusive are given a
+ disk geometry of 255 heads and 63 sectors. On "C" Series boards the firmware
+ can be queried for the precise translation in effect for each drive
+ individually, but there is really no need to do so since we know the total
+ capacity of the drive and whether Extended Translation is enabled, hence we
+ can deduce the BIOS disk geometry that must be in effect.
+*/
+
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *Disk, KernelDevice_T Device,
+ int *Parameters)
+{
+ BusLogic_HostAdapter_T *HostAdapter =
+ (BusLogic_HostAdapter_T *) Disk->device->host->hostdata;
+ BIOS_DiskParameters_T *DiskParameters = (BIOS_DiskParameters_T *) Parameters;
+ if (HostAdapter->ExtendedTranslation &&
+ Disk->capacity >= 2*1024*1024 /* 1 GB in 512 byte sectors */)
+ if (Disk->capacity >= 4*1024*1024 /* 2 GB in 512 byte sectors */)
+ {
+ DiskParameters->Heads = 255;
+ DiskParameters->Sectors = 63;
+ }
+ else
+ {
+ DiskParameters->Heads = 128;
+ DiskParameters->Sectors = 32;
+ }
+ else
+ {
+ DiskParameters->Heads = 64;
+ DiskParameters->Sectors = 32;
+ }
+ DiskParameters->Cylinders =
+ Disk->capacity / (DiskParameters->Heads * DiskParameters->Sectors);
+ return 0;
+}
+
+
+/*
+ BusLogic_Setup handles processing of Kernel Command Line Arguments.
+
+ For the BusLogic driver, a kernel command line entry comprises the driver
+ identifier "BusLogic=" optionally followed by a comma-separated sequence of
+ integers and then optionally followed by a comma-separated sequence of
+ strings. Each command line entry applies to one BusLogic Host Adapter.
+ Multiple command line entries may be used in systems which contain multiple
+ BusLogic Host Adapters.
+
+ The first integer specified is the I/O Address at which the Host Adapter is
+ located. If unspecified, it defaults to 0 which means to apply this entry to
+ the first BusLogic Host Adapter found during the default probe sequence. If
+ any I/O Address parameters are provided on the command line, then the default
+ probe sequence is omitted.
+
+ The second integer specified is the number of Concurrent Commands per Logical
+ Unit to allow for Target Devices on the Host Adapter. If unspecified, it
+ defaults to 0 which means to use the value of BusLogic_Concurrency for
+ non-ISA Host Adapters, or BusLogic_Concurrency_ISA for ISA Host Adapters.
+
+ The third integer specified is the Bus Settle Time in seconds. This is
+ the amount of time to wait between a Host Adapter Hard Reset which initiates
+ a SCSI Bus Reset and issuing any SCSI commands. If unspecified, it defaults
+ to 0 which means to use the value of BusLogic_DefaultBusSettleTime.
+
+ The fourth integer specified is the Local Options. If unspecified, it
+ defaults to 0. Note that Local Options are only applied to a specific Host
+ Adapter.
+
+ The fifth integer specified is the Global Options. If unspecified, it
+ defaults to 0. Note that Global Options are applied across all Host
+ Adapters.
+
+ The string options are used to provide control over Tagged Queuing and Error
+ Recovery. If both Tagged Queuing and Error Recovery strings are provided, the
+ Tagged Queuing specification string must come first.
+
+ The Tagged Queuing specification begins with "TQ:" and allows for explicitly
+ specifying whether Tagged Queuing is permitted on Target Devices that support
+ it. The following specification options are available:
+
+ TQ:Default Tagged Queuing will be permitted based on the firmware
+ version of the BusLogic Host Adapter and based on
+ whether the Concurrency value allows queuing multiple
+ commands.
+
+ TQ:Enable Tagged Queuing will be enabled for all Target Devices
+ on this Host Adapter overriding any limitation that
+ would otherwise be imposed based on the Host Adapter
+ firmware version.
+
+ TQ:Disable Tagged Queuing will be disabled for all Target Devices
+ on this Host Adapter.
+
+ TQ:<Per-Target-Spec> Tagged Queuing will be controlled individually for each
+ Target Device. <Per-Target-Spec> is a sequence of "Y",
+ "N", and "X" characters. "Y" enabled Tagged Queuing,
+ "N" disables Tagged Queuing, and "X" accepts the
+ default based on the firmware version. The first
+ character refers to Target 0, the second to Target 1,
+ and so on; if the sequence of "Y", "N", and "X"
+ characters does not cover all the Target Devices,
+ unspecified characters are assumed to be "X".
+
+ Note that explicitly requesting Tagged Queuing may lead to problems; this
+ facility is provided primarily to allow disabling Tagged Queuing on Target
+ Devices that do not implement it correctly.
+
+ The Error Recovery specification begins with "ER:" and allows for explicitly
+ specifying the Error Recovery action to be performed when ResetCommand is
+ called due to a SCSI Command failing to complete successfully. The following
+ specification options are available:
+
+ ER:Default Error Recovery will select between the Hard Reset and
+ Bus Device Reset options based on the recommendation
+ of the SCSI Subsystem.
+
+ ER:HardReset Error Recovery will initiate a Host Adapter Hard Reset
+ which also causes a SCSI Bus Reset.
+
+ ER:BusDeviceReset Error Recovery will send a Bus Device Reset message to
+ the individual Target Device causing the error. If
+ Error Recovery is again initiated for this Target
+ Device and no SCSI Command to this Target Device has
+ completed successfully since the Bus Device Reset
+ message was sent, then a Hard Reset will be attempted.
+
+ ER:None Error Recovery will be suppressed. This option should
+ only be selected if a SCSI Bus Reset or Bus Device
+ Reset will cause the Target Device to fail completely
+ and unrecoverably.
+
+ ER:<Per-Target-Spec> Error Recovery will be controlled individually for each
+ Target Device. <Per-Target-Spec> is a sequence of "D",
+ "H", "B", and "N" characters. "D" selects Default, "H"
+ selects Hard Reset, "B" selects Bus Device Reset, and
+ "N" selects None. The first character refers to Target
+ 0, the second to Target 1, and so on; if the sequence
+ of "D", "H", "B", and "N" characters does not cover all
+ the Target Devices, unspecified characters are assumed
+ to be "D".
+*/
+
+void BusLogic_Setup(char *Strings, int *Integers)
+{
+ BusLogic_CommandLineEntry_T *CommandLineEntry =
+ &BusLogic_CommandLineEntries[BusLogic_CommandLineEntryCount++];
+ static int ProbeListIndex = 0;
+ int IntegerCount = Integers[0], TargetID, i;
+ CommandLineEntry->IO_Address = 0;
+ CommandLineEntry->Concurrency = 0;
+ CommandLineEntry->BusSettleTime = 0;
+ CommandLineEntry->LocalOptions = 0;
+ CommandLineEntry->TaggedQueuingPermitted = 0;
+ CommandLineEntry->TaggedQueuingPermittedMask = 0;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryDefault,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ if (IntegerCount > 5)
+ printk("BusLogic: Unexpected Command Line Integers ignored\n");
+ if (IntegerCount >= 1)
+ {
+ unsigned short IO_Address = Integers[1];
+ if (IO_Address > 0)
+ {
+ for (i = 0; ; i++)
+ if (BusLogic_IO_StandardAddresses[i] == 0)
+ {
+ printk("BusLogic: Invalid Command Line Entry "
+ "(illegal I/O Address 0x%X)\n", IO_Address);
+ return;
+ }
+ else if (i < ProbeListIndex &&
+ IO_Address == BusLogic_IO_AddressProbeList[i])
+ {
+ printk("BusLogic: Invalid Command Line Entry "
+ "(duplicate I/O Address 0x%X)\n", IO_Address);
+ return;
+ }
+ else if (IO_Address >= 0x1000 ||
+ IO_Address == BusLogic_IO_StandardAddresses[i]) break;
+ BusLogic_IO_AddressProbeList[ProbeListIndex++] = IO_Address;
+ BusLogic_IO_AddressProbeList[ProbeListIndex] = 0;
+ }
+ CommandLineEntry->IO_Address = IO_Address;
+ }
+ if (IntegerCount >= 2)
+ {
+ unsigned short Concurrency = Integers[2];
+ if (Concurrency > BusLogic_MailboxCount)
+ {
+ printk("BusLogic: Invalid Command Line Entry "
+ "(illegal Concurrency %d)\n", Concurrency);
+ return;
+ }
+ CommandLineEntry->Concurrency = Concurrency;
+ }
+ if (IntegerCount >= 3)
+ CommandLineEntry->BusSettleTime = Integers[3];
+ if (IntegerCount >= 4)
+ CommandLineEntry->LocalOptions = Integers[4];
+ if (IntegerCount >= 5)
+ BusLogic_GlobalOptions |= Integers[5];
+ if (!(BusLogic_CommandLineEntryCount == 0 || ProbeListIndex == 0 ||
+ BusLogic_CommandLineEntryCount == ProbeListIndex))
+ {
+ printk("BusLogic: Invalid Command Line Entry "
+ "(all or no I/O Addresses must be specified)\n");
+ return;
+ }
+ if (Strings == NULL) return;
+ if (strncmp(Strings, "TQ:", 3) == 0)
+ {
+ Strings += 3;
+ if (strncmp(Strings, "Default", 7) == 0)
+ Strings += 7;
+ else if (strncmp(Strings, "Enable", 6) == 0)
+ {
+ Strings += 6;
+ CommandLineEntry->TaggedQueuingPermitted = 0xFFFF;
+ CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+ }
+ else if (strncmp(Strings, "Disable", 7) == 0)
+ {
+ Strings += 7;
+ CommandLineEntry->TaggedQueuingPermitted = 0x0000;
+ CommandLineEntry->TaggedQueuingPermittedMask = 0xFFFF;
+ }
+ else
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+ switch (*Strings++)
+ {
+ case 'Y':
+ CommandLineEntry->TaggedQueuingPermitted |= 1 << TargetID;
+ CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+ break;
+ case 'N':
+ CommandLineEntry->TaggedQueuingPermittedMask |= 1 << TargetID;
+ break;
+ case 'X':
+ break;
+ default:
+ Strings--;
+ TargetID = BusLogic_MaxTargetIDs;
+ break;
+ }
+ }
+ if (*Strings == ',') Strings++;
+ if (strncmp(Strings, "ER:", 3) == 0)
+ {
+ Strings += 3;
+ if (strncmp(Strings, "Default", 7) == 0)
+ Strings += 7;
+ else if (strncmp(Strings, "HardReset", 9) == 0)
+ {
+ Strings += 9;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryHardReset,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ }
+ else if (strncmp(Strings, "BusDeviceReset", 14) == 0)
+ {
+ Strings += 14;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryBusDeviceReset,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ }
+ else if (strncmp(Strings, "None", 4) == 0)
+ {
+ Strings += 4;
+ memset(CommandLineEntry->ErrorRecoveryOption,
+ BusLogic_ErrorRecoveryNone,
+ sizeof(CommandLineEntry->ErrorRecoveryOption));
+ }
+ else
+ for (TargetID = 0; TargetID < BusLogic_MaxTargetIDs; TargetID++)
+ switch (*Strings++)
+ {
+ case 'D':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryDefault;
+ break;
+ case 'H':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryHardReset;
+ break;
+ case 'B':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryBusDeviceReset;
+ break;
+ case 'N':
+ CommandLineEntry->ErrorRecoveryOption[TargetID] =
+ BusLogic_ErrorRecoveryNone;
+ break;
+ default:
+ Strings--;
+ TargetID = BusLogic_MaxTargetIDs;
+ break;
+ }
+ }
+ if (*Strings != '\0')
+ printk("BusLogic: Unexpected Command Line String '%s' ignored\n", Strings);
+}
+
+
+/*
+ Include Module support if requested.
+*/
+
+
+#ifdef MODULE
+
+SCSI_Host_Template_T driver_template = BUSLOGIC;
+
+#include "scsi_module.c"
+
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/BusLogic.h b/i386/i386at/gpl/linux/scsi/BusLogic.h
new file mode 100644
index 00000000..69048e9f
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/BusLogic.h
@@ -0,0 +1,977 @@
+/*
+
+ Linux Driver for BusLogic MultiMaster SCSI Host Adapters
+
+ Copyright 1995 by Leonard N. Zubkoff <lnz@dandelion.com>
+
+ See BusLogic.c for licensing information.
+
+*/
+
+
+/*
+ Define types for some of the structures that interface with the rest
+ of the Linux Kernel and SCSI Subsystem.
+*/
+
+typedef struct pt_regs Registers_T;
+typedef Scsi_Host_Template SCSI_Host_Template_T;
+typedef struct Scsi_Host SCSI_Host_T;
+typedef struct scsi_disk SCSI_Disk_T;
+typedef struct scsi_cmnd SCSI_Command_T;
+typedef struct scatterlist SCSI_ScatterList_T;
+typedef kdev_t KernelDevice_T;
+
+
+/*
+ Define prototypes for the BusLogic Driver Interface Functions.
+*/
+
+const char *BusLogic_DriverInfo(SCSI_Host_T *);
+int BusLogic_DetectHostAdapter(SCSI_Host_Template_T *);
+int BusLogic_ReleaseHostAdapter(SCSI_Host_T *);
+int BusLogic_QueueCommand(SCSI_Command_T *,
+ void (*CompletionRoutine)(SCSI_Command_T *));
+int BusLogic_AbortCommand(SCSI_Command_T *);
+int BusLogic_ResetCommand(SCSI_Command_T *);
+int BusLogic_BIOSDiskParameters(SCSI_Disk_T *, KernelDevice_T, int *);
+
+
+/*
+ Define the BusLogic SCSI Host Template structure.
+*/
+
+#define BUSLOGIC \
+ { NULL, /* Next */ \
+ NULL, /* Usage Count Pointer */ \
+ NULL, /* /proc Directory Entry */ \
+ NULL, /* /proc Info Function */ \
+ "BusLogic", /* Driver Name */ \
+ BusLogic_DetectHostAdapter, /* Detect Host Adapter */ \
+ BusLogic_ReleaseHostAdapter, /* Release Host Adapter */ \
+ BusLogic_DriverInfo, /* Driver Info Function */ \
+ NULL, /* Command Function */ \
+ BusLogic_QueueCommand, /* Queue Command Function */ \
+ BusLogic_AbortCommand, /* Abort Command Function */ \
+ BusLogic_ResetCommand, /* Reset Command Function */ \
+ NULL, /* Slave Attach Function */ \
+ BusLogic_BIOSDiskParameters, /* Disk BIOS Parameters */ \
+ 0, /* Can Queue */ \
+ 0, /* This ID */ \
+ 0, /* Scatter/Gather Table Size */ \
+ 0, /* SCSI Commands per LUN */ \
+ 0, /* Present */ \
+ 1, /* Default Unchecked ISA DMA */ \
+ ENABLE_CLUSTERING } /* Enable Clustering */
+
+
+/*
+ BusLogic_DriverVersion protects the private portion of this file.
+*/
+
+#ifdef BusLogic_DriverVersion
+
+
+/*
+ Define the maximum number of BusLogic Host Adapters that are supported.
+*/
+
+#define BusLogic_MaxHostAdapters 10
+
+
+/*
+ Define the maximum number of I/O Addresses that may be probed.
+*/
+
+#define BusLogic_IO_MaxProbeAddresses 16
+
+
+/*
+ Define the maximum number of Target IDs supported by this driver.
+*/
+
+#define BusLogic_MaxTargetIDs 16
+
+
+/*
+ Define the number of Incoming and Outgoing Mailboxes used by this driver.
+ The maximum possible value is 255, since the MailboxCount parameter to the
+ Initialize Extended Mailbox command is limited to a single byte.
+*/
+
+#define BusLogic_MailboxCount 64
+
+
+/*
+ Define the number of Command Control Blocks (CCBs) to create during
+ initialization for each Host Adapter. Additional CCBs will be allocated
+ if necessary as commands are queued.
+*/
+
+#define BusLogic_InitialCCBs 32
+
+
+/*
+ Define the maximum number of Scatter/Gather Segments used by this driver.
+ For maximum performance, it is important that this limit be at least as
+ large as the maximum single request generated by the routine make_request.
+*/
+
+#define BusLogic_ScatterGatherLimit 128
+
+
+/*
+ Define the default number of Concurrent Commands per Logical Unit to allow
+ for Target Devices depending on whether or not ISA bounce buffers are
+ required.
+*/
+
+#define BusLogic_Concurrency 7
+#define BusLogic_Concurrency_BB 1
+
+
+/*
+ Define the default amount of time in seconds to wait between a Host Adapter
+ Hard Reset which initiates a SCSI Bus Reset and issuing any SCSI commands.
+ Some SCSI devices get confused if they receive SCSI commands too soon after
+ a SCSI Bus Reset.
+*/
+
+#define BusLogic_DefaultBusSettleTime 2
+
+
+/*
+ Define the possible Local Options.
+*/
+
+#define BusLogic_InhibitTargetInquiry 1
+
+
+/*
+ Define the possible Global Options.
+*/
+
+#define BusLogic_TraceProbe 1
+#define BusLogic_TraceHardReset 2
+#define BusLogic_TraceConfiguration 4
+#define BusLogic_TraceErrors 8
+
+
+/*
+ Define the possible Error Recovery Options.
+*/
+
+#define BusLogic_ErrorRecoveryDefault 0
+#define BusLogic_ErrorRecoveryHardReset 1
+#define BusLogic_ErrorRecoveryBusDeviceReset 2
+#define BusLogic_ErrorRecoveryNone 3
+
+static char
+ *BusLogic_ErrorRecoveryOptions[] =
+ { "Default", "Hard Reset", "Bus Device Reset", "None" },
+ *BusLogic_ErrorRecoveryOptions2[] =
+ { "D", "H", "B", "N" };
+
+
+/*
+ Define a boolean data type.
+*/
+
+#define false 0
+#define true 1
+typedef unsigned char boolean;
+
+
+/*
+ Define the BusLogic SCSI Host Adapter I/O Register Offsets.
+*/
+
+#define BusLogic_IO_PortCount 4 /* I/O Registers */
+#define BusLogic_ControlRegister 0 /* WO register */
+#define BusLogic_StatusRegister 0 /* RO register */
+#define BusLogic_CommandParameterRegister 1 /* WO register */
+#define BusLogic_DataInRegister 1 /* RO register */
+#define BusLogic_InterruptRegister 2 /* RO register */
+#define BusLogic_GeometryRegister 3 /* RO, undocumented */
+
+
+/*
+ Define the bits in the write-only Control Register.
+*/
+
+#define BusLogic_ReservedCR 0x0F
+#define BusLogic_SCSIBusReset 0x10
+#define BusLogic_InterruptReset 0x20
+#define BusLogic_SoftReset 0x40
+#define BusLogic_HardReset 0x80
+
+
+/*
+ Define the bits in the read-only Status Register.
+*/
+
+#define BusLogic_CommandInvalid 0x01
+#define BusLogic_ReservedSR 0x02
+#define BusLogic_DataInRegisterReady 0x04
+#define BusLogic_CommandParameterRegisterBusy 0x08
+#define BusLogic_HostAdapterReady 0x10
+#define BusLogic_InitializationRequired 0x20
+#define BusLogic_DiagnosticFailure 0x40
+#define BusLogic_DiagnosticActive 0x80
+
+
+/*
+ Define the bits in the read-only Interrupt Register.
+*/
+
+#define BusLogic_IncomingMailboxLoaded 0x01
+#define BusLogic_OutgoingMailboxAvailable 0x02
+#define BusLogic_CommandComplete 0x04
+#define BusLogic_SCSIResetState 0x08
+#define BusLogic_ReservedIR 0x70
+#define BusLogic_InterruptValid 0x80
+
+
+/*
+ Define the bits in the undocumented read-only Geometry Register.
+*/
+
+#define BusLogic_Drive0Geometry 0x03
+#define BusLogic_Drive1Geometry 0x0C
+#define BusLogic_ReservedGR 0x70
+#define BusLogic_ExtendedTranslationEnabled 0x80
+
+
+/*
+ Define the BusLogic SCSI Host Adapter Command Register Operation Codes.
+*/
+
+typedef enum
+{
+ BusLogic_TestCommandCompleteInterrupt = 0x00, /* documented */
+ BusLogic_InitializeMailbox = 0x01, /* documented */
+ BusLogic_StartMailboxCommand = 0x02, /* documented */
+ BusLogic_StartBIOSCommand = 0x03, /* documented */
+ BusLogic_InquireBoardID = 0x04, /* documented */
+ BusLogic_EnableOutgoingMailboxAvailableIRQ = 0x05, /* documented */
+ BusLogic_SetSCSISelectionTimeout = 0x06, /* documented */
+ BusLogic_SetPreemptTimeOnBus = 0x07, /* documented */
+ BusLogic_SetTimeOffBus = 0x08, /* ISA Bus only */
+ BusLogic_SetBusTransferRate = 0x09, /* ISA Bus only */
+ BusLogic_InquireInstalledDevicesID0to7 = 0x0A, /* documented */
+ BusLogic_InquireConfiguration = 0x0B, /* documented */
+ BusLogic_SetTargetMode = 0x0C, /* now undocumented */
+ BusLogic_InquireSetupInformation = 0x0D, /* documented */
+ BusLogic_WriteAdapterLocalRAM = 0x1A, /* documented */
+ BusLogic_ReadAdapterLocalRAM = 0x1B, /* documented */
+ BusLogic_WriteBusMasterChipFIFO = 0x1C, /* documented */
+ BusLogic_ReadBusMasterChipFIFO = 0x1D, /* documented */
+ BusLogic_EchoCommandData = 0x1F, /* documented */
+ BusLogic_HostAdapterDiagnostic = 0x20, /* documented */
+ BusLogic_SetAdapterOptions = 0x21, /* documented */
+ BusLogic_InquireInstalledDevicesID8to15 = 0x23, /* Wide only */
+ BusLogic_InitializeExtendedMailbox = 0x81, /* documented */
+ BusLogic_InquireFirmwareVersion3rdDigit = 0x84, /* undocumented */
+ BusLogic_InquireFirmwareVersionLetter = 0x85, /* undocumented */
+ BusLogic_InquireBoardModelNumber = 0x8B, /* undocumented */
+ BusLogic_InquireSynchronousPeriod = 0x8C, /* undocumented */
+ BusLogic_InquireExtendedSetupInformation = 0x8D, /* documented */
+ BusLogic_EnableStrictRoundRobinMode = 0x8F, /* documented */
+ BusLogic_ModifyIOAddress = 0x95, /* PCI only */
+ BusLogic_EnableWideModeCCB = 0x96 /* Wide only */
+}
+BusLogic_OperationCode_T;
+
+
+/*
+ Define the Inquire Board ID reply structure.
+*/
+
+typedef struct BusLogic_BoardID
+{
+ unsigned char BoardType;
+ unsigned char CustomFeatures;
+ unsigned char FirmwareVersion1stDigit;
+ unsigned char FirmwareVersion2ndDigit;
+}
+BusLogic_BoardID_T;
+
+
+/*
+ Define the Inquire Installed Devices ID 0 to 7 and Inquire Installed
+ Devices ID 8 to 15 reply type. For each Target ID, a byte is returned
+ where bit 0 set indicates that Logical Unit 0 exists, bit 1 set indicates
+ that Logical Unit 1 exists, and so on.
+*/
+
+typedef unsigned char BusLogic_InstalledDevices8_T[8];
+
+typedef unsigned char BusLogic_InstalledDevices_T[BusLogic_MaxTargetIDs];
+
+
+/*
+ Define the Inquire Configuration reply structure.
+*/
+
+typedef struct BusLogic_Configuration
+{
+ unsigned char :5; /* Byte 0: DMA Channel */
+ boolean DMA_Channel5:1;
+ boolean DMA_Channel6:1;
+ boolean DMA_Channel7:1;
+ boolean IRQ_Channel9:1; /* Byte 1: IRQ Channel */
+ boolean IRQ_Channel10:1;
+ boolean IRQ_Channel11:1;
+ boolean IRQ_Channel12:1;
+ unsigned char :1;
+ boolean IRQ_Channel14:1;
+ boolean IRQ_Channel15:1;
+ unsigned char :1;
+ unsigned char HostAdapterID:4; /* Byte 2: Host Adapter ID */
+ unsigned char :4;
+}
+BusLogic_Configuration_T;
+
+
+/*
+ Define the Inquire Setup Information reply structure.
+*/
+
+typedef struct BusLogic_SynchronousValue
+{
+ unsigned char Offset:4;
+ unsigned char TransferPeriod:3;
+ boolean Synchronous:1;
+}
+BusLogic_SynchronousValue_T;
+
+typedef BusLogic_SynchronousValue_T
+ BusLogic_SynchronousValues8_T[8];
+
+typedef BusLogic_SynchronousValue_T
+ BusLogic_SynchronousValues_T[BusLogic_MaxTargetIDs];
+
+typedef struct BusLogic_SetupInformation
+{
+ boolean SynchronousInitiationEnabled:1; /* Byte 0 */
+ boolean ParityCheckEnabled:1;
+ unsigned char :6;
+ unsigned char BusTransferRate; /* Byte 1 */
+ unsigned char PreemptTimeOnBus; /* Byte 2 */
+ unsigned char TimeOffBus; /* Byte 3 */
+ unsigned char MailboxCount; /* Byte 4 */
+ unsigned char MailboxAddress[3]; /* Bytes 5-7 */
+ BusLogic_SynchronousValues8_T SynchronousValuesID0to7; /* Bytes 8-15 */
+ unsigned char DisconnectPermittedID0to7; /* Byte 16 */
+ unsigned char Signature; /* Byte 17 */
+ unsigned char CharacterD; /* Byte 18 */
+ unsigned char BusLetter; /* Byte 19 */
+ unsigned char :8; /* Byte 20 */
+ unsigned char :8; /* Byte 21 */
+ BusLogic_SynchronousValues8_T SynchronousValuesID8to15; /* Bytes 22-29 */
+ unsigned char DisconnectPermittedID8to15; /* Byte 30 */
+}
+BusLogic_SetupInformation_T;
+
+
+/*
+ Define the Initialize Extended Mailbox request structure.
+*/
+
+typedef struct BusLogic_ExtendedMailboxRequest
+{
+ unsigned char MailboxCount;
+ void *BaseMailboxAddress __attribute__ ((packed));
+}
+BusLogic_ExtendedMailboxRequest_T;
+
+
+/*
+ Define the Inquire Firmware Version 3rd Digit reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersion3rdDigit_T;
+
+
+/*
+ Define the Inquire Firmware Version Letter reply type.
+*/
+
+typedef unsigned char BusLogic_FirmwareVersionLetter_T;
+
+
+/*
+ Define the Inquire Board Model Number reply type.
+*/
+
+typedef unsigned char BusLogic_BoardModelNumber_T[5];
+
+
+/*
+ Define the Inquire Synchronous Period reply type. For each Target ID, a byte
+ is returned which represents the Synchronous Transfer Period in units of 10
+ nanoseconds.
+*/
+
+typedef unsigned char BusLogic_SynchronousPeriod_T[BusLogic_MaxTargetIDs];
+
+
+/*
+ Define the Inquire Extended Setup Information reply structure.
+*/
+
+typedef struct BusLogic_ExtendedSetupInformation
+{
+ unsigned char BusType; /* Byte 0 */
+ unsigned char BIOS_Address; /* Byte 1 */
+ unsigned short ScatterGatherLimit; /* Bytes 2-3 */
+ unsigned char MailboxCount; /* Byte 4 */
+ void *BaseMailboxAddress __attribute__ ((packed)); /* Bytes 5-8 */
+ struct { unsigned char :6; /* Byte 9 */
+ boolean LevelSensitiveInterrupts:1;
+ unsigned char :1; } Misc;
+ unsigned char FirmwareRevision[3]; /* Bytes 10-12 */
+ boolean HostWideSCSI:1; /* Byte 13 Bit 0 */
+ boolean HostDifferentialSCSI:1; /* Byte 13 Bit 1 */
+ unsigned char :6;
+}
+BusLogic_ExtendedSetupInformation_T;
+
+
+/*
+ Define the Enable Strict Round Robin Mode request type.
+*/
+
+#define BusLogic_AggressiveRoundRobinMode 0x00
+#define BusLogic_StrictRoundRobinMode 0x01
+
+typedef unsigned char BusLogic_RoundRobinModeRequest_T;
+
+
+/*
+ Define the Modify I/O Address request type. On PCI Host Adapters, the
+ Modify I/O Address command allows modification of the ISA compatible I/O
+ Address that the Host Adapter responds to; it does not affect the PCI
+ compliant I/O Address assigned at system initialization.
+*/
+
+#define BusLogic_ModifyIO_330 0x00
+#define BusLogic_ModifyIO_334 0x01
+#define BusLogic_ModifyIO_230 0x02
+#define BusLogic_ModifyIO_234 0x03
+#define BusLogic_ModifyIO_130 0x04
+#define BusLogic_ModifyIO_134 0x05
+#define BusLogic_ModifyIO_Disable 0x06
+#define BusLogic_ModifyIO_Disable2 0x07
+
+typedef unsigned char BusLogic_ModifyIOAddressRequest_T;
+
+
+/*
+ Define the Enable Wide Mode SCSI CCB request type. Wide Mode CCBs are
+ necessary to support more than 8 Logical Units per Target.
+*/
+
+#define BusLogic_NormalModeCCB 0x00
+#define BusLogic_WideModeCCB 0x01
+
+typedef unsigned char BusLogic_WideModeCCBRequest_T;
+
+
+/*
+ Define the Requested Reply Length type used by the Inquire Setup Information,
+ Inquire Board Model Number, Inquire Synchronous Period, and Inquire Extended
+ Setup Information commands.
+*/
+
+typedef unsigned char BusLogic_RequestedReplyLength_T;
+
+
+/*
+ Define a Lock data structure. Until a true symmetric multiprocessing kernel
+ is available, locking is implemented as saving the processor flags and
+ disabling interrupts, and unlocking restores the saved processor flags.
+*/
+
+typedef unsigned long BusLogic_Lock_T;
+
+
+/*
+ Define the Outgoing Mailbox Action Codes.
+*/
+
+typedef enum
+{
+ BusLogic_OutgoingMailboxFree = 0,
+ BusLogic_MailboxStartCommand = 1,
+ BusLogic_MailboxAbortCommand = 2
+}
+BusLogic_ActionCode_T;
+
+
+/*
+ Define the Incoming Mailbox Completion Codes.
+*/
+
+typedef enum
+{
+ BusLogic_IncomingMailboxFree = 0,
+ BusLogic_CommandCompletedWithoutError = 1,
+ BusLogic_CommandAbortedAtHostRequest = 2,
+ BusLogic_AbortedCommandNotFound = 3,
+ BusLogic_CommandCompletedWithError = 4
+}
+BusLogic_CompletionCode_T;
+
+
+/*
+ Define the Command Control Block (CCB) Opcodes.
+*/
+
+typedef enum
+{
+ BusLogic_InitiatorCCB = 0x00,
+ BusLogic_TargetCCB = 0x01,
+ BusLogic_InitiatorCCB_ScatterGather = 0x02,
+ BusLogic_InitiatorCCB_ResidualDataLength = 0x03,
+ BusLogic_InitiatorCCB_ScatterGatherResidual = 0x04,
+ BusLogic_SCSIBusDeviceReset = 0x81
+}
+BusLogic_CCB_Opcode_T;
+
+
+/*
+ Define the CCB Data Direction Codes.
+*/
+
+typedef enum
+{
+ BusLogic_UncheckedDataTransfer = 0x00,
+ BusLogic_DataInLengthChecked = 0x01,
+ BusLogic_DataOutLengthChecked = 0x02,
+ BusLogic_NoDataTransfer = 0x03
+}
+BusLogic_DataDirection_T;
+
+
+/*
+ Define the Host Adapter Status Codes.
+*/
+
+typedef enum
+{
+ BusLogic_CommandCompletedNormally = 0x00,
+ BusLogic_LinkedCommandCompleted = 0x0A,
+ BusLogic_LinkedCommandCompletedWithFlag = 0x0B,
+ BusLogic_SCSISelectionTimeout = 0x11,
+ BusLogic_DataOverUnderRun = 0x12,
+ BusLogic_UnexpectedBusFree = 0x13,
+ BusLogic_InvalidBusPhaseRequested = 0x14,
+ BusLogic_InvalidOutgoingMailboxActionCode = 0x15,
+ BusLogic_InvalidCommandOperationCode = 0x16,
+ BusLogic_LinkedCCBhasInvalidLUN = 0x17,
+ BusLogic_InvalidCommandParameter = 0x1A,
+ BusLogic_AutoRequestSenseFailed = 0x1B,
+ BusLogic_TaggedQueuingMessageRejected = 0x1C,
+ BusLogic_UnsupportedMessageReceived = 0x1D,
+ BusLogic_HostAdapterHardwareFailed = 0x20,
+ BusLogic_TargetFailedResponseToATN = 0x21,
+ BusLogic_HostAdapterAssertedRST = 0x22,
+ BusLogic_OtherDeviceAssertedRST = 0x23,
+ BusLogic_TargetDeviceReconnectedImproperly = 0x24,
+ BusLogic_HostAdapterAssertedBusDeviceReset = 0x25,
+ BusLogic_AbortQueueGenerated = 0x26,
+ BusLogic_HostAdapterSoftwareError = 0x27,
+ BusLogic_HostAdapterHardwareTimeoutError = 0x30,
+ BusLogic_SCSIParityErrorDetected = 0x34
+}
+BusLogic_HostAdapterStatus_T;
+
+
+/*
+ Define the SCSI Target Device Status Codes.
+*/
+
+typedef enum
+{
+ BusLogic_OperationGood = 0x00,
+ BusLogic_CheckCondition = 0x02,
+ BusLogic_DeviceBusy = 0x08
+}
+BusLogic_TargetDeviceStatus_T;
+
+
+/*
+ Define the Queue Tag Codes.
+*/
+
+typedef enum
+{
+ BusLogic_SimpleQueueTag = 0x00,
+ BusLogic_HeadOfQueueTag = 0x01,
+ BusLogic_OrderedQueueTag = 0x02,
+ BusLogic_ReservedQT = 0x03
+}
+BusLogic_QueueTag_T;
+
+
+/*
+ Define the SCSI Command Descriptor Block (CDB).
+*/
+
+#define BusLogic_CDB_MaxLength 12
+
+typedef unsigned char SCSI_CDB_T[BusLogic_CDB_MaxLength];
+
+
+/*
+ Define the SCSI Sense Data.
+*/
+
+#define BusLogic_SenseDataMaxLength 255
+
+typedef unsigned char SCSI_SenseData_T[BusLogic_SenseDataMaxLength];
+
+
+/*
+ Define the Scatter/Gather Segment structure required by the Host Adapter
+ Firmware Interface.
+*/
+
+typedef struct BusLogic_ScatterGatherSegment
+{
+ unsigned long SegmentByteCount;
+ void *SegmentDataPointer;
+}
+BusLogic_ScatterGatherSegment_T;
+
+
+/*
+ Define the 32 Bit Mode Command Control Block (CCB) structure. The first 40
+ bytes are defined by the Host Adapter Firmware Interface. The remaining
+ components are defined by the Linux BusLogic Driver. Wide Mode CCBs differ
+ from standard 32 Bit Mode CCBs only in having the TagEnable and QueueTag
+ fields moved from byte 17 to byte 1, and the Logical Unit field in byte 17
+ expanded to 6 bits; unfortunately, using a union of structs containing
+ enumeration type bitfields to provide both definitions leads to packing
+ problems, so the following definition is used which requires setting
+ TagEnable to Logical Unit bit 5 in Wide Mode CCBs.
+*/
+
+typedef struct BusLogic_CCB
+{
+ /*
+ BusLogic Host Adapter Firmware Portion.
+ */
+ BusLogic_CCB_Opcode_T Opcode:8; /* Byte 0 */
+ unsigned char :3; /* Byte 1 Bits 0-2 */
+ BusLogic_DataDirection_T DataDirection:2; /* Byte 1 Bits 3-4 */
+ boolean WideModeTagEnable:1; /* Byte 1 Bit 5 */
+ BusLogic_QueueTag_T WideModeQueueTag:2; /* Byte 1 Bits 6-7 */
+ unsigned char CDB_Length; /* Byte 2 */
+ unsigned char SenseDataLength; /* Byte 3 */
+ unsigned long DataLength; /* Bytes 4-7 */
+ void *DataPointer; /* Bytes 8-11 */
+ unsigned char :8; /* Byte 12 */
+ unsigned char :8; /* Byte 13 */
+ BusLogic_HostAdapterStatus_T HostAdapterStatus:8; /* Byte 14 */
+ BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8; /* Byte 15 */
+ unsigned char TargetID; /* Byte 16 */
+ unsigned char LogicalUnit:5; /* Byte 17 Bits 0-4 */
+ boolean TagEnable:1; /* Byte 17 Bit 5 */
+ BusLogic_QueueTag_T QueueTag:2; /* Byte 17 Bits 6-7 */
+ SCSI_CDB_T CDB; /* Bytes 18-29 */
+ unsigned char :8; /* Byte 30 */
+ unsigned char :8; /* Byte 31 */
+ unsigned long :32; /* Bytes 32-35 */
+ SCSI_SenseData_T *SenseDataPointer; /* Bytes 36-39 */
+ /*
+ BusLogic Linux Driver Portion.
+ */
+ struct BusLogic_HostAdapter *HostAdapter;
+ SCSI_Command_T *Command;
+ enum { BusLogic_CCB_Free = 0,
+ BusLogic_CCB_Active = 1,
+ BusLogic_CCB_Completed = 2,
+ BusLogic_CCB_Reset = 3 } Status;
+ BusLogic_CompletionCode_T MailboxCompletionCode;
+ unsigned int SerialNumber;
+ struct BusLogic_CCB *Next;
+ struct BusLogic_CCB *NextAll;
+ BusLogic_ScatterGatherSegment_T
+ ScatterGatherList[BusLogic_ScatterGatherLimit];
+}
+BusLogic_CCB_T;
+
+
+/*
+ Define the 32 Bit Mode Outgoing Mailbox structure.
+*/
+
+typedef struct BusLogic_OutgoingMailbox
+{
+ BusLogic_CCB_T *CCB;
+ unsigned long :24;
+ BusLogic_ActionCode_T ActionCode:8;
+}
+BusLogic_OutgoingMailbox_T;
+
+
+/*
+ Define the 32 Bit Mode Incoming Mailbox structure.
+*/
+
+typedef struct BusLogic_IncomingMailbox
+{
+ BusLogic_CCB_T *CCB;
+ BusLogic_HostAdapterStatus_T HostAdapterStatus:8;
+ BusLogic_TargetDeviceStatus_T TargetDeviceStatus:8;
+ unsigned char :8;
+ BusLogic_CompletionCode_T CompletionCode:8;
+}
+BusLogic_IncomingMailbox_T;
+
+
+/*
+ Define the possible Bus Types.
+*/
+
+typedef enum
+{
+ BusLogic_Unknown_Bus = 0,
+ BusLogic_ISA_Bus = 1,
+ BusLogic_MCA_Bus = 2,
+ BusLogic_EISA_Bus = 3,
+ BusLogic_VESA_Bus = 4,
+ BusLogic_PCI_Bus = 5
+}
+BusLogic_BusType_T;
+
+static char
+ *BusLogic_BusNames[] =
+ { "Unknown", "ISA", "MCA", "EISA", "VESA", "PCI" };
+
+
+/*
+ Define the Linux BusLogic Driver Command Line Entry structure.
+*/
+
+typedef struct BusLogic_CommandLineEntry
+{
+ unsigned short IO_Address;
+ unsigned short Concurrency;
+ unsigned short BusSettleTime;
+ unsigned short LocalOptions;
+ unsigned short TaggedQueuingPermitted;
+ unsigned short TaggedQueuingPermittedMask;
+ unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
+}
+BusLogic_CommandLineEntry_T;
+
+
+/*
+ Define the Linux BusLogic Driver Host Adapter structure.
+*/
+
+typedef struct BusLogic_HostAdapter
+{
+ SCSI_Host_T *SCSI_Host;
+ unsigned char HostNumber;
+ unsigned char ModelName[9];
+ unsigned char FirmwareVersion[6];
+ unsigned char BoardName[18];
+ unsigned char InterruptLabel[62];
+ unsigned short IO_Address;
+ unsigned char IRQ_Channel;
+ unsigned char DMA_Channel;
+ unsigned char SCSI_ID;
+ BusLogic_BusType_T BusType:3;
+ boolean IRQ_ChannelAcquired:1;
+ boolean DMA_ChannelAcquired:1;
+ boolean SynchronousInitiation:1;
+ boolean ParityChecking:1;
+ boolean ExtendedTranslation:1;
+ boolean LevelSensitiveInterrupts:1;
+ boolean HostWideSCSI:1;
+ boolean HostDifferentialSCSI:1;
+ boolean HostAdapterResetPending:1;
+ boolean BounceBuffersRequired:1;
+ volatile boolean HostAdapterCommandCompleted:1;
+ unsigned short HostAdapterScatterGatherLimit;
+ unsigned short DriverScatterGatherLimit;
+ unsigned short MaxTargetIDs;
+ unsigned short MaxLogicalUnits;
+ unsigned short Concurrency;
+ unsigned short BusSettleTime;
+ unsigned short LocalOptions;
+ unsigned short DisconnectPermitted;
+ unsigned short TaggedQueuingPermitted;
+ unsigned long BIOS_Address;
+ BusLogic_InstalledDevices_T InstalledDevices;
+ BusLogic_SynchronousValues_T SynchronousValues;
+ BusLogic_SynchronousPeriod_T SynchronousPeriod;
+ BusLogic_Lock_T Lock;
+ struct BusLogic_HostAdapter *Next;
+ BusLogic_CommandLineEntry_T *CommandLineEntry;
+ BusLogic_CCB_T *All_CCBs;
+ BusLogic_CCB_T *Free_CCBs;
+ unsigned char ErrorRecoveryOption[BusLogic_MaxTargetIDs];
+ unsigned char CommandSuccessfulFlag[BusLogic_MaxTargetIDs];
+ unsigned long ReadWriteOperationCount[BusLogic_MaxTargetIDs];
+ unsigned char QueuedOperationCount[BusLogic_MaxTargetIDs];
+ unsigned long LastSequencePoint[BusLogic_MaxTargetIDs];
+ BusLogic_OutgoingMailbox_T *FirstOutgoingMailbox;
+ BusLogic_OutgoingMailbox_T *LastOutgoingMailbox;
+ BusLogic_OutgoingMailbox_T *NextOutgoingMailbox;
+ BusLogic_IncomingMailbox_T *FirstIncomingMailbox;
+ BusLogic_IncomingMailbox_T *LastIncomingMailbox;
+ BusLogic_IncomingMailbox_T *NextIncomingMailbox;
+ BusLogic_OutgoingMailbox_T OutgoingMailboxes[BusLogic_MailboxCount];
+ BusLogic_IncomingMailbox_T IncomingMailboxes[BusLogic_MailboxCount];
+}
+BusLogic_HostAdapter_T;
+
+
+/*
+ Define a symbolic structure for the BIOS Disk Parameters.
+*/
+
+typedef struct BIOS_DiskParameters
+{
+ int Heads;
+ int Sectors;
+ int Cylinders;
+}
+BIOS_DiskParameters_T;
+
+
+/*
+ BusLogic_LockHostAdapter acquires exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_LockHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ save_flags(HostAdapter->Lock);
+ cli();
+}
+
+
+/*
+ BusLogic_UnlockHostAdapter releases exclusive access to Host Adapter.
+*/
+
+static inline
+void BusLogic_UnlockHostAdapter(BusLogic_HostAdapter_T *HostAdapter)
+{
+ restore_flags(HostAdapter->Lock);
+}
+
+
+/*
+ BusLogic_LockHostAdapterID acquires exclusive access to Host Adapter,
+ but is only called when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_LockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter)
+{
+}
+
+
+/*
+ BusLogic_UnlockHostAdapterID releases exclusive access to Host Adapter,
+ but is only called when interrupts are disabled.
+*/
+
+static inline
+void BusLogic_UnlockHostAdapterID(BusLogic_HostAdapter_T *HostAdapter)
+{
+}
+
+
+/*
+ Define functions to provide an abstraction for reading and writing the
+ Host Adapter I/O Registers.
+*/
+
+static inline
+void BusLogic_WriteControlRegister(BusLogic_HostAdapter_T *HostAdapter,
+ unsigned char Value)
+{
+ outb(Value, HostAdapter->IO_Address + BusLogic_ControlRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadStatusRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_StatusRegister);
+}
+
+static inline
+void BusLogic_WriteCommandParameterRegister(BusLogic_HostAdapter_T *HostAdapter,
+ unsigned char Value)
+{
+ outb(Value, HostAdapter->IO_Address + BusLogic_CommandParameterRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadDataInRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_DataInRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadInterruptRegister(BusLogic_HostAdapter_T
+ *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_InterruptRegister);
+}
+
+static inline
+unsigned char BusLogic_ReadGeometryRegister(BusLogic_HostAdapter_T *HostAdapter)
+{
+ return inb(HostAdapter->IO_Address + BusLogic_GeometryRegister);
+}
+
+
+/*
+ BusLogic_StartMailboxScan issues a Start Mailbox Scan command, which
+ notifies the Host Adapter that an entry has been made in an Outgoing
+ Mailbox.
+*/
+
+static inline
+void BusLogic_StartMailboxScan(BusLogic_HostAdapter_T *HostAdapter)
+{
+ BusLogic_WriteCommandParameterRegister(HostAdapter,
+ BusLogic_StartMailboxCommand);
+}
+
+
+/*
+ BusLogic_Delay waits for Seconds to elapse.
+*/
+
+static inline void BusLogic_Delay(int Seconds)
+{
+ unsigned long TimeoutJiffies = jiffies + Seconds * HZ;
+ unsigned long ProcessorFlags;
+ save_flags(ProcessorFlags);
+ sti();
+ while (jiffies < TimeoutJiffies) ;
+ restore_flags(ProcessorFlags);
+}
+
+
+/*
+ Define prototypes for the forward referenced BusLogic Driver
+ Internal Functions.
+*/
+
+static void BusLogic_InterruptHandler(int, Registers_T *);
+static int BusLogic_ResetHostAdapter(BusLogic_HostAdapter_T *,
+ SCSI_Command_T *);
+
+
+#endif /* BusLogic_DriverVersion */
diff --git a/i386/i386at/gpl/linux/scsi/NCR5380.h b/i386/i386at/gpl/linux/scsi/NCR5380.h
new file mode 100644
index 00000000..3a121810
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/NCR5380.h
@@ -0,0 +1,363 @@
+/*
+ * NCR 5380 defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:59 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:09 goel
+ * Linux driver merge.
+ *
+ */
+
+#ifndef NCR5380_H
+#define NCR5380_H
+
+#define NCR5380_PUBLIC_RELEASE 6
+#define NCR53C400_PUBLIC_RELEASE 2
+
+#define NDEBUG_ARBITRATION 0x1
+#define NDEBUG_AUTOSENSE 0x2
+#define NDEBUG_DMA 0x4
+#define NDEBUG_HANDSHAKE 0x8
+#define NDEBUG_INFORMATION 0x10
+#define NDEBUG_INIT 0x20
+#define NDEBUG_INTR 0x40
+#define NDEBUG_LINKED 0x80
+#define NDEBUG_MAIN 0x100
+#define NDEBUG_NO_DATAOUT 0x200
+#define NDEBUG_NO_WRITE 0x400
+#define NDEBUG_PIO 0x800
+#define NDEBUG_PSEUDO_DMA 0x1000
+#define NDEBUG_QUEUES 0x2000
+#define NDEBUG_RESELECTION 0x4000
+#define NDEBUG_SELECTION 0x8000
+#define NDEBUG_USLEEP 0x10000
+#define NDEBUG_LAST_BYTE_SENT 0x20000
+#define NDEBUG_RESTART_SELECT 0x40000
+#define NDEBUG_EXTENDED 0x80000
+#define NDEBUG_C400_PREAD 0x100000
+#define NDEBUG_C400_PWRITE 0x200000
+#define NDEBUG_LISTS 0x400000
+
+/*
+ * The contents of the OUTPUT DATA register are asserted on the bus when
+ * either arbitration is occurring or the phase-indicating signals (
+ * IO, CD, MSG) in the TARGET COMMAND register and the ASSERT DATA
+ * bit in the INITIATOR COMMAND register is set.
+ */
+
+#define OUTPUT_DATA_REG 0 /* wo DATA lines on SCSI bus */
+#define CURRENT_SCSI_DATA_REG 0 /* ro same */
+
+#define INITIATOR_COMMAND_REG 1 /* rw */
+#define ICR_ASSERT_RST 0x80 /* rw Set to assert RST */
+#define ICR_ARBITRATION_PROGRESS 0x40 /* ro Indicates arbitration complete */
+#define ICR_TRI_STATE 0x40 /* wo Set to tri-state drivers */
+#define ICR_ARBITRATION_LOST 0x20 /* ro Indicates arbitration lost */
+#define ICR_DIFF_ENABLE 0x20 /* wo Set to enable diff. drivers */
+#define ICR_ASSERT_ACK 0x10 /* rw ini Set to assert ACK */
+#define ICR_ASSERT_BSY 0x08 /* rw Set to assert BSY */
+#define ICR_ASSERT_SEL 0x04 /* rw Set to assert SEL */
+#define ICR_ASSERT_ATN 0x02 /* rw Set to assert ATN */
+#define ICR_ASSERT_DATA 0x01 /* rw SCSI_DATA_REG is asserted */
+
+#ifdef DIFFERENTIAL
+#define ICR_BASE ICR_DIFF_ENABLE
+#else
+#define ICR_BASE 0
+#endif
+
+#define MODE_REG 2
+/*
+ * Note : BLOCK_DMA code will keep DRQ asserted for the duration of the
+ * transfer, causing the chip to hog the bus. You probably don't want
+ * this.
+ */
+#define MR_BLOCK_DMA_MODE 0x80 /* rw block mode DMA */
+#define MR_TARGET 0x40 /* rw target mode */
+#define MR_ENABLE_PAR_CHECK 0x20 /* rw enable parity checking */
+#define MR_ENABLE_PAR_INTR 0x10 /* rw enable bad parity interrupt */
+#define MR_ENABLE_EOP_INTR 0x08 /* rw enable eop interrupt */
+#define MR_MONITOR_BSY 0x04 /* rw enable int on unexpected bsy fail */
+#define MR_DMA_MODE 0x02 /* rw DMA / pseudo DMA mode */
+#define MR_ARBITRATE 0x01 /* rw start arbitration */
+
+#ifdef PARITY
+#define MR_BASE MR_ENABLE_PAR_CHECK
+#else
+#define MR_BASE 0
+#endif
+
+#define TARGET_COMMAND_REG 3
+#define TCR_LAST_BYTE_SENT 0x80 /* ro DMA done */
+#define TCR_ASSERT_REQ 0x08 /* tgt rw assert REQ */
+#define TCR_ASSERT_MSG 0x04 /* tgt rw assert MSG */
+#define TCR_ASSERT_CD 0x02 /* tgt rw assert CD */
+#define TCR_ASSERT_IO 0x01 /* tgt rw assert IO */
+
+#define STATUS_REG 4 /* ro */
+/*
+ * Note : a set bit indicates an active signal, driven by us or another
+ * device.
+ */
+#define SR_RST 0x80
+#define SR_BSY 0x40
+#define SR_REQ 0x20
+#define SR_MSG 0x10
+#define SR_CD 0x08
+#define SR_IO 0x04
+#define SR_SEL 0x02
+#define SR_DBP 0x01
+
+/*
+ * Setting a bit in this register will cause an interrupt to be generated when
+ * BSY is false and SEL true and this bit is asserted on the bus.
+ */
+#define SELECT_ENABLE_REG 4 /* wo */
+
+#define BUS_AND_STATUS_REG 5 /* ro */
+#define BASR_END_DMA_TRANSFER 0x80 /* ro set on end of transfer */
+#define BASR_DRQ 0x40 /* ro mirror of DRQ pin */
+#define BASR_PARITY_ERROR 0x20 /* ro parity error detected */
+#define BASR_IRQ 0x10 /* ro mirror of IRQ pin */
+#define BASR_PHASE_MATCH 0x08 /* ro Set when MSG CD IO match TCR */
+#define BASR_BUSY_ERROR 0x04 /* ro Unexpected change to inactive state */
+#define BASR_ATN 0x02 /* ro BUS status */
+#define BASR_ACK 0x01 /* ro BUS status */
+
+/* Write any value to this register to start a DMA send */
+#define START_DMA_SEND_REG 5 /* wo */
+
+/*
+ * Used in DMA transfer mode, data is latched from the SCSI bus on
+ * the falling edge of REQ (ini) or ACK (tgt)
+ */
+#define INPUT_DATA_REG 6 /* ro */
+
+/* Write any value to this register to start a DMA receive */
+#define START_DMA_TARGET_RECEIVE_REG 6 /* wo */
+
+/* Read this register to clear interrupt conditions */
+#define RESET_PARITY_INTERRUPT_REG 7 /* ro */
+
+/* Write any value to this register to start an ini mode DMA receive */
+#define START_DMA_INITIATOR_RECEIVE_REG 7 /* wo */
+
+#define C400_CONTROL_STATUS_REG NCR53C400_register_offset-8 /* rw */
+
+#define CSR_RESET 0x80 /* wo Resets 53c400 */
+#define CSR_53C80_REG 0x80 /* ro 5380 registers busy */
+#define CSR_TRANS_DIR 0x40 /* rw Data transfer direction */
+#define CSR_SCSI_BUFF_INTR 0x20 /* rw Enable int on transfer ready */
+#define CSR_53C80_INTR 0x10 /* rw Enable 53c80 interrupts */
+#define CSR_SHARED_INTR 0x08 /* rw Interrupt sharing */
+#define CSR_HOST_BUF_NOT_RDY 0x04 /* ro Is Host buffer ready */
+#define CSR_SCSI_BUF_RDY 0x02 /* ro SCSI buffer read */
+#define CSR_GATED_53C80_IRQ 0x01 /* ro Last block xferred */
+
+#if 0
+#define CSR_BASE CSR_SCSI_BUFF_INTR | CSR_53C80_INTR
+#else
+#define CSR_BASE CSR_53C80_INTR
+#endif
+
+/* Number of 128-byte blocks to be transferred */
+#define C400_BLOCK_COUNTER_REG NCR53C400_register_offset-7 /* rw */
+
+/* Resume transfer after disconnect */
+#define C400_RESUME_TRANSFER_REG NCR53C400_register_offset-6 /* wo */
+
+/* Access to host buffer stack */
+#define C400_HOST_BUFFER NCR53C400_register_offset-4 /* rw */
+
+
+/* Note : PHASE_* macros are based on the values of the STATUS register */
+#define PHASE_MASK (SR_MSG | SR_CD | SR_IO)
+
+#define PHASE_DATAOUT 0
+#define PHASE_DATAIN SR_IO
+#define PHASE_CMDOUT SR_CD
+#define PHASE_STATIN (SR_CD | SR_IO)
+#define PHASE_MSGOUT (SR_MSG | SR_CD)
+#define PHASE_MSGIN (SR_MSG | SR_CD | SR_IO)
+#define PHASE_UNKNOWN 0xff
+
+/*
+ * Convert status register phase to something we can use to set phase in
+ * the target register so we can get phase mismatch interrupts on DMA
+ * transfers.
+ */
+
+#define PHASE_SR_TO_TCR(phase) ((phase) >> 2)
+
+/*
+ * The internal should_disconnect() function returns these based on the
+ * expected length of a disconnect if a device supports disconnect/
+ * reconnect.
+ */
+
+#define DISCONNECT_NONE 0
+#define DISCONNECT_TIME_TO_DATA 1
+#define DISCONNECT_LONG 2
+
+/*
+ * These are "special" values for the tag parameter passed to NCR5380_select.
+ */
+
+#define TAG_NEXT -1 /* Use next free tag */
+#define TAG_NONE -2 /*
+ * Establish I_T_L nexus instead of I_T_L_Q
+ * even on SCSI-II devices.
+ */
+
+/*
+ * These are "special" values for the irq and dma_channel fields of the
+ * Scsi_Host structure
+ */
+
+#define IRQ_NONE 255
+#define DMA_NONE 255
+#define IRQ_AUTO 254
+#define DMA_AUTO 254
+
+#define FLAG_HAS_LAST_BYTE_SENT 1 /* NCR53c81 or better */
+#define FLAG_CHECK_LAST_BYTE_SENT 2 /* Only test once */
+#define FLAG_NCR53C400 4 /* NCR53c400 */
+#define FLAG_NO_PSEUDO_DMA 8 /* Inhibit DMA */
+
+#ifndef ASM
+struct NCR5380_hostdata {
+ NCR5380_implementation_fields; /* implementation specific */
+ unsigned char id_mask, id_higher_mask; /* 1 << id, all bits greater */
+ unsigned char targets_present; /* targets we have connected
+ to, so we can call a select
+ failure a retryable condition */
+ volatile unsigned char busy[8]; /* index = target, bit = lun */
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+ volatile int dma_len; /* requested length of DMA */
+#endif
+ volatile unsigned char last_message; /* last message OUT */
+ volatile Scsi_Cmnd *connected; /* currently connected command */
+ volatile Scsi_Cmnd *issue_queue; /* waiting to be issued */
+ volatile Scsi_Cmnd *disconnected_queue; /* waiting for reconnect */
+ volatile int restart_select; /* we have disconnected,
+ used to restart
+ NCR5380_select() */
+ volatile unsigned aborted:1; /* flag, says aborted */
+ int flags;
+#ifdef USLEEP
+ unsigned long time_expires; /* in jiffies, set prior to sleeping */
+ struct Scsi_Host *next_timer;
+#endif
+};
+
+#ifdef __KERNEL__
+static struct Scsi_Host *first_instance; /* linked list of 5380's */
+
+#if defined(AUTOPROBE_IRQ)
+static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible);
+#endif
+static void NCR5380_init (struct Scsi_Host *instance, int flags);
+static void NCR5380_information_transfer (struct Scsi_Host *instance);
+static void NCR5380_intr (int irq, struct pt_regs * regs);
+static void NCR5380_main (void);
+static void NCR5380_print_options (struct Scsi_Host *instance);
+static void NCR5380_print_phase (struct Scsi_Host *instance);
+static void NCR5380_print (struct Scsi_Host *instance);
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd);
+#ifndef NCR5380_reset
+static
+#endif
+int NCR5380_reset (Scsi_Cmnd *cmd);
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *));
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance);
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd, int tag);
+#if defined(PSEUDO_DMA) || defined(REAL_DMA) || defined(REAL_DMA_POLL)
+static int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data);
+#endif
+static int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data);
+
+#if (defined(REAL_DMA) || defined(REAL_DMA_POLL)) && defined(i386)
+static __inline__ int NCR5380_i386_dma_setup (struct Scsi_Host *instance,
+ unsigned char *ptr, unsigned int count, unsigned char mode) {
+ unsigned limit;
+
+ if (instance->dma_channel <=3) {
+ if (count > 65536)
+ count = 65536;
+ limit = 65536 - (((unsigned) ptr) & 0xFFFF);
+ } else {
+ if (count > 65536 * 2)
+ count = 65536 * 2;
+ limit = 65536* 2 - (((unsigned) ptr) & 0x1FFFF);
+ }
+
+ if (count > limit) count = limit;
+
+ if ((count & 1) || (((unsigned) ptr) & 1))
+ panic ("scsi%d : attempted unaligned DMA transfer\n", instance->host_no);
+ cli();
+ disable_dma(instance->dma_channel);
+ clear_dma_ff(instance->dma_channel);
+ set_dma_addr(instance->dma_channel, (unsigned int) ptr);
+ set_dma_count(instance->dma_channel, count);
+ set_dma_mode(instance->dma_channel, mode);
+ enable_dma(instance->dma_channel);
+ sti();
+ return count;
+}
+
+static __inline__ int NCR5380_i386_dma_write_setup (struct Scsi_Host *instance,
+ unsigned char *src, unsigned int count) {
+ return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_WRITE);
+}
+
+static __inline__ int NCR5380_i386_dma_read_setup (struct Scsi_Host *instance,
+ unsigned char *src, unsigned int count) {
+ return NCR5380_i386_dma_setup (instance, src, count, DMA_MODE_READ);
+}
+
+static __inline__ int NCR5380_i386_dma_residual (struct Scsi_Host *instance) {
+ register int tmp;
+ cli();
+ clear_dma_ff(instance->dma_channel);
+ tmp = get_dma_residue(instance->dma_channel);
+ sti();
+ return tmp;
+}
+#endif /* defined(REAL_DMA) && defined(i386) */
+#endif __KERNEL_
+#endif /* ndef ASM */
+#endif /* NCR5380_H */
diff --git a/i386/i386at/gpl/linux/scsi/NCR5380.src b/i386/i386at/gpl/linux/scsi/NCR5380.src
new file mode 100644
index 00000000..64beb813
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/NCR5380.src
@@ -0,0 +1,3035 @@
+#ifndef NDEBUG
+#define NDEBUG (NDEBUG_RESTART_SELECT | NDEBUG_ABORT)
+#endif
+/*
+ * NCR 5380 generic driver routines. These should make it *trivial*
+ * to implement 5380 SCSI drivers under Linux with a non-trantor
+ * architecture.
+ *
+ * Note that these routines also work with NR53c400 family chips.
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * DISTRIBUTION RELEASE 6.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: NCR5380.src,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:59 thomas
+ * Imported from UK22
+ *
+# Revision 1.1 1996/03/25 20:25:10 goel
+# Linux driver merge.
+#
+ * Revision 1.5 1994/01/19 09:14:57 drew
+ * Fixed udelay() hack that was being used on DATAOUT phases
+ * instead of a proper wait for the final handshake.
+ *
+ * Revision 1.4 1994/01/19 06:44:25 drew
+ * *** empty log message ***
+ *
+ * Revision 1.3 1994/01/19 05:24:40 drew
+ * Added support for TCR LAST_BYTE_SENT bit.
+ *
+ * Revision 1.2 1994/01/15 06:14:11 drew
+ * REAL DMA support, bug fixes.
+ *
+ * Revision 1.1 1994/01/15 06:00:54 drew
+ * Initial revision
+ *
+ */
+
+/*
+ * Further development / testing that should be done :
+ * 1. Cleanup the NCR5380_transfer_dma function and DMA operation complete
+ * code so that everything does the same thing that's done at the
+ * end of a pseudo-DMA read operation.
+ *
+ * 2. Fix REAL_DMA (interrupt driven, polled works fine) -
+ * basically, transfer size needs to be reduced by one
+ * and the last byte read as is done with PSEUDO_DMA.
+ *
+ * 3. Test USLEEP code
+ *
+ * 4. Test SCSI-II tagged queueing (I have no devices which support
+ * tagged queueing)
+ *
+ * 5. Test linked command handling code after Eric is ready with
+ * the high level code.
+ */
+
+#if (NDEBUG & NDEBUG_LISTS)
+#define LIST(x,y) {printk("LINE:%d Adding %p to %p\n", __LINE__, (void*)(x), (void*)(y)); if ((x)==(y)) udelay(5); }
+#define REMOVE(w,x,y,z) {printk("LINE:%d Removing: %p->%p %p->%p \n", __LINE__, (void*)(w), (void*)(x), (void*)(y), (void*)(z)); if ((x)==(y)) udelay(5); }
+#else
+#define LIST(x,y)
+#define REMOVE(w,x,y,z)
+#endif
+
+#ifndef notyet
+#undef LINKED
+#undef USLEEP
+#undef REAL_DMA
+#endif
+
+#ifdef REAL_DMA_POLL
+#undef READ_OVERRUNS
+#define READ_OVERRUNS
+#endif
+
+/*
+ * Design
+ * Issues :
+ *
+ * The other Linux SCSI drivers were written when Linux was Intel PC-only,
+ * and specifically for each board rather than each chip. This makes their
+ * adaptation to platforms like the Mac (Some of which use NCR5380's)
+ * more difficult than it has to be.
+ *
+ * Also, many of the SCSI drivers were written before the command queuing
+ * routines were implemented, meaning their implementations of queued
+ * commands were hacked on rather than designed in from the start.
+ *
+ * When I designed the Linux SCSI drivers I figured that
+ * while having two different SCSI boards in a system might be useful
+ * for debugging things, two of the same type wouldn't be used.
+ * Well, I was wrong and a number of users have mailed me about running
+ * multiple high-performance SCSI boards in a server.
+ *
+ * Finally, when I get questions from users, I have no idea what
+ * revision of my driver they are running.
+ *
+ * This driver attempts to address these problems :
+ * This is a generic 5380 driver. To use it on a different platform,
+ * one simply writes appropriate system specific macros (ie, data
+ * transfer - some PC's will use the I/O bus, 68K's must use
+ * memory mapped) and drops this file in their 'C' wrapper.
+ *
+ * As far as command queueing, two queues are maintained for
+ * each 5380 in the system - commands that haven't been issued yet,
+ * and commands that are currently executing. This means that an
+ * unlimited number of commands may be queued, letting
+ * more commands propagate from the higher driver levels giving higher
+ * throughput. Note that both I_T_L and I_T_L_Q nexuses are supported,
+ * allowing multiple commands to propagate all the way to a SCSI-II device
+ * while a command is already executing.
+ *
+ * To solve the multiple-boards-in-the-same-system problem,
+ * there is a separate instance structure for each instance
+ * of a 5380 in the system. So, multiple NCR5380 drivers will
+ * be able to coexist with appropriate changes to the high level
+ * SCSI code.
+ *
+ * A NCR5380_PUBLIC_REVISION macro is provided, with the release
+ * number (updated for each public release) printed by the
+ * NCR5380_print_options command, which should be called from the
+ * wrapper detect function, so that I know what release of the driver
+ * users are using.
+ *
+ * Issues specific to the NCR5380 :
+ *
+ * When used in a PIO or pseudo-dma mode, the NCR5380 is a braindead
+ * piece of hardware that requires you to sit in a loop polling for
+ * the REQ signal as long as you are connected. Some devices are
+ * brain dead (ie, many TEXEL CD ROM drives) and won't disconnect
+ * while doing long seek operations.
+ *
+ * The workaround for this is to keep track of devices that have
+ * disconnected. If the device hasn't disconnected, for commands that
+ * should disconnect, we do something like
+ *
+ * while (!REQ is asserted) { sleep for N usecs; poll for M usecs }
+ *
+ * Some tweaking of N and M needs to be done. An algorithm based
+ * on "time to data" would give the best results as long as short time
+ * to datas (ie, on the same track) were considered, however these
+ * broken devices are the exception rather than the rule and I'd rather
+ * spend my time optimizing for the normal case.
+ *
+ * Architecture :
+ *
+ * At the heart of the design is a coroutine, NCR5380_main,
+ * which is started when not running by the interrupt handler,
+ * timer, and queue command function. It attempts to establish
+ * I_T_L or I_T_L_Q nexuses by removing the commands from the
+ * issue queue and calling NCR5380_select() if a nexus
+ * is not established.
+ *
+ * Once a nexus is established, the NCR5380_information_transfer()
+ * phase goes through the various phases as instructed by the target.
+ * if the target goes into MSG IN and sends a DISCONNECT message,
+ * the command structure is placed into the per instance disconnected
+ * queue, and NCR5380_main tries to find more work. If USLEEP
+ * was defined, and the target is idle for too long, the system
+ * will try to sleep.
+ *
+ * If a command has disconnected, eventually an interrupt will trigger,
+ * calling NCR5380_intr() which will in turn call NCR5380_reselect
+ * to reestablish a nexus. This will run main if necessary.
+ *
+ * On command termination, the done function will be called as
+ * appropriate.
+ *
+ * SCSI pointers are maintained in the SCp field of SCSI command
+ * structures, being initialized after the command is connected
+ * in NCR5380_select, and set as appropriate in NCR5380_information_transfer.
+ * Note that in violation of the standard, an implicit SAVE POINTERS operation
+ * is done, since some BROKEN disks fail to issue an explicit SAVE POINTERS.
+ */
+
+/*
+ * Using this file :
+ * This file a skeleton Linux SCSI driver for the NCR 5380 series
+ * of chips. To use it, you write a architecture specific functions
+ * and macros and include this file in your driver.
+ *
+ * These macros control options :
+ * AUTOPROBE_IRQ - if defined, the NCR5380_probe_irq() function will be
+ * defined.
+ *
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * DIFFERENTIAL - if defined, NCR53c81 chips will use external differential
+ * transceivers.
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ * bytes at a time. Since interrupts are disabled by default during
+ * these transfers, we might need this to give reasonable interrupt
+ * service time if the transfer size gets too large.
+ *
+ * LINKED - if defined, linked commands are supported.
+ *
+ * PSEUDO_DMA - if defined, PSEUDO DMA is used during the data transfer phases.
+ *
+ * REAL_DMA - if defined, REAL DMA is used during the data transfer phases.
+ *
+ * REAL_DMA_POLL - if defined, REAL DMA is used but the driver doesn't
+ * rely on phase mismatch and EOP interrupts to determine end
+ * of phase.
+ *
+ * SCSI2 - if defined, SCSI-2 tagged queuing is used where possible
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
+ * only really want to use this if you're having a problem with
+ * dropped characters during high speed communications, and even
+ * then, you're going to be better off twiddling with transfersize
+ * in the high level code.
+ *
+ * USLEEP - if defined, on devices that aren't disconnecting from the
+ * bus, we will go to sleep so that the CPU can get real work done
+ * when we run a command that won't complete immediately.
+ *
+ * Note that if USLEEP is defined, NCR5380_TIMER *must* also be
+ * defined.
+ *
+ * Defaults for these will be provided if USLEEP is defined, although
+ * the user may want to adjust these to allocate CPU resources to
+ * the SCSI driver or "real" code.
+ *
+ * USLEEP_SLEEP - amount of time, in jiffies, to sleep
+ *
+ * USLEEP_POLL - amount of time, in jiffies, to poll
+ *
+ * These macros MUST be defined :
+ * NCR5380_local_declare() - declare any local variables needed for your transfer
+ * routines.
+ *
+ * NCR5380_setup(instance) - initialize any local variables needed from a given
+ * instance of the host adapter for NCR5380_{read,write,pread,pwrite}
+ *
+ * NCR5380_read(register) - read from the specified register
+ *
+ * NCR5380_write(register, value) - write to the specific register
+ *
+ * NCR5380_implementation_fields - additional fields needed for this
+ * specific implementation of the NCR5380
+ *
+ * Either real DMA *or* pseudo DMA may be implemented
+ * REAL functions :
+ * NCR5380_REAL_DMA should be defined if real DMA is to be used.
+ * Note that the DMA setup functions should return the number of bytes
+ * that they were able to program the controller for.
+ *
+ * Also note that generic i386/PC versions of these macros are
+ * available as NCR5380_i386_dma_write_setup,
+ * NCR5380_i386_dma_read_setup, and NCR5380_i386_dma_residual.
+ *
+ * NCR5380_dma_write_setup(instance, src, count) - initialize
+ * NCR5380_dma_read_setup(instance, dst, count) - initialize
+ * NCR5380_dma_residual(instance); - residual count
+ *
+ * PSEUDO functions :
+ * NCR5380_pwrite(instance, src, count)
+ * NCR5380_pread(instance, dst, count);
+ *
+ * If nothing specific to this implementation needs doing (ie, with external
+ * hardware), you must also define
+ *
+ * NCR5380_queue_command
+ * NCR5380_reset
+ * NCR5380_abort
+ *
+ * to be the global entry points into the specific driver, ie
+ * #define NCR5380_queue_command t128_queue_command.
+ *
+ * If this is not done, the routines will be defined as static functions
+ * with the NCR5380* names and the user must provide a globally
+ * accessible wrapper function.
+ *
+ * The generic driver is initialized by calling NCR5380_init(instance),
+ * after setting the appropriate host specific fields and ID. If the
+ * driver wishes to autoprobe for an IRQ line, the NCR5380_probe_irq(instance,
+ * possible) function may be used. Before the specific driver initialization
+ * code finishes, NCR5380_print_options should be called.
+ */
+
+static int do_abort (struct Scsi_Host *host);
+static void do_reset (struct Scsi_Host *host);
+static struct Scsi_Host *first_instance = NULL;
+static Scsi_Host_Template *the_template = NULL;
+
+/*
+ * Function : void initialize_SCp(Scsi_Cmnd *cmd)
+ *
+ * Purpose : initialize the saved data pointers for cmd to point to the
+ * start of the buffer.
+ *
+ * Inputs : cmd - Scsi_Cmnd structure to have pointers reset.
+ */
+
+static __inline__ void initialize_SCp(Scsi_Cmnd *cmd) {
+ /*
+ * Initialize the Scsi Pointer field so that all of the commands in the
+ * various queues are valid.
+ */
+
+ if (cmd->use_sg) {
+ cmd->SCp.buffer = (struct scatterlist *) cmd->buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg - 1;
+ cmd->SCp.ptr = (char *) cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ } else {
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ }
+}
+
+#include <linux/delay.h>
+
+#ifdef NDEBUG
+static struct {
+ unsigned char mask;
+ const char * name;}
+signals[] = {{ SR_DBP, "PARITY"}, { SR_RST, "RST" }, { SR_BSY, "BSY" },
+ { SR_REQ, "REQ" }, { SR_MSG, "MSG" }, { SR_CD, "CD" }, { SR_IO, "IO" },
+ { SR_SEL, "SEL" }, {0, NULL}},
+basrs[] = {{BASR_ATN, "ATN"}, {BASR_ACK, "ACK"}, {0, NULL}},
+icrs[] = {{ICR_ASSERT_RST, "ASSERT RST"},{ICR_ASSERT_ACK, "ASSERT ACK"},
+ {ICR_ASSERT_BSY, "ASSERT BSY"}, {ICR_ASSERT_SEL, "ASSERT SEL"},
+ {ICR_ASSERT_ATN, "ASSERT ATN"}, {ICR_ASSERT_DATA, "ASSERT DATA"},
+ {0, NULL}},
+mrs[] = {{MR_BLOCK_DMA_MODE, "MODE BLOCK DMA"}, {MR_TARGET, "MODE TARGET"},
+ {MR_ENABLE_PAR_CHECK, "MODE PARITY CHECK"}, {MR_ENABLE_PAR_INTR,
+ "MODE PARITY INTR"}, {MR_MONITOR_BSY, "MODE MONITOR BSY"},
+ {MR_DMA_MODE, "MODE DMA"}, {MR_ARBITRATE, "MODE ARBITRATION"},
+ {0, NULL}};
+
+/*
+ * Function : void NCR5380_print(struct Scsi_Host *instance)
+ *
+ * Purpose : print the SCSI bus signals for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print(struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ unsigned char status, data, basr, mr, icr, i;
+ NCR5380_setup(instance);
+ cli();
+ data = NCR5380_read(CURRENT_SCSI_DATA_REG);
+ status = NCR5380_read(STATUS_REG);
+ mr = NCR5380_read(MODE_REG);
+ icr = NCR5380_read(INITIATOR_COMMAND_REG);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ sti();
+ printk("STATUS_REG: %02x ", status);
+ for (i = 0; signals[i].mask ; ++i)
+ if (status & signals[i].mask)
+ printk(",%s", signals[i].name);
+ printk("\nBASR: %02x ", basr);
+ for (i = 0; basrs[i].mask ; ++i)
+ if (basr & basrs[i].mask)
+ printk(",%s", basrs[i].name);
+ printk("\nICR: %02x ", icr);
+ for (i = 0; icrs[i].mask; ++i)
+ if (icr & icrs[i].mask)
+ printk(",%s", icrs[i].name);
+ printk("\nMODE: %02x ", mr);
+ for (i = 0; mrs[i].mask; ++i)
+ if (mr & mrs[i].mask)
+ printk(",%s", mrs[i].name);
+ printk("\n");
+}
+
+static struct {
+ unsigned char value;
+ const char *name;
+} phases[] = {
+{PHASE_DATAOUT, "DATAOUT"}, {PHASE_DATAIN, "DATAIN"}, {PHASE_CMDOUT, "CMDOUT"},
+{PHASE_STATIN, "STATIN"}, {PHASE_MSGOUT, "MSGOUT"}, {PHASE_MSGIN, "MSGIN"},
+{PHASE_UNKNOWN, "UNKNOWN"}};
+
+/*
+ * Function : void NCR5380_print_phase(struct Scsi_Host *instance)
+ *
+ * Purpose : print the current SCSI phase for debugging purposes
+ *
+ * Input : instance - which NCR5380
+ */
+
+static void NCR5380_print_phase(struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ unsigned char status;
+ int i;
+ NCR5380_setup(instance);
+
+ status = NCR5380_read(STATUS_REG);
+ if (!(status & SR_REQ))
+ printk("scsi%d : REQ not asserted, phase unknown.\n",
+ instance->host_no);
+ else {
+ for (i = 0; (phases[i].value != PHASE_UNKNOWN) &&
+ (phases[i].value != (status & PHASE_MASK)); ++i);
+ printk("scsi%d : phase %s\n", instance->host_no, phases[i].name);
+ }
+}
+#endif
+
+/*
+ * We need to have our coroutine active given these constraints :
+ * 1. The mutex flag, main_running, can only be set when the main
+ * routine can actually process data, otherwise SCSI commands
+ * will never get issued.
+ *
+ * 2. NCR5380_main() shouldn't be called before it has exited, because
+ * other drivers have had kernel stack overflows in similar
+ * situations.
+ *
+ * 3. We don't want to inline NCR5380_main() because of space concerns,
+ * even though it is only called in two places.
+ *
+ * So, the solution is to set the mutex in an inline wrapper for the
+ * main coroutine, and have the main coroutine exit with interrupts
+ * disabled after the final search through the queues so that no race
+ * conditions are possible.
+ */
+
+static volatile int main_running = 0;
+
+/*
+ * Function : run_main(void)
+ *
+ * Purpose : insure that the coroutine is running and will process our
+ * request. main_running is checked/set here (in an inline function)
+ * rather than in NCR5380_main itself to reduce the chances of stack
+ * overflow.
+ *
+ */
+
+static __inline__ void run_main(void) {
+ cli();
+ if (!main_running) {
+ main_running = 1;
+ NCR5380_main();
+ /*
+ * main_running is cleared in NCR5380_main once it can't do
+ * more work, and NCR5380_main exits with interrupts disabled.
+ */
+ sti();
+ } else
+ sti();
+}
+
+#ifdef USLEEP
+#ifndef NCR5380_TIMER
+#error "NCR5380_TIMER must be defined so that this type of NCR5380 driver gets a unique timer."
+#endif
+
+/*
+ * These need tweaking, and would probably work best as per-device
+ * flags initialized differently for disk, tape, cd, etc devices.
+ * People with broken devices are free to experiment as to what gives
+ * the best results for them.
+ *
+ * USLEEP_SLEEP should be a minimum seek time.
+ *
+ * USLEEP_POLL should be a maximum rotational latency.
+ */
+#ifndef USLEEP_SLEEP
+/* 20 ms (reasonable hard disk speed) */
+#define USLEEP_SLEEP 2
+#endif
+/* 300 RPM (floppy speed) */
+#ifndef USLEEP_POLL
+#define USLEEP_POLL 20
+#endif
+
+static struct Scsi_Host * expires_first = NULL;
+
+/*
+ * Function : int should_disconnect (unsigned char cmd)
+ *
+ * Purpose : decide weather a command would normally disconnect or
+ * not, since if it won't disconnect we should go to sleep.
+ *
+ * Input : cmd - opcode of SCSI command
+ *
+ * Returns : DISCONNECT_LONG if we should disconnect for a really long
+ * time (ie always, sleep, look for REQ active, sleep),
+ * DISCONNECT_TIME_TO_DATA if we would only disconnect for a normal
+ * time-to-data delay, DISCONNECT_NONE if this command would return
+ * immediately.
+ *
+ * Future sleep algorithms based on time to data can exploit
+ * something like this so they can differentiate between "normal"
+ * (ie, read, write, seek) and unusual commands (ie, * format).
+ *
+ * Note : We don't deal with commands that handle an immediate disconnect,
+ *
+ */
+
+static int should_disconnect (unsigned char cmd) {
+ switch (cmd) {
+ case READ_6:
+ case WRITE_6:
+ case SEEK_6:
+ case READ_10:
+ case WRITE_10:
+ case SEEK_10:
+ return DISCONNECT_TIME_TO_DATA;
+ case FORMAT_UNIT:
+ case SEARCH_HIGH:
+ case SEARCH_LOW:
+ case SEARCH_EQUAL:
+ return DISCONNECT_LONG;
+ default:
+ return DISCONNECT_NONE;
+ }
+}
+
+/*
+ * Assumes instance->time_expires has been set in higher level code.
+ */
+
+static int NCR5380_set_timer (struct Scsi_Host *instance) {
+ struct Scsi_Host *tmp, **prev;
+
+ cli();
+ if (((struct NCR5380_hostdata *) (instance->host_data))->next_timer) {
+ sti();
+ return -1;
+ }
+
+ for (prev = &expires_first, tmp = expires_first; tmp;
+ prev = &(((struct NCR5380_hostdata *) tmp->host_data)->next_timer),
+ tmp = ((struct NCR5380_hostdata *) tmp->host_data)->next_timer)
+ if (instance->time_expires < tmp->time_expires)
+ break;
+
+ instance->next_timer = tmp;
+ *prev = instance;
+ timer_table[NCR5380_TIMER].expires = expires_first->time_expires;
+ timer_active |= 1 << NCR5380_TIMER;
+ sti();
+ return 0;
+}
+
+/* Doing something about unwanted reentrancy here might be useful */
+void NCR5380_timer_fn(void) {
+ struct Scsi_Host *instance;
+ cli();
+ for (; expires_first && expires_first->time_expires >= jiffies; ) {
+ instance = ((NCR5380_hostdata *) expires_first->host_data)->
+ expires_next;
+ ((NCR5380_hostdata *) expires_first->host_data)->expires_next =
+ NULL;
+ ((NCR5380_hostdata *) expires_first->host_data)->time_expires =
+ 0;
+ expires_first = instance;
+ }
+
+ if (expires_first) {
+ timer_table[NCR5380_TIMER].expires = ((NCR5380_hostdata *)
+ expires_first->host_data)->time_expires;
+ timer_active |= (1 << NCR5380_TIMER);
+ } else {
+ timer_table[NCR5380_TIMER].expires = 0;
+ timer_active &= ~(1 << MCR5380_TIMER);
+ }
+ sti();
+
+ run_main();
+}
+#endif /* def USLEEP */
+
+static void NCR5380_all_init (void) {
+ static int done = 0;
+ if (!done) {
+#if (NDEBUG & NDEBUG_INIT)
+ printk("scsi : NCR5380_all_init()\n");
+#endif
+ done = 1;
+#ifdef USLEEP
+ timer_table[NCR5380_TIMER].expires = 0;
+ timer_table[NCR5380_TIMER].fn = NCR5380_timer_fn;
+#endif
+ }
+}
+
+#ifdef AUTOPROBE_IRQ
+/*
+ * Function : int NCR5380_probe_irq (struct Scsi_Host *instance, int possible)
+ *
+ * Purpose : autoprobe for the IRQ line used by the NCR5380.
+ *
+ * Inputs : instance - pointer to this instance of the NCR5380 driver,
+ * possible - bitmask of permissible interrupts.
+ *
+ * Returns : number of the IRQ selected, IRQ_NONE if no interrupt fired.
+ *
+ * XXX no effort is made to deal with spurious interrupts.
+ */
+
+
+static int probe_irq;
+static void probe_intr (int irq, struct pt_regs * regs) {
+ probe_irq = irq;
+};
+
+static int NCR5380_probe_irq (struct Scsi_Host *instance, int possible) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned long timeout;
+ int trying_irqs, i, mask;
+ NCR5380_setup(instance);
+
+ for (trying_irqs = i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ if ((mask & possible) && (request_irq(i, &probe_intr, SA_INTERRUPT, "NCR-probe")
+ == 0))
+ trying_irqs |= mask;
+
+ timeout = jiffies + 25;
+ probe_irq = IRQ_NONE;
+
+/*
+ * A interrupt is triggered whenever BSY = false, SEL = true
+ * and a bit set in the SELECT_ENABLE_REG is asserted on the
+ * SCSI bus.
+ *
+ * Note that the bus is only driven when the phase control signals
+ * (I/O, C/D, and MSG) match those in the TCR, so we must reset that
+ * to zero.
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_SEL);
+
+ while (probe_irq == IRQ_NONE && jiffies < timeout)
+ barrier();
+
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ for (i = 0, mask = 1; i < 16; ++i, mask <<= 1)
+ if (trying_irqs & mask)
+ free_irq(i);
+
+ return probe_irq;
+}
+#endif /* AUTOPROBE_IRQ */
+
+/*
+ * Function : void NCR58380_print_options (struct Scsi_Host *instance)
+ *
+ * Purpose : called by probe code indicating the NCR5380 driver
+ * options that were selected.
+ *
+ * Inputs : instance, pointer to this instance. Unused.
+ */
+
+static void NCR5380_print_options (struct Scsi_Host *instance) {
+ printk(" generic options"
+#ifdef AUTOPROBE_IRQ
+ " AUTOPROBE_IRQ"
+#endif
+#ifdef AUTOSENSE
+ " AUTOSENSE"
+#endif
+#ifdef DIFFERENTIAL
+ " DIFFERENTIAL"
+#endif
+#ifdef REAL_DMA
+ " REAL DMA"
+#endif
+#ifdef REAL_DMA_POLL
+ " REAL DMA POLL"
+#endif
+#ifdef PARITY
+ " PARITY"
+#endif
+#ifdef PSEUDO_DMA
+ " PSEUDO DMA"
+#endif
+#ifdef SCSI2
+ " SCSI-2"
+#endif
+#ifdef UNSAFE
+ " UNSAFE "
+#endif
+ );
+#ifdef USLEEP
+ printk(" USLEEP, USLEEP_POLL=%d USLEEP_SLEEP=%d", USLEEP_POLL, USLEEP_SLEEP);
+#endif
+ printk(" generic release=%d", NCR5380_PUBLIC_RELEASE);
+ if (((struct NCR5380_hostdata *)instance->hostdata)->flags & FLAG_NCR53C400) {
+ printk(" ncr53c400 release=%d", NCR53C400_PUBLIC_RELEASE);
+ }
+}
+
+/*
+ * Function : void NCR5380_print_status (struct Scsi_Host *instance)
+ *
+ * Purpose : print commands in the various queues, called from
+ * NCR5380_abort and NCR5380_debug to aid debugging.
+ *
+ * Inputs : instance, pointer to this instance.
+ */
+
+static void NCR5380_print_status (struct Scsi_Host *instance) {
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ Scsi_Cmnd *ptr;
+
+
+ printk("NCR5380 : coroutine is%s running.\n",
+ main_running ? "" : "n't");
+
+#ifdef NDEBUG
+ NCR5380_print (instance);
+ NCR5380_print_phase (instance);
+#endif
+
+ cli();
+ if (!hostdata->connected) {
+ printk ("scsi%d: no currently connected command\n",
+ instance->host_no);
+ } else {
+ print_Scsi_Cmnd ((Scsi_Cmnd *) hostdata->connected);
+ }
+
+ printk ("scsi%d: issue_queue\n", instance->host_no);
+
+ for (ptr = (Scsi_Cmnd *) hostdata->issue_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ print_Scsi_Cmnd (ptr);
+
+ printk ("scsi%d: disconnected_queue\n", instance->host_no);
+
+ for (ptr = (Scsi_Cmnd *) hostdata->disconnected_queue; ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ print_Scsi_Cmnd (ptr);
+
+ sti();
+}
+
+
+/*
+ * Function : void NCR5380_init (struct Scsi_Host *instance, flags)
+ *
+ * Purpose : initializes *instance and corresponding 5380 chip,
+ * with flags OR'd into the initial flags value.
+ *
+ * Inputs : instance - instantiation of the 5380 driver.
+ *
+ * Notes : I assume that the host, hostno, and id bits have been
+ * set correctly. I don't care about the irq and other fields.
+ *
+ */
+
+static void NCR5380_init (struct Scsi_Host *instance, int flags) {
+ NCR5380_local_declare();
+ int i, pass;
+ unsigned long timeout;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+
+ /*
+ * On NCR53C400 boards, NCR5380 registers are mapped 8 past
+ * the base address.
+ */
+
+#ifdef NCR53C400
+ if (flags & FLAG_NCR53C400)
+ instance->NCR5380_instance_name += NCR53C400_address_adjust;
+#endif
+
+ NCR5380_setup(instance);
+
+ NCR5380_all_init();
+
+ hostdata->aborted = 0;
+ hostdata->id_mask = 1 << instance->this_id;
+ for (i = hostdata->id_mask; i <= 0x80; i <<= 1)
+ if (i > hostdata->id_mask)
+ hostdata->id_higher_mask |= i;
+ for (i = 0; i < 8; ++i)
+ hostdata->busy[i] = 0;
+#ifdef REAL_DMA
+ hostdata->dmalen = 0;
+#endif
+ hostdata->targets_present = 0;
+ hostdata->connected = NULL;
+ hostdata->issue_queue = NULL;
+ hostdata->disconnected_queue = NULL;
+
+ /* The CHECK code seems to break the 53C400. Will check it later maybe */
+ if (flags & FLAG_NCR53C400)
+ hostdata->flags = FLAG_HAS_LAST_BYTE_SENT | flags;
+ else
+ hostdata->flags = FLAG_CHECK_LAST_BYTE_SENT | flags;
+
+ if (!the_template) {
+ the_template = instance->hostt;
+ first_instance = instance;
+ }
+
+
+#ifdef USLEEP
+ hostdata->time_expires = 0;
+ hostdata->next_timer = NULL;
+#endif
+
+#ifndef AUTOSENSE
+ if ((instance->cmd_per_lun > 1) || instance->can_queue > 1))
+ printk("scsi%d : WARNING : support for multiple outstanding commands enabled\n"
+ " without AUTOSENSE option, contingent allegiance conditions may\n"
+ " be incorrectly cleared.\n", instance->host_no);
+#endif /* def AUTOSENSE */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+#ifdef NCR53C400
+ if (hostdata->flags & FLAG_NCR53C400) {
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ }
+#endif
+
+ /*
+ * Detect and correct bus wedge problems.
+ *
+ * If the system crashed, it may have crashed in a state
+ * where a SCSI command was still executing, and the
+ * SCSI bus is not in a BUS FREE STATE.
+ *
+ * If this is the case, we'll try to abort the currently
+ * established nexus which we know nothing about, and that
+ * failing, do a hard reset of the SCSI bus
+ */
+
+ for (pass = 1; (NCR5380_read(STATUS_REG) & SR_BSY) &&
+ pass <= 6 ; ++pass) {
+ switch (pass) {
+ case 1:
+ case 3:
+ case 5:
+ printk("scsi%d: SCSI bus busy, waiting up to five seconds\n",
+ instance->host_no);
+ timeout = jiffies + 500;
+ while (jiffies < timeout && (NCR5380_read(STATUS_REG) & SR_BSY));
+ break;
+ case 2:
+ printk("scsi%d: bus busy, attempting abort\n",
+ instance->host_no);
+ do_abort (instance);
+ break;
+ case 4:
+ printk("scsi%d: bus busy, attempting reset\n",
+ instance->host_no);
+ do_reset (instance);
+ break;
+ case 6:
+ printk("scsi%d: bus locked solid or invalid override\n",
+ instance->host_no);
+ }
+ }
+}
+
+/*
+ * Function : int NCR5380_queue_command (Scsi_Cmnd *cmd,
+ * void (*done)(Scsi_Cmnd *))
+ *
+ * Purpose : enqueues a SCSI command
+ *
+ * Inputs : cmd - SCSI command, done - function called on completion, with
+ * a pointer to the command descriptor.
+ *
+ * Returns : 0
+ *
+ * Side effects :
+ * cmd is added to the per instance issue_queue, with minor
+ * twiddling done to the host specific fields of cmd. If the
+ * main coroutine is not running, it is restarted.
+ *
+ */
+
+/* Only make static if a wrapper function is used */
+#ifndef NCR5380_queue_command
+static
+#endif
+int NCR5380_queue_command (Scsi_Cmnd *cmd, void (*done)(Scsi_Cmnd *)) {
+ struct Scsi_Host *instance = cmd->host;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ Scsi_Cmnd *tmp;
+
+#if (NDEBUG & NDEBUG_NO_WRITE)
+ switch (cmd->cmnd[0]) {
+ case WRITE:
+ case WRITE_10:
+ printk("scsi%d : WRITE attempted with NO_WRITE debugging flag set\n",
+ instance->host_no);
+ cmd->result = (DID_ERROR << 16);
+ done(cmd);
+ return 0;
+ }
+#endif /* (NDEBUG & NDEBUG_NO_WRITE) */
+
+
+ /*
+ * We use the host_scribble field as a pointer to the next command
+ * in a queue
+ */
+
+ cmd->host_scribble = NULL;
+ cmd->scsi_done = done;
+
+ cmd->result = 0;
+
+
+ /*
+ * Insert the cmd into the issue queue. Note that REQUEST SENSE
+ * commands are added to the head of the queue since any command will
+ * clear the contingent allegiance condition that exists and the
+ * sense data is only guaranteed to be valid while the condition exists.
+ */
+
+ cli();
+ if (!(hostdata->issue_queue) || (cmd->cmnd[0] == REQUEST_SENSE)) {
+ LIST(cmd, hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *) hostdata->issue_queue;
+ hostdata->issue_queue = cmd;
+ } else {
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue; tmp->host_scribble;
+ tmp = (Scsi_Cmnd *) tmp->host_scribble);
+ LIST(cmd, tmp);
+ tmp->host_scribble = (unsigned char *) cmd;
+ }
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : command added to %s of queue\n", instance->host_no,
+ (cmd->cmnd[0] == REQUEST_SENSE) ? "head" : "tail");
+#endif
+
+/* Run the coroutine if it isn't already running. */
+ run_main();
+ return 0;
+}
+
+/*
+ * Function : NCR5380_main (void)
+ *
+ * Purpose : NCR5380_main is a coroutine that runs as long as more work can
+ * be done on the NCR5380 host adapters in a system. Both
+ * NCR5380_queue_command() and NCR5380_intr() will try to start it
+ * in case it is not running.
+ *
+ * NOTE : NCR5380_main exits with interrupts *disabled*, the caller should
+ * reenable them. This prevents reentrancy and kernel stack overflow.
+ */
+
+static void NCR5380_main (void) {
+ Scsi_Cmnd *tmp, *prev;
+ struct Scsi_Host *instance;
+ struct NCR5380_hostdata *hostdata;
+ int done;
+
+ /*
+ * We run (with interrupts disabled) until we're sure that none of
+ * the host adapters have anything that can be done, at which point
+ * we set main_running to 0 and exit.
+ *
+ * Interrupts are enabled before doing various other internal
+ * instructions, after we've decided that we need to run through
+ * the loop again.
+ *
+ * this should prevent any race conditions.
+ */
+
+ do {
+ cli(); /* Freeze request queues */
+ done = 1;
+ for (instance = first_instance; instance &&
+ instance->hostt == the_template; instance=instance->next) {
+ hostdata = (struct NCR5380_hostdata *) instance->hostdata;
+ cli();
+ if (!hostdata->connected) {
+#if (NDEBUG & NDEBUG_MAIN)
+ printk("scsi%d : not connected\n", instance->host_no);
+#endif
+ /*
+ * Search through the issue_queue for a command destined
+ * for a target that's not busy.
+ */
+#if (NDEBUG & NDEBUG_LISTS)
+ for (tmp= (Scsi_Cmnd *) hostdata->issue_queue, prev=NULL; tmp && (tmp != prev); prev=tmp, tmp=(Scsi_Cmnd*)tmp->host_scribble)
+ ;
+ /*printk("%p ", tmp);*/
+ if ((tmp == prev) && tmp) printk(" LOOP\n");/* else printk("\n");*/
+#endif
+ for (tmp = (Scsi_Cmnd *) hostdata->issue_queue,
+ prev = NULL; tmp; prev = tmp, tmp = (Scsi_Cmnd *)
+ tmp->host_scribble) {
+
+#if (NDEBUG & NDEBUG_LISTS)
+ if (prev != tmp)
+ printk("MAIN tmp=%p target=%d busy=%d lun=%d\n", tmp, tmp->target, hostdata->busy[tmp->target], tmp->lun);
+#endif
+ /* When we find one, remove it from the issue queue. */
+ if (!(hostdata->busy[tmp->target] & (1 << tmp->lun))) {
+ if (prev) {
+ REMOVE(prev,prev->host_scribble,tmp,tmp->host_scribble);
+ prev->host_scribble = tmp->host_scribble;
+ } else {
+ REMOVE(-1,hostdata->issue_queue,tmp,tmp->host_scribble);
+ hostdata->issue_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+ tmp->host_scribble = NULL;
+
+ /* reenable interrupts after finding one */
+ sti();
+
+ /*
+ * Attempt to establish an I_T_L nexus here.
+ * On success, instance->hostdata->connected is set.
+ * On failure, we must add the command back to the
+ * issue queue so we can keep trying.
+ */
+#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
+ printk("scsi%d : main() : command for target %d lun %d removed from issue_queue\n",
+ instance->host_no, tmp->target, tmp->lun);
+#endif
+
+ /*
+ * A successful selection is defined as one that
+ * leaves us with the command connected and
+ * in hostdata->connected, OR has terminated the
+ * command.
+ *
+ * With successfull commands, we fall through
+ * and see if we can do an information transfer,
+ * with failures we will restart.
+ */
+
+ if (!NCR5380_select(instance, tmp,
+ /*
+ * REQUEST SENSE commands are issued without tagged
+ * queueing, even on SCSI-II devices because the
+ * contingent allegiance condition exists for the
+ * entire unit.
+ */
+ (tmp->cmnd[0] == REQUEST_SENSE) ? TAG_NONE :
+ TAG_NEXT)) {
+ break;
+ } else {
+ cli();
+ LIST(tmp, hostdata->issue_queue);
+ tmp->host_scribble = (unsigned char *)
+ hostdata->issue_queue;
+ hostdata->issue_queue = tmp;
+ done = 0;
+ sti();
+#if (NDEBUG & (NDEBUG_MAIN | NDEBUG_QUEUES))
+ printk("scsi%d : main(): select() failed, returned to issue_queue\n",
+ instance->host_no);
+#endif
+ }
+ } /* if target/lun is not busy */
+ } /* for */
+ } /* if (!hostdata->connected) */
+
+ if (hostdata->connected
+#ifdef REAL_DMA
+ && !hostdata->dmalen
+#endif
+#ifdef USLEEP
+ && (!hostdata->time_expires || hostdata->time_expires >= jiffies)
+#endif
+ ) {
+ sti();
+#if (NDEBUG & NDEBUG_MAIN)
+ printk("scsi%d : main() : performing information transfer\n",
+ instance->host_no);
+#endif
+ NCR5380_information_transfer(instance);
+#if (NDEBUG & NDEBUG_MAIN)
+ printk("scsi%d : main() : done set false\n", instance->host_no);
+#endif
+ done = 0;
+ } else
+ break;
+ } /* for instance */
+ } while (!done);
+ main_running = 0;
+}
+
+/*
+ * Function : void NCR5380_intr (int irq)
+ *
+ * Purpose : handle interrupts, reestablishing I_T_L or I_T_L_Q nexuses
+ * from the disconnected queue, and restarting NCR5380_main()
+ * as required.
+ *
+ * Inputs : int irq, irq that caused this interrupt.
+ *
+ */
+
+static void NCR5380_intr (int irq, struct pt_regs * regs) {
+ NCR5380_local_declare();
+ struct Scsi_Host *instance;
+ int done;
+ unsigned char basr;
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi : NCR5380 irq %d triggered\n", irq);
+#endif
+ do {
+ done = 1;
+ for (instance = first_instance; instance && (instance->hostt ==
+ the_template); instance = instance->next)
+ if (instance->irq == irq) {
+
+ /* Look for pending interrupts */
+ NCR5380_setup(instance);
+ basr = NCR5380_read(BUS_AND_STATUS_REG);
+ /* XXX dispatch to appropriate routine if found and done=0 */
+ if (basr & BASR_IRQ) {
+#if (NDEBUG & NDEBUG_INTR)
+ NCR5380_print(instance);
+#endif
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ done = 0;
+ sti();
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi%d : SEL interrupt\n", instance->host_no);
+#endif
+ NCR5380_reselect(instance);
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else if (basr & BASR_PARITY_ERROR) {
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi%d : PARITY interrupt\n", instance->host_no);
+#endif
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else if ((NCR5380_read(STATUS_REG) & SR_RST) == SR_RST) {
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi%d : RESET interrupt\n", instance->host_no);
+#endif
+ (void)NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else {
+/*
+ * XXX the rest of the interrupt conditions should *only* occur during a
+ * DMA transfer, which I haven't gotten around to fixing yet.
+ */
+
+#if defined(REAL_DMA)
+ /*
+ * We should only get PHASE MISMATCH and EOP interrupts
+ * if we have DMA enabled, so do a sanity check based on
+ * the current setting of the MODE register.
+ */
+
+ if ((NCR5380_read(MODE_REG) & MR_DMA) && ((basr &
+ BASR_END_DMA_TRANSFER) ||
+ !(basr & BASR_PHASE_MATCH))) {
+ int transfered;
+
+ if (!hostdata->connected)
+ panic("scsi%d : received end of DMA interrupt with no connected cmd\n",
+ instance->hostno);
+
+ transfered = (hostdata->dmalen - NCR5380_dma_residual(instance));
+ hostdata->connected->SCp.this_residual -= transferred;
+ hostdata->connected->SCp.ptr += transferred;
+ hostdata->dmalen = 0;
+
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + NCR_TIMEOUT;
+
+ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK
+ && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ printk("scsi%d: timeout at NCR5380.c:%d\n",
+ host->host_no, __LINE__);
+ }
+#else /* NCR_TIMEOUT */
+ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+#else
+#if (NDEBUG & NDEBUG_INTR)
+ printk("scsi : unknown interrupt, BASR 0x%X, MR 0x%X, SR 0x%x\n", basr, NCR5380_read(MODE_REG), NCR5380_read(STATUS_REG));
+#endif
+ (void) NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+#endif
+ }
+ } /* if BASR_IRQ */
+ if (!done)
+ run_main();
+ } /* if (instance->irq == irq) */
+ } while (!done);
+}
+
+/*
+ * Function : int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ * int tag);
+ *
+ * Purpose : establishes I_T_L or I_T_L_Q nexus for new or existing command,
+ * including ARBITRATION, SELECTION, and initial message out for
+ * IDENTIFY and queue messages.
+ *
+ * Inputs : instance - instantiation of the 5380 driver on which this
+ * target lives, cmd - SCSI command to execute, tag - set to TAG_NEXT for
+ * new tag, TAG_NONE for untagged queueing, otherwise set to the tag for
+ * the command that is presently connected.
+ *
+ * Returns : -1 if selection could not execute for some reason,
+ * 0 if selection succeeded or failed because the target
+ * did not respond.
+ *
+ * Side effects :
+ * If bus busy, arbitration failed, etc, NCR5380_select() will exit
+ * with registers as they should have been on entry - ie
+ * SELECT_ENABLE will be set appropriately, the NCR5380
+ * will cease to drive any SCSI bus signals.
+ *
+ * If successful : I_T_L or I_T_L_Q nexus will be established,
+ * instance->connected will be set to cmd.
+ * SELECT interrupt will be disabled.
+ *
+ * If failed (no target) : cmd->scsi_done() will be called, and the
+ * cmd->result host byte set to DID_BAD_TARGET.
+ */
+
+static int NCR5380_select (struct Scsi_Host *instance, Scsi_Cmnd *cmd,
+ int tag) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata*)
+ instance->hostdata;
+ unsigned char tmp[3], phase;
+ unsigned char *data;
+ int len;
+ unsigned long timeout;
+ NCR5380_setup(instance);
+
+ hostdata->restart_select = 0;
+#if defined (NDEBUG) && (NDEBUG & NDEBUG_ARBITRATION)
+ NCR5380_print(instance);
+ printk("scsi%d : starting arbitration, id = %d\n", instance->host_no,
+ instance->this_id);
+#endif
+ cli();
+
+ /*
+ * Set the phase bits to 0, otherwise the NCR5380 won't drive the
+ * data bus during SELECTION.
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+
+ /*
+ * Start arbitration.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, hostdata->id_mask);
+ NCR5380_write(MODE_REG, MR_ARBITRATE);
+
+ sti();
+
+ /* Wait for arbitration logic to complete */
+#if NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + 2*NCR_TIMEOUT;
+
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS)
+ && jiffies < timeout)
+ ;
+ if (jiffies >= timeout)
+ {
+ printk("scsi: arbitration timeout at %d\n", __LINE__);
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_PROGRESS));
+#endif
+
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : arbitration complete\n", instance->host_no);
+/* Avoid GCC 2.4.5 asm needs to many reloads error */
+ __asm__("nop");
+#endif
+
+ /*
+ * The arbitration delay is 2.2us, but this is a minimum and there is
+ * no maximum so we can safely sleep for ceil(2.2) usecs to accommodate
+ * the integral nature of udelay().
+ *
+ */
+
+ udelay(3);
+
+ /* Check for lost arbitration */
+ if ((NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) ||
+ (NCR5380_read(CURRENT_SCSI_DATA_REG) & hostdata->id_higher_mask) ||
+ (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST)) {
+ NCR5380_write(MODE_REG, MR_BASE);
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : lost arbitration, deasserting MR_ARBITRATE\n",
+ instance->host_no);
+#endif
+ return -1;
+ }
+
+
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_SEL);
+
+ if (NCR5380_read(INITIATOR_COMMAND_REG) & ICR_ARBITRATION_LOST) {
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : lost arbitration, deasserting ICR_ASSERT_SEL\n",
+ instance->host_no);
+#endif
+ return -1;
+ }
+
+ /*
+ * Again, bus clear + bus settle time is 1.2us, however, this is
+ * a minimum so we'll udelay ceil(1.2)
+ */
+
+ udelay(2);
+
+#if (NDEBUG & NDEBUG_ARBITRATION)
+ printk("scsi%d : won arbitration\n", instance->host_no);
+#endif
+
+
+ /*
+ * Now that we have won arbitration, start Selection process, asserting
+ * the host and target ID's on the SCSI bus.
+ */
+
+ NCR5380_write(OUTPUT_DATA_REG, (hostdata->id_mask | (1 << cmd->target)));
+
+ /*
+ * Raise ATN while SEL is true before BSY goes false from arbitration,
+ * since this is the only way to guarantee that we'll get a MESSAGE OUT
+ * phase immediately after selection.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_BSY |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_SEL ));
+ NCR5380_write(MODE_REG, MR_BASE);
+
+ /*
+ * Reselect interrupts must be turned off prior to the dropping of BSY,
+ * otherwise we will trigger an interrupt.
+ */
+ NCR5380_write(SELECT_ENABLE_REG, 0);
+
+ /*
+ * The initiator shall then wait at least two deskew delays and release
+ * the BSY signal.
+ */
+ udelay(1); /* wingel -- wait two bus deskew delay >2*45ns */
+
+ /* Reset BSY */
+ NCR5380_write(INITIATOR_COMMAND_REG, (ICR_BASE | ICR_ASSERT_DATA |
+ ICR_ASSERT_ATN | ICR_ASSERT_SEL));
+
+ /*
+ * Something weird happens when we cease to drive BSY - looks
+ * like the board/chip is letting us do another read before the
+ * appropriate propagation delay has expired, and we're confusing
+ * a BSY signal from ourselves as the target's response to SELECTION.
+ *
+ * A small delay (the 'C++' frontend breaks the pipeline with an
+ * unnecessary jump, making it work on my 386-33/Trantor T128, the
+ * tighter 'C' code breaks and requires this) solves the problem -
+ * the 1 us delay is arbitrary, and only used because this delay will
+ * be the same on other platforms and since it works here, it should
+ * work there.
+ *
+ * wingel suggests that this could be due to failing to wait
+ * one deskew delay.
+ */
+
+ udelay(1);
+
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : selecting target %d\n", instance->host_no, cmd->target);
+#endif
+
+ /*
+ * The SCSI specification calls for a 250 ms timeout for the actual
+ * selection.
+ */
+
+ timeout = jiffies + 25;
+
+ /*
+ * XXX very interesting - we're seeing a bounce where the BSY we
+ * asserted is being reflected / still asserted (propagation delay?)
+ * and it's detecting as true. Sigh.
+ */
+
+ while ((jiffies < timeout) && !(NCR5380_read(STATUS_REG) &
+ (SR_BSY | SR_IO)));
+
+ if ((NCR5380_read(STATUS_REG) & (SR_SEL | SR_IO)) ==
+ (SR_SEL | SR_IO)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ NCR5380_reselect(instance);
+ printk ("scsi%d : reselection after won arbitration?\n",
+ instance->host_no);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+
+ /*
+ * No less than two deskew delays after the initiator detects the
+ * BSY signal is true, it shall release the SEL signal and may
+ * change the DATA BUS. -wingel
+ */
+
+ udelay(1);
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ if (!(NCR5380_read(STATUS_REG) & SR_BSY)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ if (hostdata->targets_present & (1 << cmd->target)) {
+ printk("scsi%d : weirdness\n", instance->host_no);
+ if (hostdata->restart_select)
+ printk("\trestart select\n");
+#ifdef NDEBUG
+ NCR5380_print (instance);
+#endif
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ cmd->result = DID_BAD_TARGET << 16;
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : target did not respond within 250ms\n",
+ instance->host_no);
+#endif
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return 0;
+ }
+
+ hostdata->targets_present |= (1 << cmd->target);
+
+ /*
+ * Since we followed the SCSI spec, and raised ATN while SEL
+ * was true but before BSY was false during selection, the information
+ * transfer phase should be a MESSAGE OUT phase so that we can send the
+ * IDENTIFY message.
+ *
+ * If SCSI-II tagged queuing is enabled, we also send a SIMPLE_QUEUE_TAG
+ * message (2 bytes) with a tag ID that we increment with every command
+ * until it wraps back to 0.
+ *
+ * XXX - it turns out that there are some broken SCSI-II devices,
+ * which claim to support tagged queuing but fail when more than
+ * some number of commands are issued at once.
+ */
+
+ /* Wait for start of REQ/ACK handshake */
+#ifdef NCR_TIMEOUT
+ {
+ unsigned long timeout = jiffies + NCR_TIMEOUT;
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ) && jiffies < timeout);
+
+ if (jiffies >= timeout) {
+ printk("scsi%d: timeout at NCR5380.c:%d\n", __LINE__);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return -1;
+ }
+ }
+#else /* NCR_TIMEOUT */
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+#endif /* def NCR_TIMEOUT */
+
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : target %d selected, going into MESSAGE OUT phase.\n",
+ instance->host_no, cmd->target);
+#endif
+ tmp[0] = IDENTIFY(((instance->irq == IRQ_NONE) ? 0 : 1), cmd->lun);
+#ifdef SCSI2
+ if (cmd->device->tagged_queue && (tag != TAG_NONE)) {
+ tmp[1] = SIMPLE_QUEUE_TAG;
+ if (tag == TAG_NEXT) {
+ /* 0 is TAG_NONE, used to imply no tag for this command */
+ if (cmd->device->current_tag == 0)
+ cmd->device->current_tag = 1;
+
+ cmd->tag = cmd->device->current_tag;
+ cmd->device->current_tag++;
+ } else
+ cmd->tag = (unsigned char) tag;
+
+ tmp[2] = cmd->tag;
+ hostdata->last_message = SIMPLE_QUEUE_TAG;
+ len = 3;
+ } else
+#endif /* def SCSI2 */
+ {
+ len = 1;
+ cmd->tag=0;
+ }
+
+ /* Send message(s) */
+ data = tmp;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+#if (NDEBUG & NDEBUG_SELECTION)
+ printk("scsi%d : nexus established.\n", instance->host_no);
+#endif
+ /* XXX need to handle errors here */
+ hostdata->connected = cmd;
+#ifdef SCSI2
+ if (!cmd->device->tagged_queue)
+#endif
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+
+ initialize_SCp(cmd);
+
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using polled I/O
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ * XXX Note : handling for bus free may be useful.
+ */
+
+/*
+ * Note : this code is not as quick as it could be, however it
+ * IS 100% reliable, and for the actual data transfer where speed
+ * counts, we will always do a pseudo DMA or DMA transfer.
+ */
+
+static int NCR5380_transfer_pio (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data) {
+ NCR5380_local_declare();
+ register unsigned char p = *phase, tmp;
+ register int c = *count;
+ register unsigned char *d = *data;
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_PIO)
+ if (!(p & SR_IO))
+ printk("scsi%d : pio write %d bytes\n", instance->host_no, c);
+ else
+ printk("scsi%d : pio read %d bytes\n", instance->host_no, c);
+#endif
+
+ /*
+ * The NCR5380 chip will only drive the SCSI bus when the
+ * phase specified in the appropriate bits of the TARGET COMMAND
+ * REGISTER match the STATUS REGISTER
+ */
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+ do {
+ /*
+ * Wait for assertion of REQ, after which the phase bits will be
+ * valid
+ */
+ while (!((tmp = NCR5380_read(STATUS_REG)) & SR_REQ));
+
+#if (NDEBUG & NDEBUG_HANDSHAKE)
+ printk("scsi%d : REQ detected\n", instance->host_no);
+#endif
+
+ /* Check for phase mismatch */
+ if ((tmp & PHASE_MASK) != p) {
+#if (NDEBUG & NDEBUG_PIO)
+ printk("scsi%d : phase mismatch\n", instance->host_no);
+ NCR5380_print_phase(instance);
+#endif
+ break;
+ }
+
+ /* Do actual transfer from SCSI bus to / from memory */
+ if (!(p & SR_IO))
+ NCR5380_write(OUTPUT_DATA_REG, *d);
+ else
+ *d = NCR5380_read(CURRENT_SCSI_DATA_REG);
+
+ ++d;
+
+ /*
+ * The SCSI standard suggests that in MSGOUT phase, the initiator
+ * should drop ATN on the last byte of the message phase
+ * after REQ has been asserted for the handshake but before
+ * the initiator raises ACK.
+ */
+
+ if (!(p & SR_IO)) {
+ if (!((p & SR_MSG) && c > 1)) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA);
+#if (NDEBUG & NDEBUG_PIO)
+ NCR5380_print(instance);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ACK);
+ } else {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN);
+#if (NDEBUG & NDEBUG_PIO)
+ NCR5380_print(instance);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_DATA | ICR_ASSERT_ATN | ICR_ASSERT_ACK);
+ }
+ } else {
+#if (NDEBUG & NDEBUG_PIO)
+ NCR5380_print(instance);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ACK);
+ }
+
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+
+#if (NDEBUG & NDEBUG_HANDSHAKE)
+ printk("scsi%d : req false, handshake complete\n", instance->host_no);
+#endif
+
+/*
+ * We have several special cases to consider during REQ/ACK handshaking :
+ * 1. We were in MSGOUT phase, and we are on the last byte of the
+ * message. ATN must be dropped as ACK is dropped.
+ *
+ * 2. We are in a MSGIN phase, and we are on the last byte of the
+ * message. We must exit with ACK asserted, so that the calling
+ * code may raise ATN before dropping ACK to reject the message.
+ *
+ * 3. ACK and ATN are clear and the target may proceed as normal.
+ */
+ if (!(p == PHASE_MSGIN && c == 1)) {
+ if (p == PHASE_MSGOUT && c > 1)
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ else
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ }
+ } while (--c);
+
+#if (NDEBUG & NDEBUG_PIO)
+ printk("scsi%d : residual %d\n", instance->host_no, c);
+#endif
+
+ *count = c;
+ *data = d;
+ tmp = NCR5380_read(STATUS_REG);
+ if (tmp & SR_REQ)
+ *phase = tmp & PHASE_MASK;
+ else
+ *phase = PHASE_UNKNOWN;
+
+ if (!c || (*phase == p))
+ return 0;
+ else
+ return -1;
+}
+
+static void do_reset (struct Scsi_Host *host) {
+ NCR5380_local_declare();
+ NCR5380_setup(host);
+
+ cli();
+ NCR5380_write(TARGET_COMMAND_REG,
+ PHASE_SR_TO_TCR(NCR5380_read(STATUS_REG) & PHASE_MASK));
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_RST);
+ udelay(25);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ sti();
+}
+
+/*
+ * Function : do_abort (Scsi_Host *host)
+ *
+ * Purpose : abort the currently established nexus. Should only be
+ * called from a routine which can drop into a
+ *
+ * Returns : 0 on success, -1 on failure.
+ */
+
+static int do_abort (struct Scsi_Host *host) {
+ NCR5380_local_declare();
+ unsigned char tmp, *msgptr, phase;
+ int len;
+ NCR5380_setup(host);
+
+
+ /* Request message out phase */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+
+ /*
+ * Wait for the target to indicate a valid phase by asserting
+ * REQ. Once this happens, we'll have either a MSGOUT phase
+ * and can immediately send the ABORT message, or we'll have some
+ * other phase and will have to source/sink data.
+ *
+ * We really don't care what value was on the bus or what value
+ * the target see's, so we just handshake.
+ */
+
+ while (!(tmp = NCR5380_read(STATUS_REG)) & SR_REQ);
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ if ((tmp & PHASE_MASK) != PHASE_MSGOUT) {
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN);
+ }
+
+ tmp = ABORT;
+ msgptr = &tmp;
+ len = 1;
+ phase = PHASE_MSGOUT;
+ NCR5380_transfer_pio (host, &phase, &len, &msgptr);
+
+ /*
+ * If we got here, and the command completed successfully,
+ * we're about to go into bus free state.
+ */
+
+ return len ? -1 : 0;
+}
+
+#if defined(REAL_DMA) || defined(PSEUDO_DMA) || defined (REAL_DMA_POLL)
+/*
+ * Function : int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ * unsigned char *phase, int *count, unsigned char **data)
+ *
+ * Purpose : transfers data in given phase using either real
+ * or pseudo DMA.
+ *
+ * Inputs : instance - instance of driver, *phase - pointer to
+ * what phase is expected, *count - pointer to number of
+ * bytes to transfer, **data - pointer to data pointer.
+ *
+ * Returns : -1 when different phase is entered without transferring
+ * maximum number of bytes, 0 if all bytes or transfered or exit
+ * is in same phase.
+ *
+ * Also, *phase, *count, *data are modified in place.
+ *
+ */
+
+
+static int NCR5380_transfer_dma (struct Scsi_Host *instance,
+ unsigned char *phase, int *count, unsigned char **data) {
+ NCR5380_local_declare();
+ register int c = *count;
+ register unsigned char p = *phase;
+ register unsigned char *d = *data;
+ unsigned char tmp;
+ int foo;
+#if defined(REAL_DMA_POLL)
+ int cnt, toPIO;
+ unsigned char saved_data = 0, overrun = 0, residue;
+#endif
+
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+
+ NCR5380_setup(instance);
+
+ if ((tmp = (NCR5380_read(STATUS_REG) & PHASE_MASK)) != p) {
+ *phase = tmp;
+ return -1;
+ }
+#if defined(REAL_DMA) || defined(REAL_DMA_POLL)
+#ifdef READ_OVERRUNS
+ if (p & SR_IO) {
+ c -= 2;
+ }
+#endif
+#if (NDEBUG & NDEBUG_DMA)
+ printk("scsi%d : initializing DMA channel %d for %s, %d bytes %s %0x\n",
+ instance->host_no, instance->dma_channel, (p & SR_IO) ? "reading" :
+ "writing", c, (p & SR_IO) ? "to" : "from", (unsigned) d);
+#endif
+ hostdata->dma_len = (p & SR_IO) ?
+ NCR5380_dma_read_setup(instance, d, c) :
+ NCR5380_dma_write_setup(instance, d, c);
+#endif
+
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(p));
+
+#ifdef REAL_DMA
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_EOP_INTR | MR_MONITOR_BSY);
+#elif defined(REAL_DMA_POLL)
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#else
+ /*
+ * Note : on my sample board, watch-dog timeouts occurred when interrupts
+ * were not disabled for the duration of a single DMA transfer, from
+ * before the setting of DMA mode to after transfer of the last byte.
+ */
+
+#if defined(PSEUDO_DMA) && !defined(UNSAFE)
+ cli();
+#endif
+ /* KLL May need eop and parity in 53c400 */
+ if (hostdata->flags & FLAG_NCR53C400)
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE | MR_ENABLE_PAR_CHECK
+ | MR_ENABLE_PAR_INTR | MR_ENABLE_EOP_INTR | MR_DMA_MODE
+ | MR_MONITOR_BSY);
+ else
+ NCR5380_write(MODE_REG, MR_BASE | MR_DMA_MODE);
+#endif /* def REAL_DMA */
+
+#if (NDEBUG & NDEBUG_DMA) & 0
+ printk("scsi%d : mode reg = 0x%X\n", instance->host_no, NCR5380_read(MODE_REG));
+#endif
+
+/*
+ * FOO stuff. For some UNAPPARENT reason, I'm getting
+ * watchdog timers fired on bootup for NO APPARENT REASON, meaning it's
+ * probably a timing problem.
+ *
+ * Since this is the only place I have back-to-back writes, perhaps this
+ * is the problem?
+ */
+
+ if (p & SR_IO) {
+#ifndef FOO
+ udelay(1);
+#endif
+ NCR5380_write(START_DMA_INITIATOR_RECEIVE_REG, 0);
+ } else {
+#ifndef FOO
+ udelay(1);
+#endif
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_DATA);
+#ifndef FOO
+ udelay(1);
+#endif
+ NCR5380_write(START_DMA_SEND_REG, 0);
+#ifndef FOO
+ udelay(1);
+#endif
+ }
+
+#if defined(REAL_DMA_POLL)
+ do {
+ tmp = NCR5380_read(BUS_AND_STATUS_REG);
+ } while ((tmp & BASR_PHASE_MATCH) && !(tmp & (BASR_BUSY_ERROR |
+ BASR_END_DMA_TRANSFER)));
+
+/*
+ At this point, either we've completed DMA, or we have a phase mismatch,
+ or we've unexpectedly lost BUSY (which is a real error).
+
+ For write DMAs, we want to wait until the last byte has been
+ transferred out over the bus before we turn off DMA mode. Alas, there
+ seems to be no terribly good way of doing this on a 5380 under all
+ conditions. For non-scatter-gather operations, we can wait until REQ
+ and ACK both go false, or until a phase mismatch occurs. Gather-writes
+ are nastier, since the device will be expecting more data than we
+ are prepared to send it, and REQ will remain asserted. On a 53C8[01] we
+ could test LAST BIT SENT to assure transfer (I imagine this is precisely
+ why this signal was added to the newer chips) but on the older 538[01]
+ this signal does not exist. The workaround for this lack is a watchdog;
+ we bail out of the wait-loop after a modest amount of wait-time if
+ the usual exit conditions are not met. Not a terribly clean or
+ correct solution :-%
+
+ Reads are equally tricky due to a nasty characteristic of the NCR5380.
+ If the chip is in DMA mode for an READ, it will respond to a target's
+ REQ by latching the SCSI data into the INPUT DATA register and asserting
+ ACK, even if it has _already_ been notified by the DMA controller that
+ the current DMA transfer has completed! If the NCR5380 is then taken
+ out of DMA mode, this already-acknowledged byte is lost.
+
+ This is not a problem for "one DMA transfer per command" reads, because
+ the situation will never arise... either all of the data is DMA'ed
+ properly, or the target switches to MESSAGE IN phase to signal a
+ disconnection (either operation bringing the DMA to a clean halt).
+ However, in order to handle scatter-reads, we must work around the
+ problem. The chosen fix is to DMA N-2 bytes, then check for the
+ condition before taking the NCR5380 out of DMA mode. One or two extra
+ bytes are transferred via PIO as necessary to fill out the original
+ request.
+*/
+
+ if (p & SR_IO) {
+#ifdef READ_OVERRUNS
+ udelay(10);
+ if (((NCR5380_read(BUS_AND_STATUS_REG) & (BASR_PHASE_MATCH|BASR_ACK)) ==
+ (BASR_PHASE_MATCH | BASR_ACK))) {
+ saved_data = NCR5380_read(INPUT_DATA_REGISTER);
+ overrun = 1;
+ }
+#endif
+ } else {
+ int limit = 100;
+ while (((tmp = NCR5380_read(BUS_AND_STATUS_REG)) & BASR_ACK) ||
+ (NCR5380_read(STATUS_REG) & SR_REQ)) {
+ if (!(tmp & BASR_PHASE_MATCH)) break;
+ if (--limit < 0) break;
+ }
+ }
+
+
+#if (NDEBUG & NDEBUG_DMA)
+ printk("scsi%d : polled DMA transfer complete, basr 0x%X, sr 0x%X\n",
+ instance->host_no, tmp, NCR5380_read(STATUS_REG));
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ residue = NCR5380_dma_residual(instance);
+ c -= residue;
+ *count -= c;
+ *data += c;
+ *phase = NCR5380_read(STATUS_REG) & PHASE_MASK;
+
+#ifdef READ_OVERRUNS
+ if (*phase == p && (p & SR_IO) && residue == 0) {
+ if (overrun) {
+#if (NDEBUG & NDEBUG_DMA)
+ printk("Got an input overrun, using saved byte\n");
+#endif
+ **data = saved_data;
+ *data += 1;
+ *count -= 1;
+ cnt = toPIO = 1;
+ } else {
+ printk("No overrun??\n");
+ cnt = toPIO = 2;
+ }
+#if (NDEBUG & NDEBUG_DMA)
+ printk("Doing %d-byte PIO to 0x%X\n", cnt, *data);
+#endif
+ NCR5380_transfer_pio(instance, phase, &cnt, data);
+ *count -= toPIO - cnt;
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_DMA)
+ printk("Return with data ptr = 0x%X, count %d, last 0x%X, next 0x%X\n",
+ *data, *count, *(*data+*count-1), *(*data+*count));
+#endif
+ return 0;
+
+#elif defined(REAL_DMA)
+ return 0;
+#else /* defined(REAL_DMA_POLL) */
+ if (p & SR_IO) {
+ int diff = 1;
+ if (hostdata->flags & FLAG_NCR53C400) {
+ diff=0;
+ }
+
+ if (!(foo = NCR5380_pread(instance, d, c - diff))) {
+ /*
+ * We can't disable DMA mode after successfully transferring
+ * what we plan to be the last byte, since that would open up
+ * a race condition where if the target asserted REQ before
+ * we got the DMA mode reset, the NCR5380 would have latched
+ * an additional byte into the INPUT DATA register and we'd
+ * have dropped it.
+ *
+ * The workaround was to transfer one fewer bytes than we
+ * intended to with the pseudo-DMA read function, wait for
+ * the chip to latch the last byte, read it, and then disable
+ * pseudo-DMA mode.
+ *
+ * After REQ is asserted, the NCR5380 asserts DRQ and ACK.
+ * REQ is deasserted when ACK is asserted, and not reasserted
+ * until ACK goes false. Since the NCR5380 won't lower ACK
+ * until DACK is asserted, which won't happen unless we twiddle
+ * the DMA port or we take the NCR5380 out of DMA mode, we
+ * can guarantee that we won't handshake another extra
+ * byte.
+ */
+
+ if (!(hostdata->flags & FLAG_NCR53C400)) {
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_DRQ));
+ /* Wait for clean handshake */
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ d[c - 1] = NCR5380_read(INPUT_DATA_REG);
+ }
+ }
+ } else {
+ int timeout;
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("About to pwrite %d bytes\n", c);
+#endif
+ if (!(foo = NCR5380_pwrite(instance, d, c))) {
+ /*
+ * Wait for the last byte to be sent. If REQ is being asserted for
+ * the byte we're interested, we'll ACK it and it will go false.
+ */
+ if (!(hostdata->flags & FLAG_HAS_LAST_BYTE_SENT)) {
+ timeout = 20000;
+#if 1
+#if 1
+ while (!(NCR5380_read(BUS_AND_STATUS_REG) &
+ BASR_DRQ) && (NCR5380_read(BUS_AND_STATUS_REG) &
+ BASR_PHASE_MATCH));
+#else
+ if (NCR5380_read(STATUS_REG) & SR_REQ) {
+ for (; timeout &&
+ !(NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+ --timeout);
+ for (; timeout && (NCR5380_read(STATUS_REG) & SR_REQ);
+ --timeout);
+ }
+#endif
+
+
+#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
+ if (!timeout)
+ printk("scsi%d : timed out on last byte\n",
+ instance->host_no);
+#endif
+
+
+ if (hostdata->flags & FLAG_CHECK_LAST_BYTE_SENT) {
+ hostdata->flags &= ~FLAG_CHECK_LAST_BYTE_SENT;
+ if (NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT) {
+ hostdata->flags |= FLAG_HAS_LAST_BYTE_SENT;
+#if (NDEBUG & NDEBUG_LAST_BYTE_SENT)
+ printk("scsi%d : last bit sent works\n",
+ instance->host_no);
+#endif
+ }
+ }
+ } else {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("Waiting for LASTBYTE\n");
+#endif
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT));
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("Got LASTBYTE\n");
+#endif
+ }
+#else
+ udelay (5);
+#endif
+ }
+ }
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ if ((!(p & SR_IO)) && (hostdata->flags & FLAG_NCR53C400)) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: Checking for IRQ\n");
+#endif
+ if (NCR5380_read(BUS_AND_STATUS_REG) & BASR_IRQ) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got it, reading reset interupt reg\n");
+#endif
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ } else {
+ printk("53C400w: IRQ NOT THERE!\n");
+ }
+ }
+
+ *data = d + c;
+ *count = 0;
+ *phase = (NCR5380_read(STATUS_REG & PHASE_MASK));
+#if 0
+ NCR5380_print_phase(instance);
+#endif
+#if defined(PSEUDO_DMA) && !defined(UNSAFE)
+ sti();
+#endif /* defined(REAL_DMA_POLL) */
+ return foo;
+#endif /* def REAL_DMA */
+}
+#endif /* defined(REAL_DMA) | defined(PSEUDO_DMA) */
+
+/*
+ * Function : NCR5380_information_transfer (struct Scsi_Host *instance)
+ *
+ * Purpose : run through the various SCSI phases and do as the target
+ * directs us to. Operates on the currently connected command,
+ * instance->connected.
+ *
+ * Inputs : instance, instance for which we are doing commands
+ *
+ * Side effects : SCSI things happen, the disconnected queue will be
+ * modified if a command disconnects, *instance->connected will
+ * change.
+ *
+ * XXX Note : we need to watch for bus free or a reset condition here
+ * to recover from an unexpected bus free condition.
+ */
+
+static void NCR5380_information_transfer (struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned char msgout = NOP;
+ int sink = 0;
+ int len;
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ int transfersize;
+#endif
+ unsigned char *data;
+ unsigned char phase, tmp, extended_msg[10], old_phase=0xff;
+ Scsi_Cmnd *cmd = (Scsi_Cmnd *) hostdata->connected;
+ NCR5380_setup(instance);
+
+ while (1) {
+ tmp = NCR5380_read(STATUS_REG);
+ /* We only have a valid SCSI phase when REQ is asserted */
+ if (tmp & SR_REQ) {
+ phase = (tmp & PHASE_MASK);
+ if (phase != old_phase) {
+ old_phase = phase;
+#if (NDEBUG & NDEBUG_INFORMATION)
+ NCR5380_print_phase(instance);
+#endif
+ }
+
+ if (sink && (phase != PHASE_MSGOUT)) {
+ NCR5380_write(TARGET_COMMAND_REG, PHASE_SR_TO_TCR(tmp));
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_ATN |
+ ICR_ASSERT_ACK);
+ while (NCR5380_read(STATUS_REG) & SR_REQ);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 0;
+ continue;
+ }
+
+ switch (phase) {
+ case PHASE_DATAIN:
+ case PHASE_DATAOUT:
+#if (NDEBUG & NDEBUG_NO_DATAOUT)
+ printk("scsi%d : NDEBUG_NO_DATAOUT set, attempted DATAOUT aborted\n",
+ instance->host_no);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ return;
+#endif
+ /*
+ * If there is no room left in the current buffer in the
+ * scatter-gather list, move onto the next one.
+ */
+
+ if (!cmd->SCp.this_residual && cmd->SCp.buffers_residual) {
+ ++cmd->SCp.buffer;
+ --cmd->SCp.buffers_residual;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+#if (NDEBUG & NDEBUG_INFORMATION)
+ printk("scsi%d : %d bytes and %d buffers left\n",
+ instance->host_no, cmd->SCp.this_residual,
+ cmd->SCp.buffers_residual);
+#endif
+ }
+
+ /*
+ * The preferred transfer method is going to be
+ * PSEUDO-DMA for systems that are strictly PIO,
+ * since we can let the hardware do the handshaking.
+ *
+ * For this to work, we need to know the transfersize
+ * ahead of time, since the pseudo-DMA code will sit
+ * in an unconditional loop.
+ */
+
+#if defined(PSEUDO_DMA) || defined(REAL_DMA_POLL)
+ /* KLL
+ * PSEUDO_DMA is defined here. If this is the g_NCR5380
+ * driver then it will always be defined, so the
+ * FLAG_NO_PSEUDO_DMA is used to inhibit PDMA in the base
+ * NCR5380 case. I think this is a fairly clean solution.
+ * We supplement these 2 if's with the flag.
+ */
+#ifdef NCR5380_dma_xfer_len
+ if (!cmd->device->borken &&
+ !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
+ (transfersize = NCR5380_dma_xfer_len(instance, cmd)) != 0) {
+#else
+ transfersize = cmd->transfersize;
+
+#ifdef LIMIT_TRANSFERSIZE /* If we have problems with interrupt service */
+ if( transfersize > 512 )
+ transfersize = 512;
+#endif /* LIMIT_TRANSFERSIZE */
+
+ if (!cmd->device->borken && transfersize &&
+ !(hostdata->flags & FLAG_NO_PSEUDO_DMA) &&
+ cmd->SCp.this_residual && !(cmd->SCp.this_residual %
+ transfersize)) {
+#endif
+ len = transfersize;
+ if (NCR5380_transfer_dma(instance, &phase,
+ &len, (unsigned char **) &cmd->SCp.ptr)) {
+ /*
+ * If the watchdog timer fires, all future accesses to this
+ * device will use the polled-IO.
+ */
+ printk("scsi%d : switching target %d lun %d to slow handshake\n",
+ instance->host_no, cmd->target, cmd->lun);
+ cmd->device->borken = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ sink = 1;
+ do_abort(instance);
+ cmd->result = DID_ERROR << 16;
+ cmd->done(cmd);
+ /* XXX - need to source or sink data here, as appropriate */
+ } else
+ cmd->SCp.this_residual -= transfersize - len;
+ } else
+#endif /* defined(PSEUDO_DMA) || defined(REAL_DMA_POLL) */
+ NCR5380_transfer_pio(instance, &phase,
+ (int *) &cmd->SCp.this_residual, (unsigned char **)
+ &cmd->SCp.ptr);
+ break;
+ case PHASE_MSGIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Message = tmp;
+
+ switch (tmp) {
+ /*
+ * Linking lets us reduce the time required to get the
+ * next command out to the device, hopefully this will
+ * mean we don't waste another revolution due to the delays
+ * required by ARBITRATION and another SELECTION.
+ *
+ * In the current implementation proposal, low level drivers
+ * merely have to start the next command, pointed to by
+ * next_link, done() is called as with unlinked commands.
+ */
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#if (NDEBUG & NDEBUG_LINKED)
+ printk("scsi%d : target %d lun %d linked command complete.\n",
+ instance->host_no, cmd->target, cmd->lun);
+#endif
+ /*
+ * Sanity check : A linked command should only terminate with
+ * one of these messages if there are more linked commands
+ * available.
+ */
+
+ if (!cmd->next_link) {
+ printk("scsi%d : target %d lun %d linked command complete, no next_link\n"
+ instance->host_no, cmd->target, cmd->lun);
+ sink = 1;
+ do_abort (instance);
+ return;
+ }
+
+ initialize_SCp(cmd->next_link);
+ /* The next command is still part of this process */
+ cmd->next_link->tag = cmd->tag;
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+#if (NDEBUG & NDEBUG_LINKED)
+ printk("scsi%d : target %d lun %d linked request done, calling scsi_done().\n",
+ instance->host_no, cmd->target, cmd->lun);
+#endif
+ cmd->scsi_done(cmd);
+ cmd = hostdata->connected;
+ break;
+#endif /* def LINKED */
+ case ABORT:
+ case COMMAND_COMPLETE:
+ /* Accept message by clearing ACK */
+ sink = 1;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ hostdata->connected = NULL;
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : command for target %d, lun %d completed\n",
+ instance->host_no, cmd->target, cmd->lun);
+#endif
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+
+ /*
+ * I'm not sure what the correct thing to do here is :
+ *
+ * If the command that just executed is NOT a request
+ * sense, the obvious thing to do is to set the result
+ * code to the values of the stored parameters.
+ *
+ * If it was a REQUEST SENSE command, we need some way
+ * to differentiate between the failure code of the original
+ * and the failure code of the REQUEST sense - the obvious
+ * case is success, where we fall through and leave the result
+ * code unchanged.
+ *
+ * The non-obvious place is where the REQUEST SENSE failed
+ */
+
+ if (cmd->cmnd[0] != REQUEST_SENSE)
+ cmd->result = cmd->SCp.Status | (cmd->SCp.Message << 8);
+ else if (cmd->SCp.Status != GOOD)
+ cmd->result = (cmd->result & 0x00ffff) | (DID_ERROR << 16);
+
+#ifdef AUTOSENSE
+ if ((cmd->cmnd[0] != REQUEST_SENSE) &&
+ (cmd->SCp.Status == CHECK_CONDITION)) {
+#if (NDEBUG & NDEBUG_AUTOSENSE)
+ printk("scsi%d : performing request sense\n",
+ instance->host_no);
+#endif
+ cmd->cmnd[0] = REQUEST_SENSE;
+ cmd->cmnd[1] &= 0xe0;
+ cmd->cmnd[2] = 0;
+ cmd->cmnd[3] = 0;
+ cmd->cmnd[4] = sizeof(cmd->sense_buffer);
+ cmd->cmnd[5] = 0;
+
+ cmd->SCp.buffer = NULL;
+ cmd->SCp.buffers_residual = 0;
+ cmd->SCp.ptr = (char *) cmd->sense_buffer;
+ cmd->SCp.this_residual = sizeof(cmd->sense_buffer);
+
+ cli();
+ LIST(cmd,hostdata->issue_queue);
+ cmd->host_scribble = (unsigned char *)
+ hostdata->issue_queue;
+ hostdata->issue_queue = (Scsi_Cmnd *) cmd;
+ sti();
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : REQUEST SENSE added to head of issue queue\n",instance->host_no);
+#endif
+ } else
+#endif /* def AUTOSENSE */
+ cmd->scsi_done(cmd);
+
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+ return;
+ case MESSAGE_REJECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ switch (hostdata->last_message) {
+ case HEAD_OF_QUEUE_TAG:
+ case ORDERED_QUEUE_TAG:
+ case SIMPLE_QUEUE_TAG:
+ cmd->device->tagged_queue = 0;
+ hostdata->busy[cmd->target] |= (1 << cmd->lun);
+ break;
+ default:
+ break;
+ }
+ case DISCONNECT:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ cmd->device->disconnect = 1;
+ cli();
+ LIST(cmd,hostdata->disconnected_queue);
+ cmd->host_scribble = (unsigned char *)
+ hostdata->disconnected_queue;
+ hostdata->connected = NULL;
+ hostdata->disconnected_queue = cmd;
+ sti();
+#if (NDEBUG & NDEBUG_QUEUES)
+ printk("scsi%d : command for target %d lun %d was moved from connected to"
+ " the disconnected_queue\n", instance->host_no,
+ cmd->target, cmd->lun);
+#endif
+ /*
+ * Restore phase bits to 0 so an interrupted selection,
+ * arbitration can resume.
+ */
+ NCR5380_write(TARGET_COMMAND_REG, 0);
+
+ /* Enable reselect interrupts */
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ /* Wait for bus free to avoid nasty timeouts */
+ while ((NCR5380_read(STATUS_REG) & SR_BSY) && !hostdata->connected)
+ barrier();
+#if 0
+ NCR5380_print_status(instance);
+#endif
+ return;
+ /*
+ * The SCSI data pointer is *IMPLICITLY* saved on a disconnect
+ * operation, in violation of the SCSI spec so we can safely
+ * ignore SAVE/RESTORE pointers calls.
+ *
+ * Unfortunately, some disks violate the SCSI spec and
+ * don't issue the required SAVE_POINTERS message before
+ * disconnecting, and we have to break spec to remain
+ * compatible.
+ */
+ case SAVE_POINTERS:
+ case RESTORE_POINTERS:
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ break;
+ case EXTENDED_MESSAGE:
+/*
+ * Extended messages are sent in the following format :
+ * Byte
+ * 0 EXTENDED_MESSAGE == 1
+ * 1 length (includes one byte for code, doesn't
+ * include first two bytes)
+ * 2 code
+ * 3..length+1 arguments
+ *
+ * Start the extended message buffer with the EXTENDED_MESSAGE
+ * byte, since print_msg() wants the whole thing.
+ */
+ extended_msg[0] = EXTENDED_MESSAGE;
+ /* Accept first byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+ printk("scsi%d : receiving extended message\n",
+ instance->host_no);
+#endif
+
+ len = 2;
+ data = extended_msg + 1;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+ printk("scsi%d : length=%d, code=0x%02x\n",
+ instance->host_no, (int) extended_msg[1],
+ (int) extended_msg[2]);
+#endif
+
+ if (!len && extended_msg[1] <=
+ (sizeof (extended_msg) - 1)) {
+ /* Accept third byte by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ len = extended_msg[1] - 1;
+ data = extended_msg + 3;
+ phase = PHASE_MSGIN;
+
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+#if (NDEBUG & NDEBUG_EXTENDED)
+ printk("scsi%d : message received, residual %d\n",
+ instance->host_no, len);
+#endif
+
+ switch (extended_msg[2]) {
+ case EXTENDED_SDTR:
+ case EXTENDED_WDTR:
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ tmp = 0;
+ }
+ } else if (len) {
+ printk("scsi%d: error receiving extended message\n",
+ instance->host_no);
+ tmp = 0;
+ } else {
+ printk("scsi%d: extended message code %02x length %d is too long\n",
+ instance->host_no, extended_msg[2], extended_msg[1]);
+ tmp = 0;
+ }
+ /* Fall through to reject message */
+
+ /*
+ * If we get something weird that we aren't expecting,
+ * reject it.
+ */
+ default:
+ if (!tmp) {
+ printk("scsi%d: rejecting message ", instance->host_no);
+ print_msg (extended_msg);
+ printk("\n");
+ } else if (tmp != EXTENDED_MESSAGE)
+ printk("scsi%d: rejecting unknown message %02x from target %d, lun %d\n",
+ instance->host_no, tmp, cmd->target, cmd->lun);
+ else
+ printk("scsi%d: rejecting unknown extended message code %02x, length %d from target %d, lun %d\n",
+ instance->host_no, extended_msg[1], extended_msg[0], cmd->target, cmd->lun);
+
+ msgout = MESSAGE_REJECT;
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE |
+ ICR_ASSERT_ATN);
+ break;
+ } /* switch (tmp) */
+ break;
+ case PHASE_MSGOUT:
+ len = 1;
+ data = &msgout;
+ hostdata->last_message = msgout;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ if (msgout == ABORT) {
+ hostdata->busy[cmd->target] &= ~(1 << cmd->lun);
+ hostdata->connected = NULL;
+ cmd->result = DID_ERROR << 16;
+ cmd->scsi_done(cmd);
+ NCR5380_write(SELECT_ENABLE_REG, hostdata->id_mask);
+ return;
+ }
+ msgout = NOP;
+ break;
+ case PHASE_CMDOUT:
+ len = cmd->cmd_len;
+ data = cmd->cmnd;
+ /*
+ * XXX for performance reasons, on machines with a
+ * PSEUDO-DMA architecture we should probably
+ * use the dma transfer function.
+ */
+ NCR5380_transfer_pio(instance, &phase, &len,
+ &data);
+#ifdef USLEEP
+ if (!disconnect && should_disconnect(cmd->cmnd[0])) {
+ hostdata->time_expires = jiffies + USLEEP_SLEEP;
+#if (NDEBUG & NDEBUG_USLEEP)
+ printk("scsi%d : issued command, sleeping until %ul\n", instance->host_no,
+ hostdata->time_expires);
+#endif
+ NCR5380_set_timer (instance);
+ return;
+ }
+#endif /* def USLEEP */
+ break;
+ case PHASE_STATIN:
+ len = 1;
+ data = &tmp;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+ cmd->SCp.Status = tmp;
+ break;
+ default:
+ printk("scsi%d : unknown phase\n", instance->host_no);
+#ifdef NDEBUG
+ NCR5380_print(instance);
+#endif
+ } /* switch(phase) */
+ } /* if (tmp * SR_REQ) */
+#ifdef USLEEP
+ else {
+ if (!disconnect && hostdata->time_expires && jiffies >
+ hostdata->time_expires) {
+ hostdata->time_expires = jiffies + USLEEP_SLEEP;
+#if (NDEBUG & NDEBUG_USLEEP)
+ printk("scsi%d : poll timed out, sleeping until %ul\n", instance->host_no,
+ hostdata->time_expires);
+#endif
+ NCR5380_set_timer (instance);
+ return;
+ }
+ }
+#endif
+ } /* while (1) */
+}
+
+/*
+ * Function : void NCR5380_reselect (struct Scsi_Host *instance)
+ *
+ * Purpose : does reselection, initializing the instance->connected
+ * field to point to the Scsi_Cmnd for which the I_T_L or I_T_L_Q
+ * nexus has been reestablished,
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ */
+
+
+static void NCR5380_reselect (struct Scsi_Host *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ unsigned char target_mask;
+ unsigned char lun, phase;
+ int len;
+#ifdef SCSI2
+ unsigned char tag;
+#endif
+ unsigned char msg[3];
+ unsigned char *data;
+ Scsi_Cmnd *tmp = NULL, *prev;
+ int abort = 0;
+ NCR5380_setup(instance);
+
+ /*
+ * Disable arbitration, etc. since the host adapter obviously
+ * lost, and tell an interrupted NCR5380_select() to restart.
+ */
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ hostdata->restart_select = 1;
+
+ target_mask = NCR5380_read(CURRENT_SCSI_DATA_REG) & ~(hostdata->id_mask);
+
+#if (NDEBUG & NDEBUG_RESELECTION)
+ printk("scsi%d : reselect\n", instance->host_no);
+#endif
+
+ /*
+ * At this point, we have detected that our SCSI ID is on the bus,
+ * SEL is true and BSY was false for at least one bus settle delay
+ * (400 ns).
+ *
+ * We must assert BSY ourselves, until the target drops the SEL
+ * signal.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE | ICR_ASSERT_BSY);
+
+ while (NCR5380_read(STATUS_REG) & SR_SEL);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * Wait for target to go into MSGIN.
+ */
+
+ while (!(NCR5380_read(STATUS_REG) & SR_REQ));
+
+ len = 1;
+ data = msg;
+ phase = PHASE_MSGIN;
+ NCR5380_transfer_pio(instance, &phase, &len, &data);
+
+
+ if (!msg[0] & 0x80) {
+ printk("scsi%d : expecting IDENTIFY message, got ",
+ instance->host_no);
+ print_msg(msg);
+ abort = 1;
+ } else {
+ /* Accept message by clearing ACK */
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+ lun = (msg[0] & 0x07);
+
+ /*
+ * We need to add code for SCSI-II to track which devices have
+ * I_T_L_Q nexuses established, and which have simple I_T_L
+ * nexuses so we can chose to do additional data transfer.
+ */
+
+#ifdef SCSI2
+#error "SCSI-II tagged queueing is not supported yet"
+#endif
+
+ /*
+ * Find the command corresponding to the I_T_L or I_T_L_Q nexus we
+ * just reestablished, and remove it from the disconnected queue.
+ */
+
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue, prev = NULL;
+ tmp; prev = tmp, tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if ((target_mask == (1 << tmp->target)) && (lun == tmp->lun)
+#ifdef SCSI2
+ && (tag == tmp->tag)
+#endif
+) {
+ if (prev) {
+ REMOVE(prev,prev->host_scribble,tmp,tmp->host_scribble);
+ prev->host_scribble = tmp->host_scribble;
+ } else {
+ REMOVE(-1,hostdata->disconnected_queue,tmp,tmp->host_scribble);
+ hostdata->disconnected_queue = (Scsi_Cmnd *) tmp->host_scribble;
+ }
+ tmp->host_scribble = NULL;
+ break;
+ }
+
+ if (!tmp) {
+#ifdef SCSI2
+ printk("scsi%d : warning : target bitmask %02x lun %d tag %d not in disconnect_queue.\n",
+ instance->host_no, target_mask, lun, tag);
+#else
+ printk("scsi%d : warning : target bitmask %02x lun %d not in disconnect_queue.\n",
+ instance->host_no, target_mask, lun);
+#endif
+ /*
+ * Since we have an established nexus that we can't do anything with,
+ * we must abort it.
+ */
+ abort = 1;
+ }
+ }
+
+ if (abort) {
+ do_abort (instance);
+ } else {
+ hostdata->connected = tmp;
+#if (NDEBUG & NDEBUG_RESELECTION)
+ printk("scsi%d : nexus established, target = %d, lun = %d, tag = %d\n",
+ instance->host_no, tmp->target, tmp->lun, tmp->tag);
+#endif
+ }
+}
+
+/*
+ * Function : void NCR5380_dma_complete (struct Scsi_Host *instance)
+ *
+ * Purpose : called by interrupt handler when DMA finishes or a phase
+ * mismatch occurs (which would finish the DMA transfer).
+ *
+ * Inputs : instance - this instance of the NCR5380.
+ *
+ * Returns : pointer to the Scsi_Cmnd structure for which the I_T_L
+ * nexus has been reestablished, on failure NULL is returned.
+ */
+
+#ifdef REAL_DMA
+static void NCR5380_dma_complete (NCR5380_instance *instance) {
+ NCR5380_local_declare();
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *
+ instance->hostdata);
+ int transferred;
+ NCR5380_setup(instance);
+
+ /*
+ * XXX this might not be right.
+ *
+ * Wait for final byte to transfer, ie wait for ACK to go false.
+ *
+ * We should use the Last Byte Sent bit, unfortunately this is
+ * not available on the 5380/5381 (only the various CMOS chips)
+ */
+
+ while (NCR5380_read(BUS_AND_STATUS_REG) & BASR_ACK);
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_BASE);
+
+ /*
+ * The only places we should see a phase mismatch and have to send
+ * data from the same set of pointers will be the data transfer
+ * phases. So, residual, requested length are only important here.
+ */
+
+ if (!(hostdata->connected->SCp.phase & SR_CD)) {
+ transferred = instance->dmalen - NCR5380_dma_residual();
+ hostdata->connected->SCp.this_residual -= transferred;
+ hostdata->connected->SCp.ptr += transferred;
+ }
+}
+#endif /* def REAL_DMA */
+
+/*
+ * Function : int NCR5380_abort (Scsi_Cmnd *cmd)
+ *
+ * Purpose : abort a command
+ *
+ * Inputs : cmd - the Scsi_Cmnd to abort, code - code to set the
+ * host byte of the result field to, if zero DID_ABORTED is
+ * used.
+ *
+ * Returns : 0 - success, -1 on failure.
+ *
+ * XXX - there is no way to abort the command that is currently
+ * connected, you have to wait for it to complete. If this is
+ * a problem, we could implement longjmp() / setjmp(), setjmp()
+ * called where the loop started in NCR5380_main().
+ */
+
+#ifndef NCR5380_abort
+static
+#endif
+int NCR5380_abort (Scsi_Cmnd *cmd) {
+ NCR5380_local_declare();
+ struct Scsi_Host *instance = cmd->host;
+ struct NCR5380_hostdata *hostdata = (struct NCR5380_hostdata *)
+ instance->hostdata;
+ Scsi_Cmnd *tmp, **prev;
+
+ printk("scsi%d : aborting command\n", instance->host_no);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ printk("scsi%d : aborting command\n", instance->host_no);
+ print_Scsi_Cmnd (cmd);
+
+ NCR5380_print_status (instance);
+
+ cli();
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : abort called\n", instance->host_no);
+ printk(" basr 0x%X, sr 0x%X\n",
+ NCR5380_read(BUS_AND_STATUS_REG), NCR5380_read(STATUS_REG));
+#endif
+
+#if 0
+/*
+ * Case 1 : If the command is the currently executing command,
+ * we'll set the aborted flag and return control so that
+ * information transfer routine can exit cleanly.
+ */
+
+ if (hostdata->connected == cmd) {
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : aborting connected command\n", instance->host_no);
+#endif
+ hostdata->aborted = 1;
+/*
+ * We should perform BSY checking, and make sure we haven't slipped
+ * into BUS FREE.
+ */
+
+ NCR5380_write(INITIATOR_COMMAND_REG, ICR_ASSERT_ATN);
+/*
+ * Since we can't change phases until we've completed the current
+ * handshake, we have to source or sink a byte of data if the current
+ * phase is not MSGOUT.
+ */
+
+/*
+ * Return control to the executing NCR drive so we can clear the
+ * aborted flag and get back into our main loop.
+ */
+
+ return 0;
+ }
+#endif
+
+/*
+ * Case 2 : If the command hasn't been issued yet, we simply remove it
+ * from the issue queue.
+ */
+#if (NDEBUG & NDEBUG_ABORT)
+ /* KLL */
+ printk("scsi%d : abort going into loop.\n", instance->host_no);
+#endif
+ for (prev = (Scsi_Cmnd **) &(hostdata->issue_queue),
+ tmp = (Scsi_Cmnd *) hostdata->issue_queue;
+ tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
+ (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ REMOVE(5,*prev,tmp,tmp->host_scribble);
+ (*prev) = (Scsi_Cmnd *) tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ sti();
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : abort removed command from issue queue.\n",
+ instance->host_no);
+#endif
+ tmp->done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+#if (NDEBUG & NDEBUG_ABORT)
+ /* KLL */
+ else if (prev == tmp) printk("scsi%d : LOOP\n", instance->host_no);
+#endif
+
+/*
+ * Case 3 : If any commands are connected, we're going to fail the abort
+ * and let the high level SCSI driver retry at a later time or
+ * issue a reset.
+ *
+ * Timeouts, and therefore aborted commands, will be highly unlikely
+ * and handling them cleanly in this situation would make the common
+ * case of noresets less efficient, and would pollute our code. So,
+ * we fail.
+ */
+
+ if (hostdata->connected) {
+ sti();
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : abort failed, command connected.\n", instance->host_no);
+#endif
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+/*
+ * Case 4: If the command is currently disconnected from the bus, and
+ * there are no connected commands, we reconnect the I_T_L or
+ * I_T_L_Q nexus associated with it, go into message out, and send
+ * an abort message.
+ *
+ * This case is especially ugly. In order to reestablish the nexus, we
+ * need to call NCR5380_select(). The easiest way to implement this
+ * function was to abort if the bus was busy, and let the interrupt
+ * handler triggered on the SEL for reselect take care of lost arbitrations
+ * where necessary, meaning interrupts need to be enabled.
+ *
+ * When interrupts are enabled, the queues may change - so we
+ * can't remove it from the disconnected queue before selecting it
+ * because that could cause a failure in hashing the nexus if that
+ * device reselected.
+ *
+ * Since the queues may change, we can't use the pointers from when we
+ * first locate it.
+ *
+ * So, we must first locate the command, and if NCR5380_select()
+ * succeeds, then issue the abort, relocate the command and remove
+ * it from the disconnected queue.
+ */
+
+ for (tmp = (Scsi_Cmnd *) hostdata->disconnected_queue; tmp;
+ tmp = (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ sti();
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : aborting disconnected command.\n", instance->host_no);
+#endif
+
+ if (NCR5380_select (instance, cmd, (int) cmd->tag))
+ return SCSI_ABORT_BUSY;
+
+#if (NDEBUG & NDEBUG_ABORT)
+ printk("scsi%d : nexus reestablished.\n", instance->host_no);
+#endif
+
+ do_abort (instance);
+
+ cli();
+ for (prev = (Scsi_Cmnd **) &(hostdata->disconnected_queue),
+ tmp = (Scsi_Cmnd *) hostdata->disconnected_queue;
+ tmp; prev = (Scsi_Cmnd **) &(tmp->host_scribble), tmp =
+ (Scsi_Cmnd *) tmp->host_scribble)
+ if (cmd == tmp) {
+ REMOVE(5,*prev,tmp,tmp->host_scribble);
+ *prev = (Scsi_Cmnd *) tmp->host_scribble;
+ tmp->host_scribble = NULL;
+ tmp->result = DID_ABORT << 16;
+ sti();
+ tmp->done(tmp);
+ return SCSI_ABORT_SUCCESS;
+ }
+ }
+
+/*
+ * Case 5 : If we reached this point, the command was not found in any of
+ * the queues.
+ *
+ * We probably reached this point because of an unlikely race condition
+ * between the command completing successfully and the abortion code,
+ * so we won't panic, but we will notify the user in case something really
+ * broke.
+ */
+
+ sti();
+ printk("scsi%d : warning : SCSI command probably completed successfully\n"
+ " before abortion\n", instance->host_no);
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+
+/*
+ * Function : int NCR5380_reset (Scsi_Cmnd *cmd)
+ *
+ * Purpose : reset the SCSI bus.
+ *
+ * Returns : SCSI_RESET_WAKEUP
+ *
+ */
+
+#ifndef NCR5380_reset
+static
+#endif
+int NCR5380_reset (Scsi_Cmnd *cmd) {
+ NCR5380_local_declare();
+ NCR5380_setup(cmd->host);
+
+ NCR5380_print_status (cmd->host);
+ do_reset (cmd->host);
+
+ return SCSI_RESET_WAKEUP;
+}
+
diff --git a/i386/i386at/gpl/linux/scsi/NCR53c406a.c b/i386/i386at/gpl/linux/scsi/NCR53c406a.c
new file mode 100644
index 00000000..3105b1bb
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/NCR53c406a.c
@@ -0,0 +1,1079 @@
+/*
+ * NCR53c406.c
+ * Low-level SCSI driver for NCR53c406a chip.
+ * Copyright (C) 1994, 1995, 1996 Normunds Saumanis (normunds@fi.ibm.com)
+ *
+ * LILO command line usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]
+ * Specify IRQ = 0 for non-interrupt driven mode.
+ * FASTPIO = 1 for fast pio mode, 0 for slow mode.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#define NCR53C406A_DEBUG 0
+#define VERBOSE_NCR53C406A_DEBUG 0
+
+/* Set this to 1 for PIO mode (recommended) or to 0 for DMA mode */
+#define USE_PIO 1
+
+#define USE_BIOS 0
+/* #define BIOS_ADDR 0xD8000 */ /* define this if autoprobe fails */
+/* #define PORT_BASE 0x330 */ /* define this if autoprobe fails */
+/* #define IRQ_LEV 0 */ /* define this if autoprobe fails */
+#define DMA_CHAN 5 /* this is ignored if DMA is disabled */
+
+/* Set this to 0 if you encounter kernel lockups while transferring
+ * data in PIO mode */
+#define USE_FAST_PIO 1
+
+/* ============= End of user configurable parameters ============= */
+
+#include <linux/module.h>
+
+#include <linux/errno.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/interrupt.h>
+#include <linux/proc_fs.h>
+#include <linux/stat.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <asm/bitops.h>
+#include <asm/irq.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "NCR53c406a.h"
+
+/* ============================================================= */
+
+#define WATCHDOG 5000000
+
+#define SYNC_MODE 0 /* Synchrounous transfer mode */
+
+#if DEBUG
+#undef NCR53C406A_DEBUG
+#define NCR53C406A_DEBUG 1
+#endif
+
+#if USE_PIO
+#define USE_DMA 0
+#else
+#define USE_DMA 1
+#endif
+
+/* Default configuration */
+#define C1_IMG 0x07 /* ID=7 */
+#define C2_IMG 0x48 /* FE SCSI2 */
+#if USE_DMA
+#define C3_IMG 0x21 /* CDB TE */
+#else
+#define C3_IMG 0x20 /* CDB */
+#endif
+#define C4_IMG 0x04 /* ANE */
+#define C5_IMG 0xb6 /* AA PI SIE POL */
+
+#define REG0 (outb(C4_IMG, CONFIG4))
+#define REG1 (outb(C5_IMG, CONFIG5))
+
+#if NCR53C406A_DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#if VERBOSE_NCR53C406A_DEBUG
+#define VDEB(x) x
+#else
+#define VDEB(x)
+#endif
+
+#define LOAD_DMA_COUNT(count) \
+ outb(count & 0xff, TC_LSB); \
+ outb((count >> 8) & 0xff, TC_MSB); \
+ outb((count >> 16) & 0xff, TC_HIGH);
+
+/* Chip commands */
+#define DMA_OP 0x80
+
+#define SCSI_NOP 0x00
+#define FLUSH_FIFO 0x01
+#define CHIP_RESET 0x02
+#define SCSI_RESET 0x03
+#define RESELECT 0x40
+#define SELECT_NO_ATN 0x41
+#define SELECT_ATN 0x42
+#define SELECT_ATN_STOP 0x43
+#define ENABLE_SEL 0x44
+#define DISABLE_SEL 0x45
+#define SELECT_ATN3 0x46
+#define RESELECT3 0x47
+#define TRANSFER_INFO 0x10
+#define INIT_CMD_COMPLETE 0x11
+#define MSG_ACCEPT 0x12
+#define TRANSFER_PAD 0x18
+#define SET_ATN 0x1a
+#define RESET_ATN 0x1b
+#define SEND_MSG 0x20
+#define SEND_STATUS 0x21
+#define SEND_DATA 0x22
+#define DISCONN_SEQ 0x23
+#define TERMINATE_SEQ 0x24
+#define TARG_CMD_COMPLETE 0x25
+#define DISCONN 0x27
+#define RECV_MSG 0x28
+#define RECV_CMD 0x29
+#define RECV_DATA 0x2a
+#define RECV_CMD_SEQ 0x2b
+#define TARGET_ABORT_DMA 0x04
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at */
+/* 1 = blue
+ 2 = green
+ 3 = cyan
+ 4 = red
+ 5 = magenta
+ 6 = yellow
+ 7 = white
+*/
+
+#if NCR53C406A_DEBUG
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+/*----------------------------------------------------------------*/
+
+enum Phase {
+ idle,
+ data_out,
+ data_in,
+ command_ph,
+ status_ph,
+ message_out,
+ message_in
+};
+
+/* Static function prototypes */
+static void NCR53c406a_intr(int, struct pt_regs *);
+static void internal_done(Scsi_Cmnd *);
+static void wait_intr(void);
+static void chip_init(void);
+static void calc_port_addr(void);
+#ifndef IRQ_LEV
+static int irq_probe(void);
+#endif
+
+/* ================================================================= */
+
+#if USE_BIOS
+static void *bios_base = (void *)0;
+#endif
+
+#if PORT_BASE
+static int port_base = PORT_BASE;
+#else
+static int port_base = 0;
+#endif
+
+#if IRQ_LEV
+static int irq_level = IRQ_LEV;
+#else
+static int irq_level = -1; /* 0 is 'no irq', so use -1 for 'uninitialized'*/
+#endif
+
+#if USE_DMA
+static int dma_chan = 0;
+#endif
+
+#if USE_PIO
+static int fast_pio = USE_FAST_PIO;
+#endif
+
+static Scsi_Cmnd *current_SC = NULL;
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+static char info_msg[256];
+
+struct proc_dir_entry proc_scsi_NCR53c406a = {
+ PROC_SCSI_NCR53C406A, 7, "NCR53c406a",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+/* ================================================================= */
+
+/* possible BIOS locations */
+#if USE_BIOS
+static void *addresses[] = {
+ (void *)0xd8000,
+ (void *)0xc8000
+};
+#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
+#endif USE_BIOS
+
+/* possible i/o port addresses */
+static unsigned short ports[] = { 0x230, 0x330 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+/* possible interrupt channels */
+static unsigned short intrs[] = { 10, 11, 12, 15 };
+#define INTR_COUNT (sizeof( intrs ) / sizeof( unsigned short ))
+
+/* signatures for NCR 53c406a based controllers */
+#if USE_BIOS
+struct signature {
+ char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] = {
+ /* 1 2 3 4 5 6 */
+ /* 123456789012345678901234567890123456789012345678901234567890 */
+ { "Copyright (C) Acculogic, Inc.\r\n2.8M Diskette Extension Bios ver 4.04.03 03/01/1993", 61, 82 },
+};
+#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+#endif USE_BIOS
+
+/* ============================================================ */
+
+/* Control Register Set 0 */
+static int TC_LSB; /* transfer counter lsb */
+static int TC_MSB; /* transfer counter msb */
+static int SCSI_FIFO; /* scsi fifo register */
+static int CMD_REG; /* command register */
+static int STAT_REG; /* status register */
+static int DEST_ID; /* selection/reselection bus id */
+static int INT_REG; /* interrupt status register */
+static int SRTIMOUT; /* select/reselect timeout reg */
+static int SEQ_REG; /* sequence step register */
+static int SYNCPRD; /* synchronous transfer period */
+static int FIFO_FLAGS; /* indicates # of bytes in fifo */
+static int SYNCOFF; /* synchronous offset register */
+static int CONFIG1; /* configuration register */
+static int CLKCONV; /* clock conversion reg */
+/*static int TESTREG;*/ /* test mode register */
+static int CONFIG2; /* Configuration 2 Register */
+static int CONFIG3; /* Configuration 3 Register */
+static int CONFIG4; /* Configuration 4 Register */
+static int TC_HIGH; /* Transfer Counter High */
+/*static int FIFO_BOTTOM;*/ /* Reserve FIFO byte register */
+
+/* Control Register Set 1 */
+/*static int JUMPER_SENSE;*/ /* Jumper sense port reg (r/w) */
+/*static int SRAM_PTR;*/ /* SRAM address pointer reg (r/w) */
+/*static int SRAM_DATA;*/ /* SRAM data register (r/w) */
+static int PIO_FIFO; /* PIO FIFO registers (r/w) */
+/*static int PIO_FIFO1;*/ /* */
+/*static int PIO_FIFO2;*/ /* */
+/*static int PIO_FIFO3;*/ /* */
+static int PIO_STATUS; /* PIO status (r/w) */
+/*static int ATA_CMD;*/ /* ATA command/status reg (r/w) */
+/*static int ATA_ERR;*/ /* ATA features/error register (r/w)*/
+static int PIO_FLAG; /* PIO flag interrupt enable (r/w) */
+static int CONFIG5; /* Configuration 5 register (r/w) */
+/*static int SIGNATURE;*/ /* Signature Register (r) */
+/*static int CONFIG6;*/ /* Configuration 6 register (r) */
+
+/* ============================================================== */
+
+#if USE_DMA
+static __inline__ int
+NCR53c406a_dma_setup (unsigned char *ptr,
+ unsigned int count,
+ unsigned char mode) {
+ unsigned limit;
+ unsigned long flags = 0;
+
+ VDEB(printk("dma: before count=%d ", count));
+ if (dma_chan <=3) {
+ if (count > 65536)
+ count = 65536;
+ limit = 65536 - (((unsigned) ptr) & 0xFFFF);
+ } else {
+ if (count > (65536<<1))
+ count = (65536<<1);
+ limit = (65536<<1) - (((unsigned) ptr) & 0x1FFFF);
+ }
+
+ if (count > limit) count = limit;
+
+ VDEB(printk("after count=%d\n", count));
+ if ((count & 1) || (((unsigned) ptr) & 1))
+ panic ("NCR53c406a: attempted unaligned DMA transfer\n");
+
+ save_flags(flags);
+ cli();
+ disable_dma(dma_chan);
+ clear_dma_ff(dma_chan);
+ set_dma_addr(dma_chan, (long) ptr);
+ set_dma_count(dma_chan, count);
+ set_dma_mode(dma_chan, mode);
+ enable_dma(dma_chan);
+ restore_flags(flags);
+
+ return count;
+}
+
+static __inline__ int
+NCR53c406a_dma_write(unsigned char *src, unsigned int count) {
+ return NCR53c406a_dma_setup (src, count, DMA_MODE_WRITE);
+}
+
+static __inline__ int
+NCR53c406a_dma_read(unsigned char *src, unsigned int count) {
+ return NCR53c406a_dma_setup (src, count, DMA_MODE_READ);
+}
+
+static __inline__ int
+NCR53c406a_dma_residual (void) {
+ register int tmp;
+ unsigned long flags = 0;
+ save_flags(flags);
+ cli();
+ clear_dma_ff(dma_chan);
+ tmp = get_dma_residue(dma_chan);
+ restore_flags(flags);
+
+ return tmp;
+}
+#endif USE_DMA
+
+#if USE_PIO
+static __inline__ int NCR53c406a_pio_read(unsigned char *request,
+ unsigned int reqlen)
+{
+ int i;
+ int len; /* current scsi fifo size */
+ unsigned long flags = 0;
+
+ REG1;
+ while (reqlen) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80)
+ return 0;
+
+ switch( i & 0x1e ) {
+ default:
+ case 0x10:
+ len=0; break;
+ case 0x0:
+ len=1; break;
+ case 0x8:
+ len=42; break;
+ case 0xc:
+ len=84; break;
+ case 0xe:
+ len=128; break;
+ }
+
+ if ((i & 0x40) && len == 0) { /* fifo empty and interrupt occured */
+ return 0;
+ }
+
+ if (len) {
+ if( len > reqlen )
+ len = reqlen;
+
+ save_flags(flags);
+ cli();
+ if( fast_pio && len > 3 ) {
+ insl(PIO_FIFO,request,len>>2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ }
+ else {
+ while(len--) {
+ *request++ = inb(PIO_FIFO);
+ reqlen--;
+ }
+ }
+ restore_flags(flags);
+ }
+ }
+ return 0;
+}
+
+static __inline__ int NCR53c406a_pio_write(unsigned char *request,
+ unsigned int reqlen)
+{
+ int i = 0;
+ int len; /* current scsi fifo size */
+ unsigned long flags = 0;
+
+ REG1;
+ while (reqlen && !(i&0x40)) {
+ i = inb(PIO_STATUS);
+ /* VDEB(printk("pio_status=%x\n", i)); */
+ if (i & 0x80) /* error */
+ return 0;
+
+ switch( i & 0x1e ) {
+ case 0x10:
+ len=128; break;
+ case 0x0:
+ len=84; break;
+ case 0x8:
+ len=42; break;
+ case 0xc:
+ len=1; break;
+ default:
+ case 0xe:
+ len=0; break;
+ }
+
+ if (len) {
+ if( len > reqlen )
+ len = reqlen;
+
+ save_flags(flags);
+ cli();
+ if( fast_pio && len > 3 ) {
+ outsl(PIO_FIFO,request,len>>2);
+ request += len & 0xfc;
+ reqlen -= len & 0xfc;
+ }
+ else {
+ while(len--) {
+ outb(*request++, PIO_FIFO);
+ reqlen--;
+ }
+ }
+ restore_flags(flags);
+ }
+ }
+ return 0;
+}
+#endif USE_PIO
+
+int
+NCR53c406a_detect(Scsi_Host_Template * tpnt){
+ struct Scsi_Host *shpnt;
+#ifndef PORT_BASE
+ int i;
+#endif
+
+#if USE_BIOS
+ int ii, jj;
+ bios_base = 0;
+ /* look for a valid signature */
+ for( ii=0; ii < ADDRESS_COUNT && !bios_base; ii++)
+ for( jj=0; (jj < SIGNATURE_COUNT) && !bios_base; jj++)
+ if(!memcmp((void *) addresses[ii]+signatures[jj].sig_offset,
+ (void *) signatures[jj].signature,
+ (int) signatures[jj].sig_length))
+ bios_base=addresses[ii];
+
+ if(!bios_base){
+ printk("NCR53c406a: BIOS signature not found\n");
+ return 0;
+ }
+
+ DEB(printk("NCR53c406a BIOS found at %X\n", (unsigned int) bios_base););
+#endif USE_BIOS
+
+#ifdef PORT_BASE
+ if (check_region(port_base, 0x10)) /* ports already snatched */
+ port_base = 0;
+
+#else /* autodetect */
+ if (port_base) { /* LILO override */
+ if (check_region(port_base, 0x10))
+ port_base = 0;
+ }
+ else {
+ for(i=0; i<PORT_COUNT && !port_base; i++){
+ if(check_region(ports[i], 0x10)){
+ DEB(printk("NCR53c406a: port %x in use\n", ports[i]));
+ }
+ else {
+ VDEB(printk("NCR53c406a: port %x available\n", ports[i]));
+ outb(C5_IMG, ports[i] + 0x0d); /* reg set 1 */
+ if( (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7
+ && (inb(ports[i] + 0x0e) ^ inb(ports[i] + 0x0e)) == 7
+ && (inb(ports[i] + 0x0e) & 0xf8) == 0x58 ) {
+ VDEB(printk("NCR53c406a: Sig register valid\n"));
+ VDEB(printk("port_base=%x\n", port_base));
+ port_base = ports[i];
+ }
+ }
+ }
+ }
+#endif PORT_BASE
+
+ if(!port_base){ /* no ports found */
+ printk("NCR53c406a: no available ports found\n");
+ return 0;
+ }
+
+ DEB(printk("NCR53c406a detected\n"));
+
+ calc_port_addr();
+ chip_init();
+
+#ifndef IRQ_LEV
+ if (irq_level < 0) { /* LILO override if >= 0*/
+ irq_level=irq_probe();
+ if (irq_level < 0) { /* Trouble */
+ printk("NCR53c406a: IRQ problem, irq_level=%d, giving up\n", irq_level);
+ return 0;
+ }
+ }
+#endif
+
+ DEB(printk("NCR53c406a: using port_base %x\n", port_base));
+ request_region(port_base, 0x10, "NCR53c406a");
+
+ if(irq_level > 0) {
+ if(request_irq(irq_level, NCR53c406a_intr, 0, "NCR53c406a")){
+ printk("NCR53c406a: unable to allocate IRQ %d\n", irq_level);
+ return 0;
+ }
+ tpnt->can_queue = 1;
+ DEB(printk("NCR53c406a: allocated IRQ %d\n", irq_level));
+ }
+ else if (irq_level == 0) {
+ tpnt->can_queue = 0;
+ DEB(printk("NCR53c406a: No interrupts detected\n"));
+#if USE_DMA
+ printk("NCR53c406a: No interrupts found and DMA mode defined. Giving up.\n");
+ return 0;
+#endif USE_DMA
+ }
+ else {
+ DEB(printk("NCR53c406a: Shouldn't get here!\n"));
+ return 0;
+ }
+
+#if USE_DMA
+ dma_chan = DMA_CHAN;
+ if(request_dma(dma_chan, "NCR53c406a") != 0){
+ printk("NCR53c406a: unable to allocate DMA channel %d\n", dma_chan);
+ return 0;
+ }
+
+ DEB(printk("Allocated DMA channel %d\n", dma_chan));
+#endif USE_DMA
+
+ tpnt->present = 1;
+ tpnt->proc_dir = &proc_scsi_NCR53c406a;
+
+ shpnt = scsi_register(tpnt, 0);
+ shpnt->irq = irq_level;
+ shpnt->io_port = port_base;
+ shpnt->n_io_port = 0x10;
+#if USE_DMA
+ shpnt->dma = dma_chan;
+#endif
+
+#if USE_DMA
+ sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, DMA channel %d.",
+ port_base, irq_level, dma_chan);
+#else
+ sprintf(info_msg, "NCR53c406a at 0x%x, IRQ %d, %s PIO mode.",
+ port_base, irq_level, fast_pio ? "fast" : "slow");
+#endif
+
+ return (tpnt->present);
+}
+
+/* called from init/main.c */
+void NCR53c406a_setup(char *str, int *ints)
+{
+ static size_t setup_idx = 0;
+ size_t i;
+
+ DEB(printk("NCR53c406a: Setup called\n"););
+
+ if (setup_idx >= PORT_COUNT - 1) {
+ printk("NCR53c406a: Setup called too many times. Bad LILO params?\n");
+ return;
+ }
+ if (ints[0] < 1 || ints[0] > 3) {
+ printk("NCR53c406a: Malformed command line\n");
+ printk("NCR53c406a: Usage: ncr53c406a=<PORTBASE>[,<IRQ>[,<FASTPIO>]]\n");
+ return;
+ }
+ for (i = 0; i < PORT_COUNT && !port_base; i++)
+ if (ports[i] == ints[1]) {
+ port_base = ints[1];
+ DEB(printk("NCR53c406a: Specified port_base 0x%X\n", port_base);)
+ }
+ if (!port_base) {
+ printk("NCR53c406a: Invalid PORTBASE 0x%X specified\n", ints[1]);
+ return;
+ }
+
+ if (ints[0] > 1) {
+ if (ints[2] == 0) {
+ irq_level = 0;
+ DEB(printk("NCR53c406a: Specified irq %d\n", irq_level);)
+ }
+ else
+ for (i = 0; i < INTR_COUNT && irq_level < 0; i++)
+ if (intrs[i] == ints[2]) {
+ irq_level = ints[2];
+ DEB(printk("NCR53c406a: Specified irq %d\n", port_base);)
+ }
+ if (irq_level < 0)
+ printk("NCR53c406a: Invalid IRQ %d specified\n", ints[2]);
+ }
+
+ if (ints[0] > 2)
+ fast_pio = ints[3];
+
+ DEB(printk("NCR53c406a: port_base=0x%X, irq=%d, fast_pio=%d\n",
+ port_base, irq_level, fast_pio);)
+}
+
+const char*
+NCR53c406a_info(struct Scsi_Host *SChost){
+ DEB(printk("NCR53c406a_info called\n"));
+ return (info_msg);
+}
+
+static void internal_done(Scsi_Cmnd *SCpnt) {
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+
+static void wait_intr() {
+ int i = jiffies + WATCHDOG;
+
+ while(i>jiffies && !(inb(STAT_REG)&0xe0)) /* wait for a pseudo-interrupt */
+ barrier();
+
+ if (i <= jiffies) { /* Timed out */
+ rtrc(0);
+ current_SC->result = DID_TIME_OUT << 16;
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ NCR53c406a_intr(0, NULL);
+}
+
+int NCR53c406a_command(Scsi_Cmnd *SCpnt){
+ DEB(printk("NCR53c406a_command called\n"));
+ NCR53c406a_queue(SCpnt, internal_done);
+ if(irq_level)
+ while (!internal_done_flag);
+ else /* interrupts not supported */
+ while (!internal_done_flag)
+ wait_intr();
+
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+
+int
+NCR53c406a_queue(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)){
+ int i;
+ unsigned long flags = 0;
+
+ VDEB(printk("NCR53c406a_queue called\n"));
+ DEB(printk("cmd=%02x, cmd_len=%02x, target=%02x, lun=%02x, bufflen=%d\n",
+ SCpnt->cmnd[0],
+ SCpnt->cmd_len,
+ SCpnt->target,
+ SCpnt->lun,
+ SCpnt->request_bufflen));
+
+#if 0
+ VDEB(for(i=0; i<SCpnt->cmd_len; i++)
+ printk("cmd[%d]=%02x ", i, SCpnt->cmnd[i]));
+ VDEB(printk("\n"));
+#endif
+
+ current_SC = SCpnt;
+ current_SC->scsi_done = done;
+ current_SC->SCp.phase = command_ph;
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+
+ save_flags(flags);
+ cli();
+ REG0;
+ outb(SCpnt->target, DEST_ID); /* set destination */
+ outb(FLUSH_FIFO, CMD_REG); /* reset the fifos */
+
+ for(i=0; i<SCpnt->cmd_len; i++){
+ outb(SCpnt->cmnd[i], SCSI_FIFO);
+ }
+ outb(SELECT_NO_ATN, CMD_REG);
+ restore_flags(flags);
+
+ rtrc(1);
+ return 0;
+}
+
+int
+NCR53c406a_abort(Scsi_Cmnd *SCpnt){
+ DEB(printk("NCR53c406a_abort called\n"));
+ return SCSI_ABORT_SNOOZE; /* Don't know how to abort */
+}
+
+int
+NCR53c406a_reset(Scsi_Cmnd *SCpnt){
+ DEB(printk("NCR53c406a_reset called\n"));
+ outb(C4_IMG, CONFIG4); /* Select reg set 0 */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG); /* required after reset */
+ outb(SCSI_RESET, CMD_REG);
+ chip_init();
+
+ rtrc(2);
+ if (irq_level)
+ return SCSI_RESET_PENDING; /* should get an interrupt */
+ else
+ return SCSI_RESET_WAKEUP; /* won't get any interrupts */
+}
+
+int
+NCR53c406a_biosparm(Scsi_Disk *disk, kdev_t dev, int* info_array){
+ int size;
+
+ DEB(printk("NCR53c406a_biosparm called\n"));
+
+ size = disk->capacity;
+ info_array[0] = 64; /* heads */
+ info_array[1] = 32; /* sectors */
+ info_array[2] = size>>11; /* cylinders */
+ if (info_array[2] > 1024) { /* big disk */
+ info_array[0] = 255;
+ info_array[1] = 63;
+ info_array[2] = size / (255*63);
+ }
+ return 0;
+ }
+
+ static void
+NCR53c406a_intr(int unused, struct pt_regs *regs){
+ DEB(unsigned char fifo_size;)
+ DEB(unsigned char seq_reg;)
+ unsigned char status, int_reg;
+ unsigned long flags = 0;
+#if USE_PIO
+ unsigned char pio_status;
+ struct scatterlist *sglist;
+ unsigned int sgcount;
+#endif
+
+ VDEB(printk("NCR53c406a_intr called\n"));
+
+ save_flags(flags);
+ cli();
+#if USE_PIO
+ REG1;
+ pio_status = inb(PIO_STATUS);
+#endif
+ REG0;
+ status = inb(STAT_REG);
+ DEB(seq_reg = inb(SEQ_REG));
+ int_reg = inb(INT_REG);
+ DEB(fifo_size = inb(FIFO_FLAGS) & 0x1f);
+ restore_flags(flags);
+
+#if NCR53C406A_DEBUG
+ printk("status=%02x, seq_reg=%02x, int_reg=%02x, fifo_size=%02x",
+ status, seq_reg, int_reg, fifo_size);
+#if (USE_DMA)
+ printk("\n");
+#else
+ printk(", pio=%02x\n", pio_status);
+#endif USE_DMA
+#endif NCR53C406A_DEBUG
+
+ if(int_reg & 0x80){ /* SCSI reset intr */
+ rtrc(3);
+ DEB(printk("NCR53c406a: reset intr received\n"));
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_RESET << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+#if USE_PIO
+ if(pio_status & 0x80) {
+ printk("NCR53C406A: Warning: PIO error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+#endif USE_PIO
+
+ if(status & 0x20) { /* Parity error */
+ printk("NCR53c406a: Warning: parity error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_PARITY << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if(status & 0x40) { /* Gross error */
+ printk("NCR53c406a: Warning: gross error!\n");
+ current_SC->SCp.phase = idle;
+ current_SC->result = DID_ERROR << 16;
+ current_SC->scsi_done(current_SC);
+ return;
+ }
+
+ if(int_reg & 0x20){ /* Disconnect */
+ DEB(printk("NCR53c406a: disconnect intr received\n"));
+ if(current_SC->SCp.phase != message_in){ /* Unexpected disconnect */
+ current_SC->result = DID_NO_CONNECT << 16;
+ }
+ else{ /* Command complete, return status and message */
+ current_SC->result = (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16);
+ }
+
+ rtrc(0);
+ current_SC->SCp.phase = idle;
+ current_SC->scsi_done( current_SC );
+ return;
+ }
+
+ switch(status & 0x07){ /* scsi phase */
+ case 0x00: /* DATA-OUT */
+ if(int_reg & 0x10){ /* Target requesting info transfer */
+ rtrc(5);
+ current_SC->SCp.phase = data_out;
+ VDEB(printk("NCR53c406a: Data-Out phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+#if USE_DMA /* No s/g support for DMA */
+ NCR53c406a_dma_write(current_SC->request_buffer,
+ current_SC->request_bufflen);
+#endif USE_DMA
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+#if USE_PIO
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ NCR53c406a_pio_write(current_SC->request_buffer,
+ current_SC->request_bufflen);
+ else { /* use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while( sgcount-- ) {
+ NCR53c406a_pio_write(sglist->address, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+#endif USE_PIO
+ }
+ break;
+
+ case 0x01: /* DATA-IN */
+ if(int_reg & 0x10){ /* Target requesting info transfer */
+ rtrc(6);
+ current_SC->SCp.phase = data_in;
+ VDEB(printk("NCR53c406a: Data-In phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ LOAD_DMA_COUNT(current_SC->request_bufflen); /* Max transfer size */
+#if USE_DMA /* No s/g support for DMA */
+ NCR53c406a_dma_read(current_SC->request_buffer,
+ current_SC->request_bufflen);
+#endif USE_DMA
+ outb(TRANSFER_INFO | DMA_OP, CMD_REG);
+#if USE_PIO
+ if (!current_SC->use_sg) /* Don't use scatter-gather */
+ NCR53c406a_pio_read(current_SC->request_buffer,
+ current_SC->request_bufflen);
+ else { /* Use scatter-gather */
+ sgcount = current_SC->use_sg;
+ sglist = current_SC->request_buffer;
+ while( sgcount-- ) {
+ NCR53c406a_pio_read(sglist->address, sglist->length);
+ sglist++;
+ }
+ }
+ REG0;
+#endif USE_PIO
+ }
+ break;
+
+ case 0x02: /* COMMAND */
+ current_SC->SCp.phase = command_ph;
+ printk("NCR53c406a: Warning: Unknown interupt occured in command phase!\n");
+ break;
+
+ case 0x03: /* STATUS */
+ rtrc(7);
+ current_SC->SCp.phase = status_ph;
+ VDEB(printk("NCR53c406a: Status phase\n"));
+ outb(FLUSH_FIFO, CMD_REG);
+ outb(INIT_CMD_COMPLETE, CMD_REG);
+ break;
+
+ case 0x04: /* Reserved */
+ case 0x05: /* Reserved */
+ printk("NCR53c406a: WARNING: Reserved phase!!!\n");
+ break;
+
+ case 0x06: /* MESSAGE-OUT */
+ DEB(printk("NCR53c406a: Message-Out phase\n"));
+ current_SC->SCp.phase = message_out;
+ outb(SET_ATN, CMD_REG); /* Reject the message */
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+
+ case 0x07: /* MESSAGE-IN */
+ rtrc(4);
+ VDEB(printk("NCR53c406a: Message-In phase\n"));
+ current_SC->SCp.phase = message_in;
+
+ current_SC->SCp.Status = inb(SCSI_FIFO);
+ current_SC->SCp.Message = inb(SCSI_FIFO);
+
+ VDEB(printk("SCSI FIFO size=%d\n", inb(FIFO_FLAGS) & 0x1f));
+ DEB(printk("Status = %02x Message = %02x\n",
+ current_SC->SCp.Status, current_SC->SCp.Message));
+
+ if(current_SC->SCp.Message == SAVE_POINTERS ||
+ current_SC->SCp.Message == DISCONNECT) {
+ outb(SET_ATN, CMD_REG); /* Reject message */
+ DEB(printk("Discarding SAVE_POINTERS message\n"));
+ }
+ outb(MSG_ACCEPT, CMD_REG);
+ break;
+ }
+}
+
+#ifndef IRQ_LEV
+static int irq_probe()
+{
+ int irqs, irq;
+ int i;
+
+ inb(INT_REG); /* clear the interrupt register */
+ sti();
+ irqs = probe_irq_on();
+
+ /* Invalid command will cause an interrupt */
+ REG0;
+ outb(0xff, CMD_REG);
+
+ /* Wait for the interrupt to occur */
+ i = jiffies + WATCHDOG;
+ while(i > jiffies && !(inb(STAT_REG) & 0x80))
+ barrier();
+ if (i <= jiffies) { /* Timed out, must be hardware trouble */
+ probe_irq_off(irqs);
+ return -1;
+ }
+
+ irq = probe_irq_off(irqs);
+
+ /* Kick the chip */
+ outb(CHIP_RESET, CMD_REG);
+ outb(SCSI_NOP, CMD_REG);
+ chip_init();
+
+ return irq;
+}
+#endif IRQ_LEV
+
+static void chip_init()
+{
+ REG1;
+#if USE_DMA
+ outb(0x00, PIO_STATUS);
+#else /* USE_PIO */
+ outb(0x01, PIO_STATUS);
+#endif
+ outb(0x00, PIO_FLAG);
+
+ outb(C4_IMG, CONFIG4); /* REG0; */
+ outb(C3_IMG, CONFIG3);
+ outb(C2_IMG, CONFIG2);
+ outb(C1_IMG, CONFIG1);
+
+ outb(0x05, CLKCONV); /* clock conversion factor */
+ outb(0x9C, SRTIMOUT); /* Selection timeout */
+ outb(0x05, SYNCPRD); /* Synchronous transfer period */
+ outb(SYNC_MODE, SYNCOFF); /* synchronous mode */
+}
+
+void calc_port_addr()
+{
+ /* Control Register Set 0 */
+ TC_LSB = (port_base+0x00);
+ TC_MSB = (port_base+0x01);
+ SCSI_FIFO = (port_base+0x02);
+ CMD_REG = (port_base+0x03);
+ STAT_REG = (port_base+0x04);
+ DEST_ID = (port_base+0x04);
+ INT_REG = (port_base+0x05);
+ SRTIMOUT = (port_base+0x05);
+ SEQ_REG = (port_base+0x06);
+ SYNCPRD = (port_base+0x06);
+ FIFO_FLAGS = (port_base+0x07);
+ SYNCOFF = (port_base+0x07);
+ CONFIG1 = (port_base+0x08);
+ CLKCONV = (port_base+0x09);
+ /* TESTREG = (port_base+0x0A); */
+ CONFIG2 = (port_base+0x0B);
+ CONFIG3 = (port_base+0x0C);
+ CONFIG4 = (port_base+0x0D);
+ TC_HIGH = (port_base+0x0E);
+ /* FIFO_BOTTOM = (port_base+0x0F); */
+
+ /* Control Register Set 1 */
+ /* JUMPER_SENSE = (port_base+0x00);*/
+ /* SRAM_PTR = (port_base+0x01);*/
+ /* SRAM_DATA = (port_base+0x02);*/
+ PIO_FIFO = (port_base+0x04);
+ /* PIO_FIFO1 = (port_base+0x05);*/
+ /* PIO_FIFO2 = (port_base+0x06);*/
+ /* PIO_FIFO3 = (port_base+0x07);*/
+ PIO_STATUS = (port_base+0x08);
+ /* ATA_CMD = (port_base+0x09);*/
+ /* ATA_ERR = (port_base+0x0A);*/
+ PIO_FLAG = (port_base+0x0B);
+ CONFIG5 = (port_base+0x0D);
+ /* SIGNATURE = (port_base+0x0E);*/
+ /* CONFIG6 = (port_base+0x0F);*/
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = NCR53c406a;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we get a uniform 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/i386/i386at/gpl/linux/scsi/NCR53c406a.h b/i386/i386at/gpl/linux/scsi/NCR53c406a.h
new file mode 100644
index 00000000..dcb48870
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/NCR53c406a.h
@@ -0,0 +1,83 @@
+#ifndef _NCR53C406A_H
+#define _NCR53C406A_H
+
+/*
+ * NCR53c406a.h
+ *
+ * Copyright (C) 1994 Normunds Saumanis (normunds@rx.tech.swh.lv)
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ */
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+/* NOTE: scatter-gather support only works in PIO mode.
+ * Use SG_NONE if DMA mode is enabled!
+ */
+#define NCR53c406a { \
+ NULL /* next */, \
+ NULL /* usage count */, \
+ &proc_scsi_NCR53c406a /* proc_dir */, \
+ NULL /* proc_info */, \
+ "NCR53c406a" /* name */, \
+ NCR53c406a_detect /* detect */, \
+ NULL /* release */, \
+ NCR53c406a_info /* info */, \
+ NCR53c406a_command /* command */, \
+ NCR53c406a_queue /* queuecommand */, \
+ NCR53c406a_abort /* abort */, \
+ NCR53c406a_reset /* reset */, \
+ NULL /* slave_attach */, \
+ NCR53c406a_biosparm /* biosparm */, \
+ 1 /* can_queue */, \
+ 7 /* SCSI ID of the chip */, \
+ 32 /*SG_ALL*/ /*SG_NONE*/, \
+ 1 /* commands per lun */, \
+ 0 /* number of boards in system */, \
+ 1 /* unchecked_isa_dma */, \
+ ENABLE_CLUSTERING \
+}
+
+extern struct proc_dir_entry proc_scsi_NCR53c406a;
+
+int NCR53c406a_detect(Scsi_Host_Template *);
+const char* NCR53c406a_info(struct Scsi_Host *);
+
+int NCR53c406a_command(Scsi_Cmnd *);
+int NCR53c406a_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int NCR53c406a_abort(Scsi_Cmnd *);
+int NCR53c406a_reset(Scsi_Cmnd *);
+int NCR53c406a_biosparm(Disk *, kdev_t, int []);
+
+#endif /* _NCR53C406A_H */
+
+/*
+ * Overrides for Emacs so that we get a uniform 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/i386/i386at/gpl/linux/scsi/advansys.c b/i386/i386at/gpl/linux/scsi/advansys.c
new file mode 100644
index 00000000..3f8efe3d
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/advansys.c
@@ -0,0 +1,9061 @@
+/* $Id: advansys.c,v 1.1.1.1 1997/02/25 21:27:45 thomas Exp $ */
+/*
+ * advansys.c - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ * All Rights Reserved.
+ *
+ * This driver may be modified and freely distributed provided that
+ * the above copyright message and this comment are included in the
+ * distribution. The latest version of this driver is available at
+ * the AdvanSys FTP and BBS sites listed below.
+ *
+ * Please send questions, comments, bug reports to:
+ * bobf@advansys.com (Bob Frey)
+ */
+
+/* The driver has been tested with Linux 1.2.1 and 1.3.57 kernels. */
+#define ASC_VERSION "1.2" /* AdvanSys Driver Version */
+
+/*
+
+ Documentation for the AdvanSys Driver
+
+ A. Adapters Supported by this Driver
+ B. Linux 1.2.X - Directions for Adding the AdvanSys Driver
+ C. Linux 1.3.X - Directions for Adding the AdvanSys Driver
+ D. Source Comments
+ E. Driver Compile Time Options and Debugging
+ F. Driver LILO Option
+ G. Release History
+ H. Known Problems or Issues
+ I. Credits
+ J. AdvanSys Contact Information
+
+
+ A. Adapters Supported by this Driver
+
+ AdvanSys (Advanced System Products, Inc.) manufactures the following
+ Bus-Mastering SCSI-2 Host Adapters for the ISA, EISA, VL, and PCI
+ buses. This Linux driver supports all of these adapters.
+
+ The CDB counts below indicate the number of SCSI CDB (Command
+ Descriptor Block) requests that can be stored in the RISC chip
+ cache and board LRAM. The driver detect routine will display the
+ number of CDBs available for each adapter detected. This value
+ can be lowered in the BIOS by changing the 'Host Queue Size'
+ adapter setting.
+
+ Connectivity Products:
+ ABP920 - Bus-Master PCI 16 CDB
+ ABP930 - Bus-Master PCI 16 CDB
+ ABP5140 - Bus-Master PnP ISA 16 CDB
+
+ Single Channel Products:
+ ABP542 - Bus-Master ISA 240 CDB
+ ABP5150 - Bus-Master ISA 240 CDB *
+ ABP742 - Bus-Master EISA 240 CDB
+ ABP842 - Bus-Master VL 240 CDB
+ ABP940 - Bus-Master PCI 240 CDB
+
+ Dual Channel Products:
+ ABP950 - Dual Channel Bus-Master PCI 240 CDB Per Channel
+ ABP852 - Dual Channel Bus-Master VL 240 CDB Per Channel
+ ABP752 - Dual Channel Bus-Master EISA 240 CDB Per Channel
+
+ * This board is shipped by HP with the 4020i CD-R drive. It has
+ no BIOS so it cannot control a boot device, but it can control
+ any secondary devices.
+
+ B. Linux 1.2.X - Directions for Adding the AdvanSys Driver
+
+ There are two source files: advansys.h and advansys.c. Copy
+ both of these files to the directory /usr/src/linux/drivers/scsi.
+
+ 1. Add the following line to /usr/src/linux/arch/i386/config.in
+ after "comment 'SCSI low-level drivers'":
+
+ bool 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS y
+
+ 2. Add the following lines to /usr/src/linux/drivers/scsi/hosts.c
+ after "#include "hosts.h"":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ #include "advansys.h"
+ #endif
+
+ and after "static Scsi_Host_Template builtin_scsi_hosts[] =":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+ #endif
+
+ 3. Add the following lines to /usr/src/linux/drivers/scsi/Makefile:
+
+ ifdef CONFIG_SCSI_ADVANSYS
+ SCSI_SRCS := $(SCSI_SRCS) advansys.c
+ SCSI_OBJS := $(SCSI_OBJS) advansys.o
+ else
+ SCSI_MODULE_OBJS := $(SCSI_MODULE_OBJS) advansys.o
+ endif
+
+ 4. (Optional) If you would like to enable the LILO command line
+ and /etc/lilo.conf 'advansys' option, make the following changes.
+ This option can be used to disable I/O port scanning or to limit
+ I/O port scanning to specific addresses. Refer to the 'Driver
+ LILO Option' section below. Add the following lines to
+ /usr/src/linux/init/main.c in the prototype section:
+
+ extern void advansys_setup(char *str, int *ints);
+
+ and add the following lines to the bootsetups[] array.
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ { "advansys=", advansys_setup },
+ #endif
+
+ 5. If you have the HP 4020i CD-R driver and Linux 1.2.X you should
+ add a fix to the CD-ROM target driver. This fix will allow
+ you to mount CDs with the iso9660 file system. Linux 1.3.X
+ already has this fix. In the file /usr/src/linux/drivers/scsi/sr.c
+ and function get_sectorsize() after the line:
+
+ if(scsi_CDs[i].sector_size == 0) scsi_CDs[i].sector_size = 2048;
+
+ add the following line:
+
+ if(scsi_CDs[i].sector_size == 2340) scsi_CDs[i].sector_size = 2048;
+
+ 6. In the directory /usr/src/linux run 'make config' to configure
+ the AdvanSys driver, then run 'make vmlinux' or 'make zlilo' to
+ make the kernel. If the AdvanSys driver is not configured, then
+ a loadable module can be built by running 'make modules' and
+ 'make modules_install'. Use 'insmod' and 'rmmod' to install
+ and remove advansys.o.
+
+ C. Linux 1.3.X - Directions for Adding the AdvanSys Driver
+
+ There are two source files: advansys.h and advansys.c. Copy
+ both of these files to the directory /usr/src/linux/drivers/scsi.
+
+ 1. Add the following line to /usr/src/linux/drivers/scsi/Config.in
+ after "comment 'SCSI low-level drivers'":
+
+ dep_tristate 'AdvanSys SCSI support' CONFIG_SCSI_ADVANSYS $CONFIG_SCSI
+
+ 2. Add the following lines to /usr/src/linux/drivers/scsi/hosts.c
+ after "#include "hosts.h"":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ #include "advansys.h"
+ #endif
+
+ and after "static Scsi_Host_Template builtin_scsi_hosts[] =":
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+ #endif
+
+ 3. Add the following lines to /usr/src/linux/drivers/scsi/Makefile:
+
+ ifeq ($(CONFIG_SCSI_ADVANSYS),y)
+ L_OBJS += advansys.o
+ else
+ ifeq ($(CONFIG_SCSI_ADVANSYS),m)
+ M_OBJS += advansys.o
+ endif
+ endif
+
+ 4. Add the following line to /usr/src/linux/include/linux/proc_fs.h
+ in the enum scsi_directory_inos array:
+
+ PROC_SCSI_ADVANSYS,
+
+ 5. (Optional) If you would like to enable the LILO command line
+ and /etc/lilo.conf 'advansys' option, make the following changes.
+ This option can be used to disable I/O port scanning or to limit
+ I/O port scanning to specific addresses. Refer to the 'Driver
+ LILO Option' section below. Add the following lines to
+ /usr/src/linux/init/main.c in the prototype section:
+
+ extern void advansys_setup(char *str, int *ints);
+
+ and add the following lines to the bootsetups[] array.
+
+ #ifdef CONFIG_SCSI_ADVANSYS
+ { "advansys=", advansys_setup },
+ #endif
+
+ 6. In the directory /usr/src/linux run 'make config' to configure
+ the AdvanSys driver, then run 'make vmlinux' or 'make zlilo' to
+ make the kernel. If the AdvanSys driver is not configured, then
+ a loadable module can be built by running 'make modules' and
+ 'make modules_install'. Use 'insmod' and 'rmmod' to install
+ and remove advansys.o.
+
+ D. Source Comments
+
+ 1. Use tab stops set to 4 for the source files. For vi use 'se tabstops=4'.
+
+ 2. This driver should be maintained in multiple files. But to make
+ it easier to include with Linux and to follow Linux conventions,
+ the whole driver is maintained in the source files advansys.h and
+ advansys.c. In this file logical sections of the driver begin with
+ a comment that contains '---'. The following are the logical sections
+ of the driver below.
+
+ --- Linux Version
+ --- Linux Include Files
+ --- Driver Options
+ --- Asc Library Constants and Macros
+ --- Debugging Header
+ --- Driver Constants
+ --- Driver Macros
+ --- Driver Structures
+ --- Driver Data
+ --- Driver Function Prototypes
+ --- Linux 'Scsi_Host_Template' and advansys_setup() Functions
+ --- Loadable Driver Support
+ --- Miscellaneous Driver Functions
+ --- Functions Required by the Asc Library
+ --- Tracing and Debugging Functions
+ --- Asc Library Functions
+
+ 3. The string 'XXX' is used to flag code that needs to be re-written
+ or that contains a problem that needs to be addressed.
+
+ 4. I have stripped comments from and reformatted the source for the
+ Asc Library which is included in this file. I haven't done this
+ to obfuscate the code. Actually I have done this to deobfuscate
+ the code. The Asc Library source can be found under the following
+ headings.
+
+ --- Asc Library Constants and Macros
+ --- Asc Library Functions
+
+ E. Driver Compile Time Options and Debugging
+
+ In this source file the following constants can be defined. They are
+ defined in the source below. Both of these options are enabled by
+ default.
+
+ 1. ADVANSYS_DEBUG - enable for debugging and assertions
+
+ The amount of debugging output can be controlled with the global
+ variable 'asc_dbglvl'. The higher the number the more output. By
+ default the debug level is 0.
+
+ If the driver is loaded at boot time and the LILO Driver Option
+ is included in the system, the debug level can be changed by
+ specifying a 5th (ASC_NUM_BOARD_SUPPORTED + 1) I/O Port. The
+ first three hex digits of the pseudo I/O Port must be set to
+ 'deb' and the fourth hex digit specifies the debug level: 0 - F.
+ The following command line will look for an adapter at 0x330
+ and set the debug level to 2.
+
+ linux advansys=0x330,0x0,0x0,0x0,0xdeb2
+
+ If the driver is built as a loadable module this variable can be
+ defined when the driver is loaded. The following insmod command
+ will set the debug level to one.
+
+ insmod advansys.o asc_dbglvl=1
+
+
+ Debugging Message Levels:
+ 0: Errors Only
+ 1: High-Level Tracing
+ 2-N: Verbose Tracing
+
+ I don't know the approved way for turning on printk()s to the
+ console. Here's a program I use to do this. Debug output is
+ logged in /var/adm/messages.
+
+ main()
+ {
+ syscall(103, 7, 0, 0);
+ }
+
+ I found that increasing LOG_BUF_LEN to 40960 in kernel/printk.c
+ prevents most level 1 debug messages from being lost.
+
+ 2. ADVANSYS_STATS - enable statistics and tracing
+
+ For Linux 1.2.X if ADVANSYS_STATS_1_2_PRINT is defined every
+ 10,000 I/O operations the driver will print statistics to the
+ console. This value can be changed by modifying the constant
+ used in advansys_queuecommand(). ADVANSYS_STATS_1_2_PRINT is
+ off by default.
+
+ For Linux 1.3.X statistics can be accessed by reading the
+ /proc/scsi/advansys/[0-9] files.
+
+ Note: these statistics are currently maintained on a global driver
+ basis and not per board.
+
+ F. Driver LILO Option
+
+ If init/main.c is modified as described in the 'Directions for Adding
+ the AdvanSys Driver to Linux' section (B.4.) above, the driver will
+ recognize the 'advansys' LILO command line and /etc/lilo.conf option.
+ This option can be used to either disable I/O port scanning or to limit
+ scanning to 1 - 4 I/O ports. Regardless of the option setting EISA and
+ PCI boards will still be searched for and detected. This option only
+ affects searching for ISA and VL boards.
+
+ Examples:
+ 1. Eliminate I/O port scanning:
+ boot: linux advansys=
+ or
+ boot: linux advansys=0x0
+ 2. Limit I/O port scanning to one I/O port:
+ boot: linux advansys=0x110
+ 3. Limit I/O port scanning to four I/O ports:
+ boot: linux advansys=0x110,0x210,0x230,0x330
+
+ For a loadable module the same effect can be achieved by setting
+ the 'asc_iopflag' variable and 'asc_ioport' array when loading
+ the driver, e.g.
+
+ insmod advansys.o asc_iopflag=1 asc_ioport=0x110,0x330
+
+ If ADVANSYS_DEBUG is defined a 5th (ASC_NUM_BOARD_SUPPORTED + 1)
+ I/O Port may be added to specify the driver debug level. Refer to
+ the 'Driver Compile Time Options and Debugging' section above for
+ more information.
+
+ G. Release History
+
+ 12/23/95 BETA-1.0:
+ First Release
+
+ 12/28/95 BETA-1.1:
+ 1. Prevent advansys_detect() from being called twice.
+ 2. Add LILO 0xdeb[0-f] option to set 'asc_dbglvl'.
+
+ 1/12/96 1.2:
+ 1. Prevent re-entrancy in the interrupt handler which
+ resulted in the driver hanging Linux.
+ 2. Fix problem that prevented ABP-940 cards from being
+ recognized on some PCI motherboards.
+ 3. Add support for the ABP-5140 PnP ISA card.
+ 4. Fix check condition return status.
+ 5. Add conditionally compiled code for Linux 1.3.X.
+
+ H. Known Problems or Issues
+
+ 1. The setting for 'cmd_per_lun' needs to be changed. It is currently
+ less then what the AdvanSys boards can queue. Because the target and
+ mid-level Linux drivers base memory allocation on 'cmd_per_lun' (as
+ well as 'sg_tablesize') memory use gets out of hand with a large
+ 'cmd_per_lun'. 'cmd_per_lun' should be per device instead of per
+ adapter. When the driver is compiled as a loadable module both
+ 'cmd_per_lun' and 'sg_tablesize' are tuned down to try to prevent
+ memory allocation errors.
+
+ 2. For the first scsi command sent to a device the driver increases
+ the timeout value. This gives the driver more time to perform
+ its own initialization for the board and each device. The timeout
+ value is only changed on the first scsi command for each device
+ and never thereafter.
+
+ I. Credits
+
+ Nathan Hartwell (mage@cdc3.cdc.net) provided the directions and
+ and basis for the Linux 1.3.X changes which were included in the
+ 1.2 release.
+
+ J. AdvanSys Contact Information
+
+ Mail: Advanced System Products, Inc.
+ 1150 Ringwood Court
+ San Jose, CA 95131 USA
+ Operator: 1-408-383-9400
+ FAX: 1-408-383-9612
+ Tech Support: 1-800-525-7440
+ BBS: 1-408-383-9540 (9600,N,8,1)
+ Interactive FAX: 1-408-383-9753
+ Customer Direct Sales: 1-800-883-1099
+ Tech Support E-Mail: support@advansys.com
+ Linux Support E-Mail: bobf@advansys.com
+ FTP Site: ftp.advansys.com (login: anonymous)
+ Web Site: http://www.advansys.com
+
+*/
+
+
+/*
+ * --- Linux Version
+ */
+
+/*
+ * The driver can be used in Linux 1.2.X or 1.3.X.
+ */
+#if !defined(LINUX_1_2) && !defined(LINUX_1_3)
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif /* LINUX_VERSION_CODE */
+#if LINUX_VERSION_CODE > 65536 + 3 * 256
+#define LINUX_1_3
+#else /* LINUX_VERSION_CODE */
+#define LINUX_1_2
+#endif /* LINUX_VERSION_CODE */
+#endif /* !defined(LINUX_1_2) && !defined(LINUX_1_3) */
+
+
+/*
+ * --- Linux Include Files
+ */
+
+#ifdef MODULE
+#ifdef LINUX_1_3
+#include <linux/autoconf.h>
+#endif /* LINUX_1_3 */
+#include <linux/module.h>
+#endif /* MODULE */
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/malloc.h>
+#include <linux/config.h>
+#ifdef LINUX_1_3
+#include <linux/proc_fs.h>
+#endif /* LINUX_1_3 */
+#include <asm/io.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+#ifdef LINUX_1_2
+#include "../block/blk.h"
+#else /* LINUX_1_3 */
+#include <linux/blk.h>
+#include <linux/stat.h>
+#endif /* LINUX_1_3 */
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "advansys.h"
+
+
+/*
+ * --- Driver Options
+ */
+#define ADVANSYS_DEBUG /* Enable for debugging and assertions. */
+#define ADVANSYS_STATS /* Enable for statistics and tracing. */
+#ifdef LINUX_1_2
+#undef ADVANSYS_STATS_1_2_PRINT /* Enable to print statistics to console. */
+#endif /* LINUX_1_2 */
+
+
+/*
+ * --- Asc Library Constants and Macros
+ */
+
+#define ASC_LIB_VERSION_MAJOR 1
+#define ASC_LIB_VERSION_MINOR 16
+#define ASC_LIB_SERIAL_NUMBER 53
+
+typedef unsigned char uchar;
+typedef unsigned char BYTE;
+typedef unsigned short WORD;
+typedef unsigned long DWORD;
+
+typedef int BOOL;
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+#ifndef TRUE
+#define TRUE (1)
+#endif
+
+#ifndef FALSE
+#define FALSE (0)
+#endif
+
+#define REG register
+
+#define rchar REG char
+#define rshort REG short
+#define rint REG int
+#define rlong REG long
+
+#define ruchar REG uchar
+#define rushort REG ushort
+#define ruint REG uint
+#define rulong REG ulong
+
+#define NULLPTR ( void *)0
+#define FNULLPTR ( void dosfar *)0UL
+#define EOF (-1)
+#define EOS '\0'
+#define ERR (-1)
+#define UB_ERR (uchar)(0xFF)
+#define UW_ERR (uint)(0xFFFF)
+#define UL_ERR (ulong)(0xFFFFFFFFUL)
+
+#define iseven_word( val ) ( ( ( ( uint )val) & ( uint )0x0001 ) == 0 )
+#define isodd_word( val ) ( ( ( ( uint )val) & ( uint )0x0001 ) != 0 )
+#define toeven_word( val ) ( ( ( uint )val ) & ( uint )0xFFFE )
+
+#define biton( val, bits ) ((( uint )( val >> bits ) & (uint)0x0001 ) != 0 )
+#define bitoff( val, bits ) ((( uint )( val >> bits ) & (uint)0x0001 ) == 0 )
+#define lbiton( val, bits ) ((( ulong )( val >> bits ) & (ulong)0x00000001UL ) != 0 )
+#define lbitoff( val, bits ) ((( ulong )( val >> bits ) & (ulong)0x00000001UL ) == 0 )
+
+#define absh( val ) ( ( val ) < 0 ? -( val ) : ( val ) )
+
+#define swapbyte( ch ) ( ( ( (ch) << 4 ) | ( (ch) >> 4 ) ) )
+
+#ifndef GBYTE
+#define GBYTE (0x40000000UL)
+#endif
+
+#ifndef MBYTE
+#define MBYTE (0x100000UL)
+#endif
+
+#ifndef KBYTE
+#define KBYTE (0x400)
+#endif
+
+#define HI_BYTE(x) ( *( ( BYTE *)(&x)+1 ) )
+#define LO_BYTE(x) ( *( ( BYTE *)&x ) )
+
+#define HI_WORD(x) ( *( ( WORD *)(&x)+1 ) )
+#define LO_WORD(x) ( *( ( WORD *)&x ) )
+
+#ifndef MAKEWORD
+#define MAKEWORD(lo, hi) ((WORD) (((WORD) lo) | ((WORD) hi << 8)))
+#endif
+
+#ifndef MAKELONG
+#define MAKELONG(lo, hi) ((DWORD) (((DWORD) lo) | ((DWORD) hi << 16)))
+#endif
+
+#define SwapWords(dWord) ((DWORD) ((dWord >> 16) | (dWord << 16)))
+#define SwapBytes(word) ((WORD) ((word >> 8) | (word << 8)))
+
+#define BigToLittle(dWord) \
+ ((DWORD) (SwapWords(MAKELONG(SwapBytes(LO_WORD(dWord)), SwapBytes(HI_WORD(dWord))))))
+#define LittleToBig(dWord) BigToLittle(dWord)
+
+#define Lptr
+#define dosfar
+#define far
+#define PortAddr unsigned short
+#define Ptr2Func ulong
+
+#define inp(port) inb(port)
+#define inpw(port) inw(port)
+#define outp(port, byte) outb((byte), (port))
+#define outpw(port, word) outw((word), (port))
+
+#define ASC_MAX_SG_QUEUE 5
+#define ASC_MAX_SG_LIST (1 + ((ASC_SG_LIST_PER_Q) * (ASC_MAX_SG_QUEUE)))
+
+#define CC_INIT_INQ_DISPLAY FALSE
+
+#define CC_CLEAR_LRAM_SRB_PTR FALSE
+#define CC_VERIFY_LRAM_COPY FALSE
+
+#define CC_DEBUG_SG_LIST FALSE
+#define CC_FAST_STRING_IO FALSE
+
+#define CC_WRITE_IO_COUNT FALSE
+#define CC_CLEAR_DMA_REMAIN FALSE
+
+#define CC_DISABLE_PCI_PARITY_INT TRUE
+
+#define CC_LINK_BUSY_Q FALSE
+
+#define CC_TARGET_MODE FALSE
+
+#define CC_SCAM FALSE
+
+#define CC_LITTLE_ENDIAN_HOST TRUE
+
+#ifndef CC_TEST_LRAM_ENDIAN
+
+#if CC_LITTLE_ENDIAN_HOST
+#define CC_TEST_LRAM_ENDIAN FALSE
+#else
+#define CC_TEST_LRAM_ENDIAN TRUE
+#endif
+
+#endif
+
+#define CC_STRUCT_ALIGNED TRUE
+
+#define CC_MEMORY_MAPPED_IO FALSE
+
+#ifndef CC_TARGET_MODE
+#define CC_TARGET_MODE FALSE
+#endif
+
+#ifndef CC_STRUCT_ALIGNED
+#define CC_STRUCT_ALIGNED FALSE
+#endif
+
+#ifndef CC_LITTLE_ENDIAN_HOST
+#define CC_LITTLE_ENDIAN_HOST TRUE
+#endif
+
+#if !CC_LITTLE_ENDIAN_HOST
+
+#ifndef CC_TEST_LRAM_ENDIAN
+#define CC_TEST_LRAM_ENDIAN TRUE
+#endif
+
+#endif
+
+#ifndef CC_MEMORY_MAPPED_IO
+#define CC_MEMORY_MAPPED_IO FALSE
+#endif
+
+#ifndef CC_WRITE_IO_COUNT
+#define CC_WRITE_IO_COUNT FALSE
+#endif
+
+#ifndef CC_CLEAR_DMA_REMAIN
+#define CC_CLEAR_DMA_REMAIN FALSE
+#endif
+
+#define ASC_CS_TYPE unsigned short
+
+#ifndef asc_ptr_type
+#define asc_ptr_type
+#endif
+
+#ifndef CC_SCAM
+#define CC_SCAM FALSE
+#endif
+
+#ifndef ASC_GET_PTR2FUNC
+#define ASC_GET_PTR2FUNC( fun ) ( Ptr2Func )( fun )
+#endif
+
+#define FLIP_BYTE_NIBBLE( x ) ( ((x<<4)& 0xFF) | (x>>4) )
+
+#define ASC_IS_ISA (0x0001)
+#define ASC_IS_ISAPNP (0x0081)
+#define ASC_IS_EISA (0x0002)
+#define ASC_IS_PCI (0x0004)
+#define ASC_IS_PCMCIA (0x0008)
+#define ASC_IS_PNP (0x0010)
+#define ASC_IS_MCA (0x0020)
+#define ASC_IS_VL (0x0040)
+
+#define ASC_ISA_PNP_PORT_ADDR (0x279)
+#define ASC_ISA_PNP_PORT_WRITE (ASC_ISA_PNP_PORT_ADDR+0x800)
+
+#define ASC_IS_WIDESCSI_16 (0x0100)
+#define ASC_IS_WIDESCSI_32 (0x0200)
+
+#define ASC_IS_BIG_ENDIAN (0x8000)
+
+#define ASC_CHIP_MIN_VER_VL (0x01)
+#define ASC_CHIP_MAX_VER_VL (0x07)
+
+#define ASC_CHIP_MIN_VER_PCI (0x09)
+#define ASC_CHIP_MAX_VER_PCI (0x0F)
+#define ASC_CHIP_VER_PCI_BIT (0x08)
+
+#define ASC_CHIP_MIN_VER_ISA (0x11)
+#define ASC_CHIP_MIN_VER_ISA_PNP (0x21)
+#define ASC_CHIP_MAX_VER_ISA (0x27)
+#define ASC_CHIP_VER_ISA_BIT (0x30)
+#define ASC_CHIP_VER_ISAPNP_BIT (0x20)
+
+#define ASC_CHIP_VER_ASYN_BUG (0x21)
+
+#define ASC_CHIP_MIN_VER_EISA (0x41)
+#define ASC_CHIP_MAX_VER_EISA (0x47)
+#define ASC_CHIP_VER_EISA_BIT (0x40)
+
+#define ASC_MAX_VL_DMA_ADDR (0x07FFFFFFL)
+#define ASC_MAX_VL_DMA_COUNT (0x07FFFFFFL)
+
+#define ASC_MAX_PCI_DMA_ADDR (0xFFFFFFFFL)
+#define ASC_MAX_PCI_DMA_COUNT (0xFFFFFFFFL)
+
+#define ASC_MAX_ISA_DMA_ADDR (0x00FFFFFFL)
+#define ASC_MAX_ISA_DMA_COUNT (0x00FFFFFFL)
+
+#define ASC_MAX_EISA_DMA_ADDR (0x07FFFFFFL)
+#define ASC_MAX_EISA_DMA_COUNT (0x07FFFFFFL)
+
+#if !CC_STRUCT_ALIGNED
+
+#define DvcGetQinfo( iop_base, s_addr, outbuf, words) \
+ AscMemWordCopyFromLram( iop_base, s_addr, outbuf, words)
+
+#define DvcPutScsiQ( iop_base, s_addr, outbuf, words) \
+ AscMemWordCopyToLram( iop_base, s_addr, outbuf, words)
+
+#endif
+
+#define ASC_SCSI_ID_BITS 3
+#define ASC_SCSI_TIX_TYPE uchar
+#define ASC_ALL_DEVICE_BIT_SET 0xFF
+
+#ifdef ASC_WIDESCSI_16
+
+#undef ASC_SCSI_ID_BITS
+#define ASC_SCSI_ID_BITS 4
+#define ASC_ALL_DEVICE_BIT_SET 0xFFFF
+
+#endif
+
+#ifdef ASC_WIDESCSI_32
+
+#undef ASC_SCSI_ID_BITS
+#define ASC_SCSI_ID_BITS 5
+#define ASC_ALL_DEVICE_BIT_SET 0xFFFFFFFFL
+
+#endif
+
+#if ASC_SCSI_ID_BITS == 3
+
+#define ASC_SCSI_BIT_ID_TYPE uchar
+#define ASC_MAX_TID 7
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFF
+
+#elif ASC_SCSI_ID_BITS == 4
+
+#define ASC_SCSI_BIT_ID_TYPE ushort
+#define ASC_MAX_TID 15
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFFFF
+
+#elif ASC_SCSI_ID_BITS == 5
+
+#define ASC_SCSI_BIT_ID_TYPE ulong
+#define ASC_MAX_TID 31
+#define ASC_MAX_LUN 7
+#define ASC_SCSI_WIDTH_BIT_SET 0xFFFFFFFF
+
+#else
+
+#error ASC_SCSI_ID_BITS definition is wrong
+
+#endif
+
+#define ASC_MAX_SENSE_LEN 32
+#define ASC_MIN_SENSE_LEN 14
+
+#define ASC_MAX_CDB_LEN 12
+
+#define SCSICMD_TestUnitReady 0x00
+#define SCSICMD_Rewind 0x01
+#define SCSICMD_Rezero 0x01
+#define SCSICMD_RequestSense 0x03
+#define SCSICMD_Format 0x04
+#define SCSICMD_FormatUnit 0x04
+#define SCSICMD_Read6 0x08
+#define SCSICMD_Write6 0x0A
+#define SCSICMD_Seek6 0x0B
+#define SCSICMD_Inquiry 0x12
+#define SCSICMD_Verify6 0x13
+#define SCSICMD_ModeSelect6 0x15
+#define SCSICMD_ModeSense6 0x1A
+
+#define SCSICMD_StartStopUnit 0x1B
+#define SCSICMD_LoadUnloadTape 0x1B
+#define SCSICMD_ReadCapacity 0x25
+#define SCSICMD_Read10 0x28
+#define SCSICMD_Write10 0x2A
+#define SCSICMD_Seek10 0x2B
+#define SCSICMD_Erase10 0x2C
+#define SCSICMD_WriteAndVerify10 0x2E
+#define SCSICMD_Verify10 0x2F
+
+#define SCSICMD_ModeSelect10 0x55
+#define SCSICMD_ModeSense10 0x5A
+
+#define SCSI_TYPE_DASD 0x00
+#define SCSI_TYPE_SASD 0x01
+#define SCSI_TYPE_PRN 0x02
+#define SCSI_TYPE_PROC 0x03
+
+#define SCSI_TYPE_WORM 0x04
+#define SCSI_TYPE_CDROM 0x05
+#define SCSI_TYPE_SCANNER 0x06
+#define SCSI_TYPE_OPTMEM 0x07
+#define SCSI_TYPE_MED_CHG 0x08
+#define SCSI_TYPE_COMM 0x09
+#define SCSI_TYPE_UNKNOWN 0x1F
+#define SCSI_TYPE_NO_DVC 0xFF
+
+#define ASC_SCSIDIR_NOCHK 0x00
+
+#define ASC_SCSIDIR_T2H 0x08
+
+#define ASC_SCSIDIR_H2T 0x10
+
+#define ASC_SCSIDIR_NODATA 0x18
+
+#define SCSI_SENKEY_NO_SENSE 0x00
+#define SCSI_SENKEY_UNDEFINED 0x01
+#define SCSI_SENKEY_NOT_READY 0x02
+#define SCSI_SENKEY_MEDIUM_ERR 0x03
+#define SCSI_SENKEY_HW_ERR 0x04
+#define SCSI_SENKEY_ILLEGAL 0x05
+#define SCSI_SENKEY_ATTENSION 0x06
+#define SCSI_SENKEY_PROTECTED 0x07
+#define SCSI_SENKEY_BLANK 0x08
+#define SCSI_SENKEY_V_UNIQUE 0x09
+#define SCSI_SENKEY_CPY_ABORT 0x0A
+#define SCSI_SENKEY_ABORT 0x0B
+#define SCSI_SENKEY_EQUAL 0x0C
+#define SCSI_SENKEY_VOL_OVERFLOW 0x0D
+#define SCSI_SENKEY_MISCOMP 0x0E
+#define SCSI_SENKEY_RESERVED 0x0F
+
+#define ASC_SRB_HOST( x ) ( ( uchar )( ( uchar )( x ) >> 4 ) )
+#define ASC_SRB_TID( x ) ( ( uchar )( ( uchar )( x ) & ( uchar )0x0F ) )
+
+#define ASC_SRB_LUN( x ) ( ( uchar )( ( uint )( x ) >> 13 ) )
+
+#define PUT_CDB1( x ) ( ( uchar )( ( uint )( x ) >> 8 ) )
+
+#define SS_GOOD 0x00
+#define SS_CHK_CONDITION 0x02
+#define SS_CONDITION_MET 0x04
+#define SS_TARGET_BUSY 0x08
+#define SS_INTERMID 0x10
+#define SS_INTERMID_COND_MET 0x14
+
+#define SS_RSERV_CONFLICT 0x18
+#define SS_CMD_TERMINATED 0x22
+
+#define SS_QUEUE_FULL 0x28
+
+#define MS_CMD_DONE 0x00
+#define MS_EXTEND 0x01
+#define MS_SDTR_LEN 0x03
+#define MS_SDTR_CODE 0x01
+
+#define M1_SAVE_DATA_PTR 0x02
+#define M1_RESTORE_PTRS 0x03
+#define M1_DISCONNECT 0x04
+#define M1_INIT_DETECTED_ERR 0x05
+#define M1_ABORT 0x06
+#define M1_MSG_REJECT 0x07
+#define M1_NO_OP 0x08
+#define M1_MSG_PARITY_ERR 0x09
+#define M1_LINK_CMD_DONE 0x0A
+#define M1_LINK_CMD_DONE_WFLAG 0x0B
+#define M1_BUS_DVC_RESET 0x0C
+#define M1_ABORT_TAG 0x0D
+#define M1_CLR_QUEUE 0x0E
+#define M1_INIT_RECOVERY 0x0F
+#define M1_RELEASE_RECOVERY 0x10
+#define M1_KILL_IO_PROC 0x11
+
+#define M2_QTAG_MSG_SIMPLE 0x20
+#define M2_QTAG_MSG_HEAD 0x21
+#define M2_QTAG_MSG_ORDERED 0x22
+#define M2_IGNORE_WIDE_RESIDUE 0x23
+
+typedef struct {
+ uchar peri_dvc_type:5;
+ uchar peri_qualifier:3;
+} ASC_SCSI_INQ0;
+
+typedef struct {
+ uchar dvc_type_modifier:7;
+ uchar rmb:1;
+} ASC_SCSI_INQ1;
+
+typedef struct {
+ uchar ansi_apr_ver:3;
+ uchar ecma_ver:3;
+ uchar iso_ver:2;
+} ASC_SCSI_INQ2;
+
+typedef struct {
+ uchar rsp_data_fmt:4;
+
+ uchar res:2;
+ uchar TemIOP:1;
+ uchar aenc:1;
+} ASC_SCSI_INQ3;
+
+typedef struct {
+ uchar StfRe:1;
+ uchar CmdQue:1;
+ uchar Reserved:1;
+ uchar Linked:1;
+ uchar Sync:1;
+ uchar WBus16:1;
+ uchar WBus32:1;
+ uchar RelAdr:1;
+} ASC_SCSI_INQ7;
+
+typedef struct {
+ ASC_SCSI_INQ0 byte0;
+ ASC_SCSI_INQ1 byte1;
+ ASC_SCSI_INQ2 byte2;
+ ASC_SCSI_INQ3 byte3;
+ uchar add_len;
+ uchar res1;
+ uchar res2;
+ ASC_SCSI_INQ7 byte7;
+ uchar vendor_id[8];
+ uchar product_id[16];
+ uchar product_rev_level[4];
+} ASC_SCSI_INQUIRY;
+
+typedef struct asc_req_sense {
+ uchar err_code:7;
+ uchar info_valid:1;
+ uchar segment_no;
+ uchar sense_key:4;
+ uchar reserved_bit:1;
+ uchar sense_ILI:1;
+ uchar sense_EOM:1;
+ uchar file_mark:1;
+ uchar info1[4];
+ uchar add_sense_len;
+ uchar cmd_sp_info[4];
+ uchar asc;
+ uchar ascq;
+
+ uchar fruc;
+ uchar sks_byte0:7;
+ uchar sks_valid:1;
+ uchar sks_bytes[2];
+ uchar notused[2];
+ uchar ex_sense_code;
+ uchar info2[4];
+} ASC_REQ_SENSE;
+
+#define ASC_SG_LIST_PER_Q 7
+
+#define QS_FREE 0x00
+#define QS_READY 0x01
+#define QS_DISC1 0x02
+#define QS_DISC2 0x04
+#define QS_BUSY 0x08
+
+#define QS_ABORTED 0x40
+#define QS_DONE 0x80
+
+#define QC_NO_CALLBACK 0x01
+
+#define QC_SG_SWAP_QUEUE 0x02
+#define QC_SG_HEAD 0x04
+#define QC_DATA_IN 0x08
+#define QC_DATA_OUT 0x10
+
+#define QC_URGENT 0x20
+#define QC_MSG_OUT 0x40
+#define QC_REQ_SENSE 0x80
+
+#define QCSG_SG_XFER_LIST 0x02
+#define QCSG_SG_XFER_MORE 0x04
+#define QCSG_SG_XFER_END 0x08
+
+#define QD_IN_PROGRESS 0x00
+#define QD_NO_ERROR 0x01
+#define QD_ABORTED_BY_HOST 0x02
+#define QD_WITH_ERROR 0x04
+#define QD_INVALID_REQUEST 0x80
+#define QD_INVALID_HOST_NUM 0x81
+#define QD_INVALID_DEVICE 0x82
+#define QD_ERR_INTERNAL 0xFF
+
+#define QHSTA_NO_ERROR 0x00
+#define QHSTA_M_SEL_TIMEOUT 0x11
+#define QHSTA_M_DATA_OVER_RUN 0x12
+#define QHSTA_M_DATA_UNDER_RUN 0x12
+#define QHSTA_M_UNEXPECTED_BUS_FREE 0x13
+#define QHSTA_M_BAD_BUS_PHASE_SEQ 0x14
+
+#define QHSTA_D_QDONE_SG_LIST_CORRUPTED 0x21
+#define QHSTA_D_ASC_DVC_ERROR_CODE_SET 0x22
+#define QHSTA_D_HOST_ABORT_FAILED 0x23
+#define QHSTA_D_EXE_SCSI_Q_FAILED 0x24
+#define QHSTA_D_EXE_SCSI_Q_BUSY_TIMEOUT 0x25
+
+#define QHSTA_D_ASPI_NO_BUF_POOL 0x26
+
+#define QHSTA_M_WTM_TIMEOUT 0x41
+#define QHSTA_M_BAD_CMPL_STATUS_IN 0x42
+#define QHSTA_M_NO_AUTO_REQ_SENSE 0x43
+#define QHSTA_M_AUTO_REQ_SENSE_FAIL 0x44
+#define QHSTA_M_TARGET_STATUS_BUSY 0x45
+#define QHSTA_M_BAD_TAG_CODE 0x46
+
+#define QHSTA_M_BAD_QUEUE_FULL_OR_BUSY 0x47
+
+#define QHSTA_D_LRAM_CMP_ERROR 0x81
+#define QHSTA_M_MICRO_CODE_ERROR_HALT 0xA1
+
+#define ASC_FLAG_SCSIQ_REQ 0x01
+#define ASC_FLAG_BIOS_SCSIQ_REQ 0x02
+#define ASC_FLAG_BIOS_ASYNC_IO 0x04
+#define ASC_FLAG_SRB_LINEAR_ADDR 0x08
+
+#define ASC_FLAG_WIN16 0x10
+#define ASC_FLAG_WIN32 0x20
+
+#define ASC_FLAG_DOS_VM_CALLBACK 0x80
+
+#define ASC_TAG_FLAG_ADD_ONE_BYTE 0x10
+#define ASC_TAG_FLAG_ISAPNP_ADD_BYTES 0x40
+
+#define ASC_SCSIQ_CPY_BEG 4
+#define ASC_SCSIQ_SGHD_CPY_BEG 2
+
+#define ASC_SCSIQ_B_FWD 0
+#define ASC_SCSIQ_B_BWD 1
+
+#define ASC_SCSIQ_B_STATUS 2
+#define ASC_SCSIQ_B_QNO 3
+
+#define ASC_SCSIQ_B_CNTL 4
+#define ASC_SCSIQ_B_SG_QUEUE_CNT 5
+
+#define ASC_SCSIQ_D_DATA_ADDR 8
+#define ASC_SCSIQ_D_DATA_CNT 12
+#define ASC_SCSIQ_B_SENSE_LEN 20
+#define ASC_SCSIQ_DONE_INFO_BEG 22
+#define ASC_SCSIQ_D_SRBPTR 22
+#define ASC_SCSIQ_B_TARGET_IX 26
+#define ASC_SCSIQ_B_CDB_LEN 28
+#define ASC_SCSIQ_B_TAG_CODE 29
+#define ASC_SCSIQ_W_VM_ID 30
+#define ASC_SCSIQ_DONE_STATUS 32
+#define ASC_SCSIQ_HOST_STATUS 33
+#define ASC_SCSIQ_SCSI_STATUS 34
+#define ASC_SCSIQ_CDB_BEG 36
+#define ASC_SCSIQ_DW_REMAIN_XFER_ADDR 56
+#define ASC_SCSIQ_DW_REMAIN_XFER_CNT 60
+#define ASC_SCSIQ_B_SG_WK_QP 49
+#define ASC_SCSIQ_B_SG_WK_IX 50
+#define ASC_SCSIQ_W_REQ_COUNT 52
+#define ASC_SCSIQ_B_LIST_CNT 6
+#define ASC_SCSIQ_B_CUR_LIST_CNT 7
+
+#define ASC_SGQ_B_SG_CNTL 4
+#define ASC_SGQ_B_SG_HEAD_QP 5
+#define ASC_SGQ_B_SG_LIST_CNT 6
+#define ASC_SGQ_B_SG_CUR_LIST_CNT 7
+#define ASC_SGQ_LIST_BEG 8
+
+#define ASC_DEF_SCSI1_QNG 2
+#define ASC_MAX_SCSI1_QNG 2
+#define ASC_DEF_SCSI2_QNG 16
+#define ASC_MAX_SCSI2_QNG 32
+
+#define ASC_TAG_CODE_MASK 0x23
+
+#define ASC_STOP_REQ_RISC_STOP 0x01
+
+#define ASC_STOP_ACK_RISC_STOP 0x03
+
+#define ASC_STOP_CLEAN_UP_BUSY_Q 0x10
+#define ASC_STOP_CLEAN_UP_DISC_Q 0x20
+#define ASC_STOP_HOST_REQ_RISC_HALT 0x40
+#define ASC_STOP_SEND_INT_TO_HOST 0x80
+
+#define ASC_TIDLUN_TO_IX( tid, lun ) ( ASC_SCSI_TIX_TYPE )( (tid) + ((lun)<<ASC_SCSI_ID_BITS) )
+
+#define ASC_TID_TO_TARGET_ID( tid ) ( ASC_SCSI_BIT_ID_TYPE )( 0x01 << (tid) )
+#define ASC_TIX_TO_TARGET_ID( tix ) ( 0x01 << ( (tix) & ASC_MAX_TID ) )
+#define ASC_TIX_TO_TID( tix ) ( (tix) & ASC_MAX_TID )
+#define ASC_TID_TO_TIX( tid ) ( (tid) & ASC_MAX_TID )
+#define ASC_TIX_TO_LUN( tix ) ( ( (tix) >> ASC_SCSI_ID_BITS ) & ASC_MAX_LUN )
+
+#define ASC_QNO_TO_QADDR( q_no ) ( (ASC_QADR_BEG)+( ( int )(q_no) << 6 ) )
+
+typedef struct asc_scisq_1 {
+ uchar status;
+ uchar q_no;
+ uchar cntl;
+ uchar sg_queue_cnt;
+
+ uchar target_id;
+ uchar target_lun;
+
+ ulong data_addr;
+ ulong data_cnt;
+ ulong sense_addr;
+ uchar sense_len;
+ uchar user_def;
+} ASC_SCSIQ_1;
+
+typedef struct asc_scisq_2 {
+ ulong srb_ptr;
+ uchar target_ix;
+
+ uchar flag;
+ uchar cdb_len;
+ uchar tag_code;
+
+ ushort vm_id;
+} ASC_SCSIQ_2;
+
+typedef struct asc_scsiq_3 {
+ uchar done_stat;
+ uchar host_stat;
+ uchar scsi_stat;
+ uchar scsi_msg;
+} ASC_SCSIQ_3;
+
+typedef struct asc_scsiq_4 {
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar y_first_sg_list_qp;
+ uchar y_working_sg_qp;
+ uchar y_working_sg_ix;
+ uchar y_cntl;
+ ushort x_req_count;
+ ushort x_reconnect_rtn;
+ ulong x_saved_data_addr;
+ ulong x_saved_data_cnt;
+} ASC_SCSIQ_4;
+
+typedef struct asc_q_done_info {
+ ASC_SCSIQ_2 d2;
+ ASC_SCSIQ_3 d3;
+ uchar q_status;
+ uchar q_no;
+ uchar cntl;
+ uchar sense_len;
+ uchar user_def;
+ uchar res;
+ ulong remain_bytes;
+} ASC_QDONE_INFO;
+
+typedef struct asc_sg_list {
+ ulong addr;
+ ulong bytes;
+} ASC_SG_LIST;
+
+typedef struct asc_sg_head {
+ uchar entry_cnt;
+
+ uchar queue_cnt;
+
+ uchar entry_to_copy;
+ uchar res;
+ ASC_SG_LIST sg_list[ASC_MAX_SG_LIST];
+} ASC_SG_HEAD;
+
+#define ASC_MIN_SG_LIST 2
+
+typedef struct asc_min_sg_head {
+ uchar entry_cnt;
+
+ uchar queue_cnt;
+
+ uchar entry_to_copy;
+ uchar res;
+ ASC_SG_LIST sg_list[ASC_MIN_SG_LIST];
+} ASC_MIN_SG_HEAD;
+
+#define QCX_SORT (0x0001)
+#define QCX_COALEASE (0x0002)
+
+#if CC_LINK_BUSY_Q
+typedef struct asc_ext_scsi_q {
+ ulong lba;
+ ushort lba_len;
+ struct asc_scsi_q dosfar *next;
+ struct asc_scsi_q dosfar *join;
+ ushort cntl;
+ ushort buffer_id;
+ uchar q_required;
+ uchar res;
+} ASC_EXT_SCSI_Q;
+
+#endif
+
+typedef struct asc_scsi_q {
+ ASC_SCSIQ_1 q1;
+ ASC_SCSIQ_2 q2;
+ uchar dosfar *cdbptr;
+
+ ASC_SG_HEAD dosfar *sg_head;
+
+#if CC_LINK_BUSY_Q
+ ASC_EXT_SCSI_Q ext;
+#endif
+
+} ASC_SCSI_Q;
+
+typedef struct asc_scsi_req_q {
+ ASC_SCSIQ_1 r1;
+ ASC_SCSIQ_2 r2;
+ uchar dosfar *cdbptr;
+ ASC_SG_HEAD dosfar *sg_head;
+
+#if CC_LINK_BUSY_Q
+ ASC_EXT_SCSI_Q ext;
+#endif
+
+ uchar dosfar *sense_ptr;
+
+ ASC_SCSIQ_3 r3;
+ uchar cdb[ASC_MAX_CDB_LEN];
+ uchar sense[ASC_MIN_SENSE_LEN];
+} ASC_SCSI_REQ_Q;
+
+typedef struct asc_risc_q {
+ uchar fwd;
+ uchar bwd;
+ ASC_SCSIQ_1 i1;
+ ASC_SCSIQ_2 i2;
+ ASC_SCSIQ_3 i3;
+ ASC_SCSIQ_4 i4;
+} ASC_RISC_Q;
+
+typedef struct asc_sg_list_q {
+
+ uchar seq_no;
+ uchar q_no;
+ uchar cntl;
+ uchar sg_head_qp;
+ uchar sg_list_cnt;
+ uchar sg_cur_list_cnt;
+
+} ASC_SG_LIST_Q;
+
+typedef struct asc_risc_sg_list_q {
+ uchar fwd;
+ uchar bwd;
+ ASC_SG_LIST_Q sg;
+ ASC_SG_LIST sg_list[7];
+} ASC_RISC_SG_LIST_Q;
+
+#define ASC_EXE_SCSI_IO_MAX_IDLE_LOOP 0x1000000UL
+#define ASC_EXE_SCSI_IO_MAX_WAIT_LOOP 1024
+
+#define ASCQ_ERR_NO_ERROR 0
+#define ASCQ_ERR_IO_NOT_FOUND 1
+#define ASCQ_ERR_LOCAL_MEM 2
+#define ASCQ_ERR_CHKSUM 3
+#define ASCQ_ERR_START_CHIP 4
+#define ASCQ_ERR_INT_TARGET_ID 5
+#define ASCQ_ERR_INT_LOCAL_MEM 6
+#define ASCQ_ERR_HALT_RISC 7
+#define ASCQ_ERR_GET_ASPI_ENTRY 8
+#define ASCQ_ERR_CLOSE_ASPI 9
+#define ASCQ_ERR_HOST_INQUIRY 0x0A
+#define ASCQ_ERR_SAVED_SRB_BAD 0x0B
+#define ASCQ_ERR_QCNTL_SG_LIST 0x0C
+#define ASCQ_ERR_Q_STATUS 0x0D
+#define ASCQ_ERR_WR_SCSIQ 0x0E
+#define ASCQ_ERR_PC_ADDR 0x0F
+#define ASCQ_ERR_SYN_OFFSET 0x10
+#define ASCQ_ERR_SYN_XFER_TIME 0x11
+#define ASCQ_ERR_LOCK_DMA 0x12
+#define ASCQ_ERR_UNLOCK_DMA 0x13
+#define ASCQ_ERR_VDS_CHK_INSTALL 0x14
+#define ASCQ_ERR_MICRO_CODE_HALT 0x15
+#define ASCQ_ERR_SET_LRAM_ADDR 0x16
+#define ASCQ_ERR_CUR_QNG 0x17
+#define ASCQ_ERR_SG_Q_LINKS 0x18
+#define ASCQ_ERR_SCSIQ_PTR 0x19
+#define ASCQ_ERR_ISR_RE_ENTRY 0x1A
+#define ASCQ_ERR_CRITICAL_RE_ENTRY 0x1B
+#define ASCQ_ERR_ISR_ON_CRITICAL 0x1C
+#define ASCQ_ERR_SG_LIST_ODD_ADDRESS 0x1D
+#define ASCQ_ERR_XFER_ADDRESS_TOO_BIG 0x1E
+#define ASCQ_ERR_SCSIQ_NULL_PTR 0x1F
+#define ASCQ_ERR_SCSIQ_BAD_NEXT_PTR 0x20
+#define ASCQ_ERR_GET_NUM_OF_FREE_Q 0x21
+#define ASCQ_ERR_SEND_SCSI_Q 0x22
+#define ASCQ_ERR_HOST_REQ_RISC_HALT 0x23
+#define ASCQ_ERR_RESET_SDTR 0x24
+
+#define ASC_WARN_NO_ERROR 0x0000
+#define ASC_WARN_IO_PORT_ROTATE 0x0001
+#define ASC_WARN_EEPROM_CHKSUM 0x0002
+#define ASC_WARN_IRQ_MODIFIED 0x0004
+#define ASC_WARN_AUTO_CONFIG 0x0008
+#define ASC_WARN_CMD_QNG_CONFLICT 0x0010
+
+#define ASC_WARN_EEPROM_RECOVER 0x0020
+#define ASC_WARN_CFG_MSW_RECOVER 0x0040
+
+#define ASC_IERR_WRITE_EEPROM 0x0001
+#define ASC_IERR_MCODE_CHKSUM 0x0002
+#define ASC_IERR_SET_PC_ADDR 0x0004
+#define ASC_IERR_START_STOP_CHIP 0x0008
+
+#define ASC_IERR_IRQ_NO 0x0010
+
+#define ASC_IERR_SET_IRQ_NO 0x0020
+#define ASC_IERR_CHIP_VERSION 0x0040
+#define ASC_IERR_SET_SCSI_ID 0x0080
+#define ASC_IERR_GET_PHY_ADDR 0x0100
+#define ASC_IERR_BAD_SIGNATURE 0x0200
+#define ASC_IERR_NO_BUS_TYPE 0x0400
+#define ASC_IERR_SCAM 0x0800
+#define ASC_IERR_SET_SDTR 0x1000
+#define ASC_IERR_RW_LRAM 0x8000
+
+#define ASC_DEF_IRQ_NO 10
+#define ASC_MAX_IRQ_NO 15
+#define ASC_MIN_IRQ_NO 10
+
+#define ASC_MIN_REMAIN_Q (0x02)
+#define ASC_DEF_MAX_TOTAL_QNG (0x40)
+
+#define ASC_MIN_TAG_Q_PER_DVC (0x04)
+#define ASC_DEF_TAG_Q_PER_DVC (0x04)
+
+#define ASC_MIN_FREE_Q ASC_MIN_REMAIN_Q
+
+#define ASC_MIN_TOTAL_QNG (( ASC_MAX_SG_QUEUE )+( ASC_MIN_FREE_Q ))
+
+#define ASC_MAX_TOTAL_QNG 240
+#define ASC_MAX_PCI_INRAM_TOTAL_QNG 20
+
+#define ASC_MAX_INRAM_TAG_QNG 16
+
+typedef struct asc_dvc_cfg {
+ ASC_SCSI_BIT_ID_TYPE can_tagged_qng;
+
+ ASC_SCSI_BIT_ID_TYPE cmd_qng_enabled;
+ ASC_SCSI_BIT_ID_TYPE disc_enable;
+ uchar res;
+ uchar chip_scsi_id:4;
+
+ uchar isa_dma_speed:4;
+
+ uchar isa_dma_channel;
+ uchar chip_version;
+ ushort pci_device_id;
+ ushort lib_serial_no;
+ ushort lib_version;
+ ushort mcode_date;
+ ushort mcode_version;
+ uchar sdtr_data[ASC_MAX_TID + 1];
+ uchar max_tag_qng[ASC_MAX_TID + 1];
+ uchar dosfar *overrun_buf;
+
+} ASC_DVC_CFG;
+
+#define ASC_DEF_DVC_CNTL 0xFFFF
+#define ASC_DEF_CHIP_SCSI_ID 7
+#define ASC_DEF_ISA_DMA_SPEED 4
+
+#define ASC_INIT_STATE_NULL 0x0000
+#define ASC_INIT_STATE_BEG_GET_CFG 0x0001
+#define ASC_INIT_STATE_END_GET_CFG 0x0002
+#define ASC_INIT_STATE_BEG_SET_CFG 0x0004
+#define ASC_INIT_STATE_END_SET_CFG 0x0008
+#define ASC_INIT_STATE_BEG_LOAD_MC 0x0010
+#define ASC_INIT_STATE_END_LOAD_MC 0x0020
+#define ASC_INIT_STATE_BEG_INQUIRY 0x0040
+#define ASC_INIT_STATE_END_INQUIRY 0x0080
+#define ASC_INIT_RESET_SCSI_DONE 0x0100
+
+#define ASC_PCI_DEVICE_ID_REV_A 0x1100
+#define ASC_PCI_DEVICE_ID_REV_B 0x1200
+
+#define ASC_BUG_FIX_ADD_ONE_BYTE 0x0001
+
+#define ASYN_SDTR_DATA_FIX_PCI_REV_AB 0x41
+
+#define ASC_MIN_TAGGED_CMD 7
+
+typedef struct asc_dvc_var {
+ PortAddr iop_base;
+ ushort err_code;
+ ushort dvc_cntl;
+ ushort bug_fix_cntl;
+ ushort bus_type;
+ Ptr2Func isr_callback;
+ Ptr2Func exe_callback;
+
+ ASC_SCSI_BIT_ID_TYPE init_sdtr;
+
+ ASC_SCSI_BIT_ID_TYPE sdtr_done;
+
+ ASC_SCSI_BIT_ID_TYPE use_tagged_qng;
+
+ ASC_SCSI_BIT_ID_TYPE unit_not_ready;
+
+ ASC_SCSI_BIT_ID_TYPE queue_full_or_busy;
+
+ ASC_SCSI_BIT_ID_TYPE start_motor;
+ uchar scsi_reset_wait;
+ uchar chip_no;
+
+ char is_in_int;
+ uchar max_total_qng;
+
+ uchar cur_total_qng;
+
+ uchar in_critical_cnt;
+
+ uchar irq_no;
+ uchar last_q_shortage;
+
+ ushort init_state;
+ uchar cur_dvc_qng[ASC_MAX_TID + 1];
+ uchar max_dvc_qng[ASC_MAX_TID + 1];
+
+ ASC_SCSI_Q dosfar *scsiq_busy_head[ASC_MAX_TID + 1];
+ ASC_SCSI_Q dosfar *scsiq_busy_tail[ASC_MAX_TID + 1];
+
+ ulong int_count;
+ ulong req_count;
+ ulong busy_count;
+
+ ASC_DVC_CFG dosfar *cfg;
+ Ptr2Func saved_ptr2func;
+ ulong reserved2;
+ ulong reserved3;
+ ulong max_dma_count;
+ ASC_SCSI_BIT_ID_TYPE no_scam;
+ ASC_SCSI_BIT_ID_TYPE pci_fix_asyn_xfer;
+} ASC_DVC_VAR;
+
+typedef int (dosfar * ASC_ISR_CALLBACK) (ASC_DVC_VAR asc_ptr_type *, ASC_QDONE_INFO dosfar *);
+typedef int (dosfar * ASC_EXE_CALLBACK) (ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q dosfar *);
+
+typedef struct asc_dvc_inq_info {
+ uchar type[ASC_MAX_TID + 1][ASC_MAX_LUN + 1];
+} ASC_DVC_INQ_INFO;
+
+typedef struct asc_cap_info {
+ ulong lba;
+ ulong blk_size;
+} ASC_CAP_INFO;
+
+typedef struct asc_cap_info_array {
+ ASC_CAP_INFO cap_info[ASC_MAX_TID + 1][ASC_MAX_LUN + 1];
+} ASC_CAP_INFO_ARRAY;
+
+#define ASC_IOADR_TABLE_MAX_IX 11
+#define ASC_IOADR_GAP 0x10
+#define ASC_SEARCH_IOP_GAP 0x10
+#define ASC_MIN_IOP_ADDR ( PortAddr )0x0100
+#define ASC_MAX_IOP_ADDR ( PortAddr )0x3F0
+
+#define ASC_IOADR_1 ( PortAddr )0x0110
+#define ASC_IOADR_2 ( PortAddr )0x0130
+#define ASC_IOADR_3 ( PortAddr )0x0150
+#define ASC_IOADR_4 ( PortAddr )0x0190
+#define ASC_IOADR_5 ( PortAddr )0x0210
+#define ASC_IOADR_6 ( PortAddr )0x0230
+#define ASC_IOADR_7 ( PortAddr )0x0250
+#define ASC_IOADR_8 ( PortAddr )0x0330
+#define ASC_IOADR_DEF ASC_IOADR_8
+
+#define ASC_SYN_XFER_NO 8
+#define ASC_MAX_SDTR_PERIOD_INDEX 7
+#define ASC_SYN_MAX_OFFSET 0x0F
+#define ASC_DEF_SDTR_OFFSET 0x0F
+#define ASC_DEF_SDTR_INDEX 0x00
+
+#define SYN_XFER_NS_0 25
+#define SYN_XFER_NS_1 30
+#define SYN_XFER_NS_2 35
+#define SYN_XFER_NS_3 40
+#define SYN_XFER_NS_4 50
+#define SYN_XFER_NS_5 60
+#define SYN_XFER_NS_6 70
+#define SYN_XFER_NS_7 85
+
+#define ASC_SDTR_PERIOD_IX_MIN 7
+
+#define SYN_XMSG_WLEN 3
+
+typedef struct sdtr_xmsg {
+ uchar msg_type;
+ uchar msg_len;
+ uchar msg_req;
+ uchar xfer_period;
+ uchar req_ack_offset;
+ uchar res;
+} SDTR_XMSG;
+
+#define ASC_MCNTL_NO_SEL_TIMEOUT ( ushort )0x0001
+#define ASC_MCNTL_NULL_TARGET ( ushort )0x0002
+
+#define ASC_CNTL_INITIATOR ( ushort )0x0001
+#define ASC_CNTL_BIOS_GT_1GB ( ushort )0x0002
+#define ASC_CNTL_BIOS_GT_2_DISK ( ushort )0x0004
+#define ASC_CNTL_BIOS_REMOVABLE ( ushort )0x0008
+#define ASC_CNTL_NO_SCAM ( ushort )0x0010
+#define ASC_CNTL_NO_PCI_FIX_ASYN_XFER ( ushort )0x0020
+
+#define ASC_CNTL_INT_MULTI_Q ( ushort )0x0080
+
+#define ASC_CNTL_NO_LUN_SUPPORT ( ushort )0x0040
+
+#define ASC_CNTL_NO_VERIFY_COPY ( ushort )0x0100
+#define ASC_CNTL_RESET_SCSI ( ushort )0x0200
+#define ASC_CNTL_INIT_INQUIRY ( ushort )0x0400
+#define ASC_CNTL_INIT_VERBOSE ( ushort )0x0800
+
+#define ASC_CNTL_SCSI_PARITY ( ushort )0x1000
+#define ASC_CNTL_BURST_MODE ( ushort )0x2000
+
+#define ASC_CNTL_USE_8_IOP_BASE ( ushort )0x4000
+
+#define ASC_EEP_DVC_CFG_BEG_VL 2
+#define ASC_EEP_MAX_DVC_ADDR_VL 15
+
+#define ASC_EEP_DVC_CFG_BEG 32
+#define ASC_EEP_MAX_DVC_ADDR 45
+
+#define ASC_EEP_DEFINED_WORDS 10
+#define ASC_EEP_MAX_ADDR 63
+#define ASC_EEP_RES_WORDS 0
+#define ASC_EEP_MAX_RETRY 20
+#define ASC_MAX_INIT_BUSY_RETRY 8
+
+#define ASC_EEP_ISA_PNP_WSIZE 16
+
+typedef struct asceep_config {
+ ushort cfg_lsw;
+ ushort cfg_msw;
+
+ uchar init_sdtr;
+ uchar disc_enable;
+
+ uchar use_cmd_qng;
+
+ uchar start_motor;
+ uchar max_total_qng;
+ uchar max_tag_qng;
+ uchar bios_scan;
+
+ uchar power_up_wait;
+
+ uchar no_scam;
+ uchar chip_scsi_id:4;
+
+ uchar isa_dma_speed:4;
+
+ uchar sdtr_data[ASC_MAX_TID + 1];
+
+ uchar adapter_info[6];
+
+ ushort cntl;
+
+ ushort chksum;
+} ASCEEP_CONFIG;
+
+#define ASC_EEP_CMD_READ 0x80
+#define ASC_EEP_CMD_WRITE 0x40
+#define ASC_EEP_CMD_WRITE_ABLE 0x30
+#define ASC_EEP_CMD_WRITE_DISABLE 0x00
+
+#define ASC_OVERRUN_BSIZE 0x00000048UL
+
+#define ASCV_MSGOUT_BEG 0x0000
+#define ASCV_MSGOUT_SDTR_PERIOD (ASCV_MSGOUT_BEG+3)
+#define ASCV_MSGOUT_SDTR_OFFSET (ASCV_MSGOUT_BEG+4)
+
+#define ASCV_MSGIN_BEG (ASCV_MSGOUT_BEG+8)
+#define ASCV_MSGIN_SDTR_PERIOD (ASCV_MSGIN_BEG+3)
+#define ASCV_MSGIN_SDTR_OFFSET (ASCV_MSGIN_BEG+4)
+
+#define ASCV_SDTR_DATA_BEG (ASCV_MSGIN_BEG+8)
+#define ASCV_SDTR_DONE_BEG (ASCV_SDTR_DATA_BEG+8)
+#define ASCV_MAX_DVC_QNG_BEG ( ushort )0x0020
+
+#define ASCV_ASCDVC_ERR_CODE_W ( ushort )0x0030
+#define ASCV_MCODE_CHKSUM_W ( ushort )0x0032
+#define ASCV_MCODE_SIZE_W ( ushort )0x0034
+#define ASCV_STOP_CODE_B ( ushort )0x0036
+#define ASCV_DVC_ERR_CODE_B ( ushort )0x0037
+
+#define ASCV_OVERRUN_PADDR_D ( ushort )0x0038
+#define ASCV_OVERRUN_BSIZE_D ( ushort )0x003C
+
+#define ASCV_HALTCODE_W ( ushort )0x0040
+#define ASCV_CHKSUM_W ( ushort )0x0042
+#define ASCV_MC_DATE_W ( ushort )0x0044
+#define ASCV_MC_VER_W ( ushort )0x0046
+#define ASCV_NEXTRDY_B ( ushort )0x0048
+#define ASCV_DONENEXT_B ( ushort )0x0049
+#define ASCV_USE_TAGGED_QNG_B ( ushort )0x004A
+#define ASCV_SCSIBUSY_B ( ushort )0x004B
+#define ASCV_CDBCNT_B ( ushort )0x004C
+#define ASCV_CURCDB_B ( ushort )0x004D
+#define ASCV_RCLUN_B ( ushort )0x004E
+#define ASCV_BUSY_QHEAD_B ( ushort )0x004F
+#define ASCV_DISC1_QHEAD_B ( ushort )0x0050
+
+#define ASCV_DISC_ENABLE_B ( ushort )0x0052
+#define ASCV_CAN_TAGGED_QNG_B ( ushort )0x0053
+#define ASCV_HOSTSCSI_ID_B ( ushort )0x0055
+#define ASCV_MCODE_CNTL_B ( ushort )0x0056
+#define ASCV_NULL_TARGET_B ( ushort )0x0057
+
+#define ASCV_FREE_Q_HEAD_W ( ushort )0x0058
+#define ASCV_DONE_Q_TAIL_W ( ushort )0x005A
+#define ASCV_FREE_Q_HEAD_B ( ushort )(ASCV_FREE_Q_HEAD_W+1)
+#define ASCV_DONE_Q_TAIL_B ( ushort )(ASCV_DONE_Q_TAIL_W+1)
+
+#define ASCV_HOST_FLAG_B ( ushort )0x005D
+
+#define ASCV_TOTAL_READY_Q_B ( ushort )0x0064
+#define ASCV_VER_SERIAL_B ( ushort )0x0065
+#define ASCV_HALTCODE_SAVED_W ( ushort )0x0066
+#define ASCV_WTM_FLAG_B ( ushort )0x0068
+#define ASCV_RISC_FLAG_B ( ushort )0x006A
+#define ASCV_REQ_SG_LIST_QP ( ushort )0x006B
+
+#define ASC_HOST_FLAG_IN_ISR 0x01
+#define ASC_HOST_FLAG_ACK_INT 0x02
+
+#define ASC_RISC_FLAG_GEN_INT 0x01
+#define ASC_RISC_FLAG_REQ_SG_LIST 0x02
+
+#define IOP_CTRL (0x0F)
+#define IOP_STATUS (0x0E)
+#define IOP_INT_ACK IOP_STATUS
+
+#define IOP_REG_IFC (0x0D)
+
+#define IOP_SYN_OFFSET (0x0B)
+#define IOP_REG_PC (0x0C)
+#define IOP_RAM_ADDR (0x0A)
+#define IOP_RAM_DATA (0x08)
+#define IOP_EEP_DATA (0x06)
+#define IOP_EEP_CMD (0x07)
+
+#define IOP_VERSION (0x03)
+#define IOP_CONFIG_HIGH (0x04)
+#define IOP_CONFIG_LOW (0x02)
+#define IOP_ASPI_ID_LOW (0x01)
+#define IOP_ASPI_ID_HIGH (0x00)
+
+#define IOP_REG_DC1 (0x0E)
+#define IOP_REG_DC0 (0x0C)
+#define IOP_REG_SB (0x0B)
+#define IOP_REG_DA1 (0x0A)
+#define IOP_REG_DA0 (0x08)
+#define IOP_REG_SC (0x09)
+#define IOP_DMA_SPEED (0x07)
+#define IOP_REG_FLAG (0x07)
+#define IOP_FIFO_H (0x06)
+#define IOP_FIFO_L (0x04)
+#define IOP_REG_ID (0x05)
+#define IOP_REG_QP (0x03)
+#define IOP_REG_IH (0x02)
+#define IOP_REG_IX (0x01)
+#define IOP_REG_AX (0x00)
+
+#define IFC_REG_LOCK (0x00)
+#define IFC_REG_UNLOCK (0x09)
+
+#define IFC_WR_EN_FILTER (0x10)
+#define IFC_RD_NO_EEPROM (0x10)
+#define IFC_SLEW_RATE (0x20)
+#define IFC_ACT_NEG (0x40)
+#define IFC_INP_FILTER (0x80)
+
+#define IFC_INIT_DEFAULT ( IFC_ACT_NEG | IFC_REG_UNLOCK )
+
+#define SC_SEL (0x80)
+#define SC_BSY (0x40)
+#define SC_ACK (0x20)
+#define SC_REQ (0x10)
+#define SC_ATN (0x08)
+#define SC_IO (0x04)
+#define SC_CD (0x02)
+#define SC_MSG (0x01)
+
+#define AscGetVarFreeQHead( port ) AscReadLramWord( port, ASCV_FREE_Q_HEAD_W )
+#define AscGetVarDoneQTail( port ) AscReadLramWord( port, ASCV_DONE_Q_TAIL_W )
+#define AscPutVarFreeQHead( port, val ) AscWriteLramWord( port, ASCV_FREE_Q_HEAD_W, val )
+#define AscPutVarDoneQTail( port, val ) AscWriteLramWord( port, ASCV_DONE_Q_TAIL_W, val )
+
+#define AscGetRiscVarFreeQHead( port ) AscReadLramByte( port, ASCV_NEXTRDY_B )
+#define AscGetRiscVarDoneQTail( port ) AscReadLramByte( port, ASCV_DONENEXT_B )
+#define AscPutRiscVarFreeQHead( port, val ) AscWriteLramByte( port, ASCV_NEXTRDY_B, val )
+#define AscPutRiscVarDoneQTail( port, val ) AscWriteLramByte( port, ASCV_DONENEXT_B, val )
+
+#define AscGetChipIFC( port ) inp( (port)+IOP_REG_IFC )
+#define AscPutChipIFC( port, data ) outp( (port)+IOP_REG_IFC, data )
+
+#define AscGetChipLramAddr( port ) ( ushort )inpw( ( PortAddr )((port)+IOP_RAM_ADDR) )
+#define AscSetChipLramAddr( port, addr ) outpw( ( PortAddr )( (port)+IOP_RAM_ADDR ), addr )
+#define AscPutChipLramData( port, data ) outpw( (port)+IOP_RAM_DATA, data )
+#define AscGetChipLramData( port ) inpw( (port)+IOP_RAM_DATA )
+
+#define AscWriteChipSyn( port, data ) outp( (port)+IOP_SYN_OFFSET, data )
+#define AscReadChipSyn( port ) inp( (port)+IOP_SYN_OFFSET )
+
+#define AscWriteChipIH( port, data ) outpw( (port)+IOP_REG_IH, data )
+#define AscReadChipIH( port ) inpw( (port)+IOP_REG_IH )
+
+#define AscWriteChipScsiID( port, data ) outp( (port)+IOP_REG_ID, data )
+#define AscReadChipScsiID( port ) inp( (port)+IOP_REG_ID )
+
+#define AscGetChipDmaSpeed( port ) ( uchar )inp( (port)+IOP_DMA_SPEED )
+#define AscSetChipDmaSpeed( port, data ) outp( (port)+IOP_DMA_SPEED, data )
+#define AscGetChipQP( port ) ( uchar )inp( (port)+IOP_REG_QP )
+#define AscSetPCAddr( port, data ) outpw( (port)+IOP_REG_PC, data )
+#define AscGetPCAddr( port ) inpw( (port)+IOP_REG_PC )
+#define AscGetChipVerNo( port ) ( uchar )inp( (port)+IOP_VERSION )
+
+#define AscGetChipEEPCmd( port ) ( uchar )inp( (port)+IOP_EEP_CMD )
+#define AscSetChipEEPCmd( port, data ) outp( (port)+IOP_EEP_CMD, data )
+#define AscGetChipEEPData( port ) inpw( (port)+IOP_EEP_DATA )
+#define AscSetChipEEPData( port, data ) outpw( (port)+IOP_EEP_DATA, data )
+
+#define AscGetChipControl( port ) ( uchar )inp( (port)+IOP_CTRL )
+#define AscSetChipControl( port, cc_val ) outp( (port)+IOP_CTRL, cc_val )
+
+#define AscGetChipStatus( port ) ( ASC_CS_TYPE )inpw( (port)+IOP_STATUS )
+#define AscSetChipStatus( port, cs_val ) outpw( (port)+IOP_STATUS, cs_val )
+
+#define AscGetChipCfgLsw( port ) ( ushort )inpw( (port)+IOP_CONFIG_LOW )
+#define AscGetChipCfgMsw( port ) ( ushort )inpw( (port)+IOP_CONFIG_HIGH )
+#define AscSetChipCfgLsw( port, data ) outpw( (port)+IOP_CONFIG_LOW, data )
+#define AscSetChipCfgMsw( port, data ) outpw( (port)+IOP_CONFIG_HIGH, data )
+
+#define AscIsIntPending( port ) ( AscGetChipStatus( port ) & CSW_INT_PENDING )
+#define AscGetChipScsiID( port ) ( ( AscGetChipCfgLsw( port ) >> 8 ) & ASC_MAX_TID )
+
+#define ASC_HALT_EXTMSG_IN ( ushort )0x8000
+#define ASC_HALT_CHK_CONDITION ( ushort )0x8100
+#define ASC_HALT_SS_QUEUE_FULL ( ushort )0x8200
+#define ASC_HALT_SDTR_REJECTED ( ushort )0x4000
+
+#define ASC_MAX_QNO 0xF8
+#define ASC_DATA_SEC_BEG ( ushort )0x0080
+#define ASC_DATA_SEC_END ( ushort )0x0080
+#define ASC_CODE_SEC_BEG ( ushort )0x0080
+#define ASC_CODE_SEC_END ( ushort )0x0080
+#define ASC_QADR_BEG (0x4000)
+#define ASC_QADR_USED ( ushort )( ASC_MAX_QNO * 64 )
+#define ASC_QADR_END ( ushort )0x7FFF
+#define ASC_QLAST_ADR ( ushort )0x7FC0
+#define ASC_QBLK_SIZE 0x40
+#define ASC_BIOS_DATA_QBEG 0xF8
+
+#define ASC_MIN_ACTIVE_QNO 0x01
+
+#define ASC_QLINK_END 0xFF
+#define ASC_EEPROM_WORDS 0x10
+#define ASC_MAX_MGS_LEN 0x10
+
+#define ASC_BIOS_ADDR_DEF 0xDC00
+#define ASC_BIOS_SIZE 0x3800
+#define ASC_BIOS_RAM_OFF 0x3800
+#define ASC_BIOS_RAM_SIZE 0x800
+#define ASC_BIOS_MIN_ADDR 0xC000
+#define ASC_BIOS_MAX_ADDR 0xEC00
+#define ASC_BIOS_BANK_SIZE 0x0400
+
+#define ASC_MCODE_START_ADDR 0x0080
+
+#define ASC_CFG0_HOST_INT_ON 0x0020
+#define ASC_CFG0_BIOS_ON 0x0040
+#define ASC_CFG0_VERA_BURST_ON 0x0080
+#define ASC_CFG0_SCSI_PARITY_ON 0x0800
+
+#define ASC_CFG1_SCSI_TARGET_ON 0x0080
+#define ASC_CFG1_LRAM_8BITS_ON 0x0800
+
+#define ASC_CFG_MSW_CLR_MASK 0xF0C0
+
+#define CSW_TEST1 ( ASC_CS_TYPE )0x8000
+#define CSW_AUTO_CONFIG ( ASC_CS_TYPE )0x4000
+#define CSW_RESERVED1 ( ASC_CS_TYPE )0x2000
+#define CSW_IRQ_WRITTEN ( ASC_CS_TYPE )0x1000
+#define CSW_33MHZ_SELECTED ( ASC_CS_TYPE )0x0800
+#define CSW_TEST2 ( ASC_CS_TYPE )0x0400
+#define CSW_TEST3 ( ASC_CS_TYPE )0x0200
+#define CSW_RESERVED2 ( ASC_CS_TYPE )0x0100
+#define CSW_DMA_DONE ( ASC_CS_TYPE )0x0080
+#define CSW_FIFO_RDY ( ASC_CS_TYPE )0x0040
+
+#define CSW_EEP_READ_DONE ( ASC_CS_TYPE )0x0020
+
+#define CSW_HALTED ( ASC_CS_TYPE )0x0010
+#define CSW_SCSI_RESET_ACTIVE ( ASC_CS_TYPE )0x0008
+
+#define CSW_PARITY_ERR ( ASC_CS_TYPE )0x0004
+#define CSW_SCSI_RESET_LATCH ( ASC_CS_TYPE )0x0002
+
+#define CSW_INT_PENDING ( ASC_CS_TYPE )0x0001
+
+#define CIW_INT_ACK ( ASC_CS_TYPE )0x0100
+#define CIW_TEST1 ( ASC_CS_TYPE )0x0200
+#define CIW_TEST2 ( ASC_CS_TYPE )0x0400
+#define CIW_SEL_33MHZ ( ASC_CS_TYPE )0x0800
+
+#define CIW_IRQ_ACT ( ASC_CS_TYPE )0x1000
+
+#define CC_CHIP_RESET ( uchar )0x80
+#define CC_SCSI_RESET ( uchar )0x40
+#define CC_HALT ( uchar )0x20
+#define CC_SINGLE_STEP ( uchar )0x10
+#define CC_DMA_ABLE ( uchar )0x08
+#define CC_TEST ( uchar )0x04
+#define CC_BANK_ONE ( uchar )0x02
+#define CC_DIAG ( uchar )0x01
+
+#define ASC_1000_ID0W 0x04C1
+#define ASC_1000_ID0W_FIX 0x00C1
+#define ASC_1000_ID1B 0x25
+
+#define ASC_EISA_BIG_IOP_GAP (0x1C30-0x0C50)
+#define ASC_EISA_SMALL_IOP_GAP (0x0020)
+#define ASC_EISA_MIN_IOP_ADDR (0x0C30)
+#define ASC_EISA_MAX_IOP_ADDR (0xFC50)
+#define ASC_EISA_REV_IOP_MASK (0x0C83)
+#define ASC_EISA_PID_IOP_MASK (0x0C80)
+#define ASC_EISA_CFG_IOP_MASK (0x0C86)
+
+#define ASC_GET_EISA_SLOT( iop ) ( PortAddr )( (iop) & 0xF000 )
+
+#define ASC_EISA_ID_740 0x01745004UL
+#define ASC_EISA_ID_750 0x01755004UL
+
+#define INS_HALTINT ( ushort )0x6281
+#define INS_HALT ( ushort )0x6280
+#define INS_SINT ( ushort )0x6200
+#define INS_RFLAG_WTM ( ushort )0x7380
+
+#define ASC_MC_SAVE_CODE_WSIZE 0x500
+#define ASC_MC_SAVE_DATA_WSIZE 0x40
+
+typedef struct asc_mc_saved {
+ ushort data[ASC_MC_SAVE_DATA_WSIZE];
+ ushort code[ASC_MC_SAVE_CODE_WSIZE];
+} ASC_MC_SAVED;
+
+int AscWriteEEPCmdReg(PortAddr iop_base, uchar cmd_reg);
+int AscWriteEEPDataReg(PortAddr iop_base, ushort data_reg);
+void AscWaitEEPRead(void);
+void AscWaitEEPWrite(void);
+ushort AscReadEEPWord(PortAddr, uchar);
+ushort AscWriteEEPWord(PortAddr, uchar, ushort);
+ushort AscGetEEPConfig(PortAddr, ASCEEP_CONFIG dosfar *, ushort);
+int AscSetEEPConfigOnce(PortAddr, ASCEEP_CONFIG dosfar *, ushort);
+int AscSetEEPConfig(PortAddr, ASCEEP_CONFIG dosfar *, ushort);
+ushort AscEEPSum(PortAddr, uchar, uchar);
+
+int AscStartChip(PortAddr);
+int AscStopChip(PortAddr);
+void AscSetChipIH(PortAddr, ushort);
+int AscSetRunChipSynRegAtID(PortAddr, uchar, uchar);
+
+int AscIsChipHalted(PortAddr);
+
+void AscSetChipCfgDword(PortAddr, ulong);
+ulong AscGetChipCfgDword(PortAddr);
+
+void AscAckInterrupt(PortAddr);
+void AscDisableInterrupt(PortAddr);
+void AscEnableInterrupt(PortAddr);
+void AscSetBank(PortAddr, uchar);
+uchar AscGetBank(PortAddr);
+int AscResetChipAndScsiBus(PortAddr);
+ushort AscGetIsaDmaChannel(PortAddr);
+ushort AscSetIsaDmaChannel(PortAddr, ushort);
+uchar AscSetIsaDmaSpeed(PortAddr, uchar);
+uchar AscGetIsaDmaSpeed(PortAddr);
+
+uchar AscReadLramByte(PortAddr, ushort);
+ushort AscReadLramWord(PortAddr, ushort);
+ulong AscReadLramDWord(PortAddr, ushort);
+void AscWriteLramWord(PortAddr, ushort, ushort);
+void AscWriteLramDWord(PortAddr, ushort, ulong);
+void AscWriteLramByte(PortAddr, ushort, uchar);
+int AscVerWriteLramDWord(PortAddr, ushort, ulong);
+int AscVerWriteLramWord(PortAddr, ushort, ushort);
+int AscVerWriteLramByte(PortAddr, ushort, uchar);
+
+ulong AscMemSumLramWord(PortAddr, ushort, int);
+void AscMemWordSetLram(PortAddr, ushort, ushort, int);
+void AscMemWordCopyToLram(PortAddr, ushort, ushort dosfar *, int);
+void AscMemDWordCopyToLram(PortAddr, ushort, ulong dosfar *, int);
+void AscMemWordCopyFromLram(PortAddr, ushort, ushort dosfar *, int);
+int AscMemWordCmpToLram(PortAddr, ushort, ushort dosfar *, int);
+
+ushort AscInitAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
+ulong AscLoadMicroCode(PortAddr, ushort,
+ ushort dosfar *, ushort);
+ushort AscInitFromEEP(ASC_DVC_VAR asc_ptr_type *);
+ushort AscInitFromAscDvcVar(ASC_DVC_VAR asc_ptr_type *);
+ushort AscInitMicroCodeVar(ASC_DVC_VAR asc_ptr_type * asc_dvc);
+
+void dosfar AscInitPollIsrCallBack(ASC_DVC_VAR asc_ptr_type *,
+ ASC_QDONE_INFO dosfar *);
+int AscTestExternalLram(ASC_DVC_VAR asc_ptr_type *);
+ushort AscTestLramEndian(PortAddr);
+
+uchar AscMsgOutSDTR(PortAddr, uchar, uchar);
+
+uchar AscCalSDTRData(uchar, uchar);
+void AscSetChipSDTR(PortAddr, uchar, uchar);
+int AscInitChipAllSynReg(ASC_DVC_VAR asc_ptr_type *, uchar);
+uchar AscGetSynPeriodIndex(uchar);
+uchar AscSynIndexToPeriod(uchar);
+uchar AscAllocFreeQueue(PortAddr, uchar);
+uchar AscAllocMultipleFreeQueue(PortAddr, uchar, uchar);
+int AscRiscHaltedAbortSRB(ASC_DVC_VAR asc_ptr_type *, ulong);
+int AscRiscHaltedAbortTIX(ASC_DVC_VAR asc_ptr_type *, uchar);
+int AscRiscHaltedAbortALL(ASC_DVC_VAR asc_ptr_type *);
+int AscHostReqRiscHalt(PortAddr);
+int AscStopQueueExe(PortAddr);
+int AscStartQueueExe(PortAddr);
+int AscCleanUpDiscQueue(PortAddr);
+int AscCleanUpBusyQueue(PortAddr);
+int _AscAbortTidBusyQueue(ASC_DVC_VAR asc_ptr_type *,
+ ASC_QDONE_INFO dosfar *, uchar);
+int _AscAbortSrbBusyQueue(ASC_DVC_VAR asc_ptr_type *,
+ ASC_QDONE_INFO dosfar *, ulong);
+int AscWaitTixISRDone(ASC_DVC_VAR asc_ptr_type *, uchar);
+int AscWaitISRDone(ASC_DVC_VAR asc_ptr_type *);
+ulong AscGetOnePhyAddr(ASC_DVC_VAR asc_ptr_type *, uchar dosfar *, ulong);
+
+int AscSendScsiQueue(ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_Q dosfar * scsiq,
+ uchar n_q_required);
+int AscPutReadyQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q dosfar *, uchar);
+int AscPutReadySgListQueue(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_Q dosfar *, uchar);
+int AscAbortScsiIO(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q dosfar *);
+void AscExeScsiIO(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q dosfar *);
+int AscSetChipSynRegAtID(PortAddr, uchar, uchar);
+int AscSetRunChipSynRegAtID(PortAddr, uchar, uchar);
+ushort AscInitLram(ASC_DVC_VAR asc_ptr_type *);
+int AscReInitLram(ASC_DVC_VAR asc_ptr_type *);
+ushort AscInitQLinkVar(ASC_DVC_VAR asc_ptr_type *);
+int AscSetLibErrorCode(ASC_DVC_VAR asc_ptr_type *, ushort);
+int _AscWaitQDone(PortAddr, ASC_SCSI_Q dosfar *);
+
+int AscEnterCritical(void);
+void AscLeaveCritical(int);
+
+int AscIsrChipHalted(ASC_DVC_VAR asc_ptr_type *);
+uchar _AscCopyLramScsiDoneQ(PortAddr, ushort,
+ ASC_QDONE_INFO dosfar *, ulong);
+int AscIsrQDone(ASC_DVC_VAR asc_ptr_type *);
+ushort AscIsrExeBusyQueue(ASC_DVC_VAR asc_ptr_type *, uchar);
+int AscScsiSetupCmdQ(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_REQ_Q dosfar *,
+ uchar dosfar *, ulong);
+
+int AscScsiInquiry(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_REQ_Q dosfar *,
+ uchar dosfar *, int);
+int AscScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_REQ_Q dosfar *);
+int AscScsiStartStopUnit(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *, uchar);
+int AscScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *,
+ uchar dosfar *);
+
+ulong dosfar *swapfarbuf4(uchar dosfar *);
+int PollQueueDone(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *,
+ int);
+int PollScsiReadCapacity(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *,
+ ASC_CAP_INFO dosfar *);
+int PollScsiInquiry(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_REQ_Q dosfar *,
+ uchar dosfar *, int);
+int PollScsiTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *);
+int PollScsiStartUnit(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *);
+int InitTestUnitReady(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *);
+void AscDispInquiry(uchar, uchar, ASC_SCSI_INQUIRY dosfar *);
+int AscPollQDone(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *, int);
+
+int AscSetBIOSBank(PortAddr, int, ushort);
+int AscSetVlBIOSBank(PortAddr, int);
+int AscSetEisaBIOSBank(PortAddr, int);
+int AscSetIsaBIOSBank(PortAddr, int);
+
+int AscIsBiosEnabled(PortAddr, ushort);
+void AscResetScsiBus(PortAddr);
+void AscClrResetScsiBus(PortAddr);
+
+void AscSingleStepChip(PortAddr);
+uchar AscSetChipScsiID(PortAddr, uchar);
+ushort AscGetChipBiosAddress(PortAddr, ushort);
+ushort AscSetChipBiosAddress(PortAddr, ushort, ushort);
+uchar AscGetChipVersion(PortAddr, ushort);
+ushort AscGetChipBusType(PortAddr);
+
+PortAddr AscSearchIOPortAddr11(PortAddr);
+PortAddr AscSearchIOPortAddr100(PortAddr);
+int AscFindSignature(PortAddr);
+void AscToggleIRQAct(PortAddr);
+int AscResetChip(PortAddr);
+void AscClrResetChip(PortAddr);
+
+short itos(ushort, uchar dosfar *, short, short);
+int insnchar(uchar dosfar *, short, short, ruchar, short);
+void itoh(ushort, ruchar dosfar *);
+void btoh(uchar, ruchar dosfar *);
+void ltoh(ulong, ruchar dosfar *);
+uchar dosfar *todstr(ushort, uchar dosfar *);
+uchar dosfar *tohstr(ushort, uchar dosfar *);
+uchar dosfar *tobhstr(uchar, uchar dosfar *);
+uchar dosfar *tolhstr(ulong, uchar dosfar *);
+
+void AscSetISAPNPWaitForKey(void);
+uchar AscGetChipIRQ(PortAddr, ushort);
+uchar AscSetChipIRQ(PortAddr, uchar, ushort);
+uchar AscGetChipScsiCtrl(PortAddr);
+
+ushort AscGetEisaChipCfg(PortAddr);
+ushort AscGetEisaChipGpReg(PortAddr);
+ushort AscSetEisaChipCfg(PortAddr, ushort);
+ushort AscSetEisaChipGpReg(PortAddr, ushort);
+
+ulong AscGetEisaProductID(PortAddr);
+PortAddr AscSearchIOPortAddrEISA(PortAddr);
+
+int AscPollQTailSync(PortAddr);
+int AscPollQHeadSync(PortAddr);
+int AscWaitQTailSync(PortAddr);
+
+int _AscRestoreMicroCode(PortAddr, ASC_MC_SAVED dosfar *);
+
+int AscSCAM(ASC_DVC_VAR asc_ptr_type *);
+
+ushort SwapByteOfWord(ushort word_val);
+ulong SwapWordOfDWord(ulong dword_val);
+ulong AdjEndianDword(ulong dword_val);
+
+int AscAdjEndianScsiQ(ASC_SCSI_Q dosfar *);
+int AscAdjEndianQDoneInfo(ASC_QDONE_INFO dosfar *);
+
+extern int DvcEnterCritical(void);
+extern void DvcLeaveCritical(int);
+
+extern void DvcInPortWords(PortAddr, ushort dosfar *, int);
+extern void DvcOutPortWords(PortAddr, ushort dosfar *, int);
+extern void DvcOutPortDWords(PortAddr, ulong dosfar *, int);
+
+extern void DvcSleepMilliSecond(ulong);
+extern void DvcDisplayString(uchar dosfar *);
+extern ulong DvcGetPhyAddr(uchar dosfar * buf_addr, ulong buf_len);
+extern ulong DvcGetSGList(ASC_DVC_VAR asc_ptr_type *, uchar dosfar *, ulong,
+ ASC_SG_HEAD dosfar *);
+
+extern void DvcSCAMDelayMS(ulong);
+extern int DvcDisableCPUInterrupt(void);
+extern void DvcRestoreCPUInterrupt(int);
+
+void DvcPutScsiQ(PortAddr, ushort, ushort dosfar *, int);
+void DvcGetQinfo(PortAddr, ushort, ushort dosfar *, int);
+
+PortAddr AscSearchIOPortAddr(PortAddr, ushort);
+ushort AscInitGetConfig(ASC_DVC_VAR asc_ptr_type *);
+ushort AscInitSetConfig(ASC_DVC_VAR asc_ptr_type *);
+ushort AscInitAsc1000Driver(ASC_DVC_VAR asc_ptr_type *);
+int AscInitScsiTarget(ASC_DVC_VAR asc_ptr_type *,
+ ASC_DVC_INQ_INFO dosfar *,
+ uchar dosfar *,
+ ASC_CAP_INFO_ARRAY dosfar *,
+ ushort);
+int AscInitPollBegin(ASC_DVC_VAR asc_ptr_type *);
+int AscInitPollEnd(ASC_DVC_VAR asc_ptr_type *);
+int AscInitPollTarget(ASC_DVC_VAR asc_ptr_type *,
+ ASC_SCSI_REQ_Q dosfar *,
+ ASC_SCSI_INQUIRY dosfar *,
+ ASC_CAP_INFO dosfar *);
+int AscExeScsiQueue(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_Q dosfar *);
+
+int AscISR(ASC_DVC_VAR asc_ptr_type *);
+void AscISR_AckInterrupt(ASC_DVC_VAR asc_ptr_type *);
+int AscISR_CheckQDone(ASC_DVC_VAR asc_ptr_type *,
+ ASC_QDONE_INFO dosfar *,
+ uchar dosfar *);
+
+int AscStartUnit(ASC_DVC_VAR asc_ptr_type *, ASC_SCSI_TIX_TYPE);
+int AscStopUnit(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_TIX_TYPE target_ix
+);
+
+uint AscGetNumOfFreeQueue(ASC_DVC_VAR asc_ptr_type *, uchar, uchar);
+int AscSgListToQueue(int);
+int AscQueueToSgList(int);
+int AscSetDvcErrorCode(ASC_DVC_VAR asc_ptr_type *, uchar);
+
+int AscAbortSRB(ASC_DVC_VAR asc_ptr_type *, ulong);
+int AscResetDevice(ASC_DVC_VAR asc_ptr_type *, uchar);
+int AscResetSB(ASC_DVC_VAR asc_ptr_type *);
+
+void AscEnableIsaDma(uchar);
+void AscDisableIsaDma(uchar);
+
+ulong AscGetMaxDmaAddress(ushort);
+ulong AscGetMaxDmaCount(ushort);
+
+int AscSaveMicroCode(ASC_DVC_VAR asc_ptr_type *, ASC_MC_SAVED dosfar *);
+int AscRestoreOldMicroCode(ASC_DVC_VAR asc_ptr_type *, ASC_MC_SAVED dosfar *);
+int AscRestoreNewMicroCode(ASC_DVC_VAR asc_ptr_type *, ASC_MC_SAVED dosfar *);
+
+/*
+ * --- Debugging Header
+ */
+
+#ifdef ADVANSYS_DEBUG
+#define STATIC
+#else /* ADVANSYS_DEBUG */
+#define STATIC static
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Driver Constants and Macros
+ */
+
+#define ASC_NUM_BOARD_SUPPORTED 4
+#define ASC_NUM_BUS 4
+
+/* Reference Scsi_Host hostdata */
+#define ASC_BOARD(host) ((struct asc_board *) &(host)->hostdata)
+
+#define NO_ISA_DMA 0xff /* No ISA DMA Channel Used */
+
+#ifndef min
+#define min(a, b) (((a) < (b)) ? (a) : (b))
+#endif /* min */
+
+/* Asc Library return codes */
+#define ASC_TRUE 1
+#define ASC_FALSE 0
+#define ASC_NOERROR 1
+#define ASC_BUSY 0
+#define ASC_ERROR (-1)
+
+/* Scsi_Cmnd function return codes */
+#define STATUS_BYTE(byte) (byte)
+#define MSG_BYTE(byte) ((byte) << 8)
+#define HOST_BYTE(byte) ((byte) << 16)
+#define DRIVER_BYTE(byte) ((byte) << 24)
+
+/* asc_enqueue() flags */
+#define ASC_FRONT 1
+#define ASC_BACK 2
+
+/* PCI configuration declarations */
+
+#define ASC_PCI_REV_A_INIT 0x01
+#define ASC_PCI_REV_A_DONE 0x02
+#define ASC_PCI_REV_B_INIT 0x04
+#define ASC_PCI_REV_B_DONE 0x08
+
+#define PCI_BASE_CLASS_PREDEFINED 0x00
+#define PCI_BASE_CLASS_MASS_STORAGE 0x01
+#define PCI_BASE_CLASS_NETWORK 0x02
+#define PCI_BASE_CLASS_DISPLAY 0x03
+#define PCI_BASE_CLASS_MULTIMEDIA 0x04
+#define PCI_BASE_CLASS_MEMORY_CONTROLLER 0x05
+#define PCI_BASE_CLASS_BRIDGE_DEVICE 0x06
+
+/* MASS STORAGE */
+#define PCI_SUB_CLASS_SCSI_CONTROLLER 0x00
+#define PCI_SUB_CLASS_IDE_CONTROLLER 0x01
+#define PCI_SUB_CLASS_FLOPPY_DISK_CONTROLLER 0x02
+#define PCI_SUB_CLASS_IPI_BUS_CONTROLLER 0x03
+#define PCI_SUB_CLASS_OTHER_MASS_CONTROLLER 0x80
+
+/* NETWORK CONTROLLER */
+#define PCI_SUB_CLASS_ETHERNET_CONTROLLER 0x00
+#define PCI_SUB_CLASS_TOKEN_RING_CONTROLLER 0x01
+#define PCI_SUB_CLASS_FDDI_CONTROLLER 0x02
+#define PCI_SUB_CLASS_OTHER_NETWORK_CONTROLLER 0x80
+
+/* DISPLAY CONTROLLER */
+#define PCI_SUB_CLASS_VGA_CONTROLLER 0x00
+#define PCI_SUB_CLASS_XGA_CONTROLLER 0x01
+#define PCI_SUB_CLASS_OTHER_DISPLAY_CONTROLLER 0x80
+
+/* MULTIMEDIA CONTROLLER */
+#define PCI_SUB_CLASS_VIDEO_DEVICE 0x00
+#define PCI_SUB_CLASS_AUDIO_DEVICE 0x01
+#define PCI_SUB_CLASS_OTHER_MULTIMEDIA_DEVICE 0x80
+
+/* MEMORY CONTROLLER */
+#define PCI_SUB_CLASS_RAM_CONTROLLER 0x00
+#define PCI_SUB_CLASS_FLASH_CONTROLLER 0x01
+#define PCI_SUB_CLASS_OTHER_MEMORY_CONTROLLER 0x80
+
+/* BRIDGE CONTROLLER */
+#define PCI_SUB_CLASS_HOST_BRIDGE_CONTROLLER 0x00
+#define PCI_SUB_CLASS_ISA_BRIDGE_CONTROLLER 0x01
+#define PCI_SUB_CLASS_EISA_BRIDGE_CONTROLLER 0x02
+#define PCI_SUB_CLASS_MC_BRIDGE_CONTROLLER 0x03
+#define PCI_SUB_CLASS_PCI_TO_PCI_BRIDGE_CONTROLLER 0x04
+#define PCI_SUB_CLASS_PCMCIA_BRIDGE_CONTROLLER 0x05
+#define PCI_SUB_CLASS_OTHER_BRIDGE_CONTROLLER 0x80
+
+#define PCI_MAX_SLOT 0x1F
+#define PCI_MAX_BUS 0xFF
+#define ASC_PCI_VENDORID 0x10CD
+#define PCI_IOADDRESS_MASK 0xFFFE
+
+/* PCI IO Port Addresses to generate special cycle */
+
+#define PCI_CONFIG_ADDRESS_MECH1 0x0CF8
+#define PCI_CONFIG_DATA_MECH1 0x0CFC
+
+#define PCI_CONFIG_FORWARD_REGISTER 0x0CFA /* 0=type 0; 1=type 1; */
+
+#define PCI_CONFIG_BUS_NUMBER_MASK 0x00FF0000
+#define PCI_CONFIG_DEVICE_FUNCTION_MASK 0x0000FF00
+#define PCI_CONFIG_REGISTER_NUMBER_MASK 0x000000F8
+
+#define PCI_DEVICE_FOUND 0x0000
+#define PCI_DEVICE_NOT_FOUND 0xffff
+
+#define SUBCLASS_OFFSET 0x0A
+#define CLASSCODE_OFFSET 0x0B
+#define VENDORID_OFFSET 0x00
+#define DEVICEID_OFFSET 0x02
+
+/*
+ * --- Driver Macros
+ */
+
+#ifndef ADVANSYS_STATS
+#define ASC_STATS(counter)
+#define ASC_STATS_ADD(counter, count)
+#else /* ADVANSYS_STATS */
+#define ASC_STATS(counter) asc_stats.counter++
+#define ASC_STATS_ADD(counter, count) asc_stats.counter += (count)
+#endif /* ADVANSYS_STATS */
+
+#ifndef ADVANSYS_DEBUG
+
+#define ASC_DBG(lvl, s)
+#define ASC_DBG1(lvl, s, a1)
+#define ASC_DBG2(lvl, s, a1, a2)
+#define ASC_DBG3(lvl, s, a1, a2, a3)
+#define ASC_DBG4(lvl, s, a1, a2, a3, a4)
+#define ASC_DBG_PRT_SCSI_HOST(lvl, s)
+#define ASC_DBG_PRT_DVC_VAR(lvl, v)
+#define ASC_DBG_PRT_DVC_CFG(lvl, c)
+#define ASC_DBG_PRT_SCSI_Q(lvl, scsiqp)
+#define ASC_DBG_PRT_QDONE_INFO(lvl, qdone)
+#define ASC_DBG_PRT_HEX(lvl, name, start, length)
+#define ASC_DBG_PRT_CDB(lvl, cdb, len)
+#define ASC_DBG_PRT_SENSE(lvl, sense, len)
+#define ASC_DBG_PRT_INQUIRY(lvl, inq, len)
+#define ASC_ASSERT(a)
+
+#else /* ADVANSYS_DEBUG */
+
+/*
+ * Debugging Message Levels:
+ * 0: Errors Only
+ * 1: High-Level Tracing
+ * 2-N: Verbose Tracing
+ */
+
+#define ASC_DBG(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk(s); \
+ } \
+ }
+
+#define ASC_DBG1(lvl, s, a1) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1)); \
+ } \
+ }
+
+#define ASC_DBG2(lvl, s, a1, a2) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2)); \
+ } \
+ }
+
+#define ASC_DBG3(lvl, s, a1, a2, a3) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2), (a3)); \
+ } \
+ }
+
+#define ASC_DBG4(lvl, s, a1, a2, a3, a4) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ printk((s), (a1), (a2), (a3), (a4)); \
+ } \
+ }
+
+#define ASC_DBG_PRT_SCSI_HOST(lvl, s) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_scsi_host(s); \
+ } \
+ }
+
+#define ASC_DBG_PRT_DVC_VAR(lvl, v) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_dvc_var(v); \
+ } \
+ }
+
+#define ASC_DBG_PRT_DVC_CFG(lvl, c) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_dvc_cfg(c); \
+ } \
+ }
+
+#define ASC_DBG_PRT_SCSI_Q(lvl, scsiqp) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_scsi_q(scsiqp); \
+ } \
+ }
+
+#define ASC_DBG_PRT_QDONE_INFO(lvl, qdone) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_qdone_info(qdone); \
+ } \
+ }
+
+#define ASC_DBG_PRT_HEX(lvl, name, start, length) \
+ { \
+ if (asc_dbglvl >= (lvl)) { \
+ asc_prt_hex((name), (start), (length)); \
+ } \
+ }
+
+#define ASC_DBG_PRT_CDB(lvl, cdb, len) \
+ ASC_DBG_PRT_HEX((lvl), "CDB", (uchar *) (cdb), (len));
+
+#define ASC_DBG_PRT_SENSE(lvl, sense, len) \
+ ASC_DBG_PRT_HEX((lvl), "SENSE", (uchar *) (sense), (len));
+
+#define ASC_DBG_PRT_INQUIRY(lvl, inq, len) \
+ ASC_DBG_PRT_HEX((lvl), "INQUIRY", (uchar *) (inq), (len));
+
+#define ASC_ASSERT(a) \
+ { \
+ if (!(a)) { \
+ printk("ASC_ASSERT() Failure: file %s, line %d\n", \
+ __FILE__, __LINE__); \
+ } \
+ }
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Driver Structures
+ */
+
+/*
+ * Structure allocated for each board.
+ *
+ * This structure is allocated by scsi_register() at the end
+ * of the 'Scsi_Host' structure starting at the 'hostdata'
+ * field. It is guaranteed to be allocated from DMA-able memory.
+ */
+struct asc_board {
+ /* Asc Library */
+ ASC_DVC_VAR board; /* Board configuration */
+ ASC_DVC_CFG cfg; /* Device configuration */
+ uchar overrun_buf[ASC_OVERRUN_BSIZE];
+ /* Queued Commands */
+ ASC_SCSI_BIT_ID_TYPE pending_tidmask; /* Pending command mask */
+ Scsi_Cmnd *pending[ASC_MAX_TID];
+ /* Target Initialization */
+ ASC_SCSI_BIT_ID_TYPE init_tidmask; /* Target initialized mask */
+ ASC_SCSI_REQ_Q scsireqq;
+ ASC_CAP_INFO cap_info;
+ ASC_SCSI_INQUIRY inquiry;
+};
+
+/*
+ * PCI configuration structures
+ */
+typedef struct _PCI_DATA_
+{
+ uchar type;
+ uchar bus;
+ uchar slot;
+ uchar func;
+ uchar offset;
+} PCI_DATA;
+
+typedef struct _PCI_DEVICE_
+{
+ ushort vendorID;
+ ushort deviceID;
+ ushort slotNumber;
+ ushort slotFound;
+ uchar busNumber;
+ uchar maxBusNumber;
+ uchar devFunc;
+ ushort startSlot;
+ ushort endSlot;
+ uchar bridge;
+ uchar type;
+} PCI_DEVICE;
+
+typedef struct _PCI_CONFIG_SPACE_
+{
+ ushort vendorID;
+ ushort deviceID;
+ ushort command;
+ ushort status;
+ uchar revision;
+ uchar classCode[3];
+ uchar cacheSize;
+ uchar latencyTimer;
+ uchar headerType;
+ uchar bist;
+ ulong baseAddress[6];
+ ushort reserved[4];
+ ulong optionRomAddr;
+ ushort reserved2[4];
+ uchar irqLine;
+ uchar irqPin;
+ uchar minGnt;
+ uchar maxLatency;
+} PCI_CONFIG_SPACE;
+
+#ifdef ADVANSYS_STATS
+struct asc_stats {
+ ulong command; /* # calls to advansys_command() */
+ ulong queuecommand; /* # calls to advansys_queuecommand() */
+ ulong abort; /* # calls to advansys_abort() */
+ ulong reset; /* # calls to advansys_reset() */
+ ulong biosparam; /* # calls to advansys_biosparam() */
+ ulong interrupt; /* # calls to advansys_interrupt() */
+ ulong callback; /* # calls asc_isr_callback() */
+ ulong cont_cnt; /* # non-scatter-gather I/O requests received */
+ ulong cont_xfer; /* contiguous transfer total (512 byte units) */
+ ulong sg_cnt; /* # scatter-gather I/O requests received */
+ ulong sg_elem; /* scatter-gather element total */
+ ulong sg_xfer; /* scatter-gather tranfer total (512 byte units) */
+ ulong error; /* # AscExeScsiQueue() ASC_ERROR returns. */
+ /*
+ * Number of times interrupts disabled in advansys_queuecommand() and
+ * asc_isr_callback(), respectively. For the former indicates how many
+ * times commands were pending when a new command was received.
+ */
+ ulong cmd_disable;
+ ulong intr_disable;
+ /*
+ * Number of times asc_enqueue() called. Indicates how many ASC_BUSY
+ * returns have occurred.
+ */
+ ulong enqueue;
+ ulong dequeue; /* # calls to asc_dequeue(). */
+ /*
+ * Number of times asc_rmqueue() called and the specified command
+ * was found and removed.
+ */
+ ulong rmqueue;
+} asc_stats;
+#endif /* ADVANSYS_STATS */
+
+
+/*
+ * --- Driver Data
+ */
+
+#ifdef LINUX_1_3
+struct proc_dir_entry proc_scsi_advansys =
+{
+ PROC_SCSI_ADVANSYS, /* unsigned short low_ino */
+ 8, /* unsigned short namelen */
+ "advansys", /* const char *name */
+ S_IFDIR | S_IRUGO | S_IXUGO, /* mode_t mode */
+ 2 /* nlink_t nlink */
+};
+#endif /* LINUX_1_3 */
+
+STATIC int asc_board_count; /* Number of boards detected in system. */
+STATIC struct Scsi_Host *asc_host[ASC_NUM_BOARD_SUPPORTED];
+STATIC Scsi_Cmnd *asc_scsi_done; /* Commands needing done function call. */
+
+STATIC ushort asc_bus[ASC_NUM_BUS] = {
+ ASC_IS_ISA,
+ ASC_IS_VL,
+ ASC_IS_EISA,
+ ASC_IS_PCI,
+};
+
+/*
+ * Used with the LILO 'advansys' option to eliminate or
+ * limit I/O port probing at boot time, cf. advansys_setup().
+ */
+int asc_iopflag = ASC_FALSE;
+int asc_ioport[ASC_NUM_BOARD_SUPPORTED] = { 0, 0, 0, 0 };
+
+#ifdef ADVANSYS_DEBUG
+char *
+asc_bus_name[ASC_NUM_BUS] = {
+ "ASC_IS_ISA",
+ "ASC_IS_VL",
+ "ASC_IS_EISA",
+ "ASC_IS_PCI",
+};
+
+int asc_dbglvl = 0;
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Driver Function Prototypes
+ *
+ * advansys.h contains function prototypes for functions global to Linux.
+ */
+
+#ifdef LINUX_1_3
+STATIC int asc_proc_copy(off_t, off_t, char *, int , char *, int);
+#endif /* LINUX_1_3 */
+STATIC void advansys_interrupt(int, struct pt_regs *);
+STATIC void advansys_command_done(Scsi_Cmnd *);
+STATIC int asc_execute_scsi_cmnd(Scsi_Cmnd *);
+STATIC void asc_isr_callback(ASC_DVC_VAR *, ASC_QDONE_INFO *);
+STATIC void asc_execute_pending(struct Scsi_Host *);
+STATIC int asc_init_dev(ASC_DVC_VAR *, Scsi_Cmnd *);
+STATIC int asc_srch_pci_dev(PCI_DEVICE *);
+STATIC uchar asc_scan_method(PCI_DEVICE *);
+STATIC int asc_pci_find_dev(PCI_DEVICE *);
+STATIC void asc_get_pci_cfg(PCI_DEVICE *, PCI_CONFIG_SPACE *);
+STATIC ushort asc_get_cfg_word(PCI_DATA *);
+STATIC uchar asc_get_cfg_byte(PCI_DATA *);
+STATIC void asc_enqueue(struct Scsi_Host *, Scsi_Cmnd *, int, int);
+STATIC Scsi_Cmnd *asc_dequeue(struct Scsi_Host *, int);
+STATIC int asc_rmqueue(struct Scsi_Host *, Scsi_Cmnd *, int);
+
+/* XXX - Asc Library Routines not supposed to be used directly */
+ushort AscGetChipBiosAddress(PortAddr, ushort);
+int AscFindSignature(PortAddr);
+
+#ifdef ADVANSYS_STATS
+STATIC int asc_prt_stats(char *, int);
+STATIC int asc_prt_stats_line(char *, int, char *fmt, ...);
+#endif /* ADVANSYS_STATS */
+#ifdef ADVANSYS_DEBUG
+STATIC void asc_prt_scsi_host(struct Scsi_Host *);
+STATIC void asc_prt_dvc_cfg(ASC_DVC_CFG *);
+STATIC void asc_prt_dvc_var(ASC_DVC_VAR *);
+STATIC void asc_prt_scsi_q(ASC_SCSI_Q *);
+STATIC void asc_prt_qdone_info(ASC_QDONE_INFO *);
+STATIC void asc_prt_hex(char *f, uchar *, int);
+STATIC int interrupts_enabled(void);
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Linux 'Scsi_Host_Template' and advansys_setup() Functions
+ */
+
+#ifdef LINUX_1_3
+/*
+ * advansys_proc_info() - /proc/scsi/advansys/[0-ASC_NUM_BOARD_SUPPORTED]
+ *
+ * *buffer: I/O buffer
+ * **start: if inout == FALSE pointer into buffer where user read should start
+ * offset: current offset into /proc/scsi/advansys file
+ * length: length of buffer
+ * hostno: Scsi_Host host_no
+ * inout: TRUE - user is writing; FALSE - user is reading
+ *
+ * Return the number of bytes read from or written to
+ * /proc/scsi/advansys file.
+ */
+int
+advansys_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct Scsi_Host *shp;
+ int i;
+ char *cp;
+ int cplen;
+ int cnt;
+ int totcnt;
+ int leftlen;
+ char *curbuf;
+ off_t advoffset;
+ Scsi_Device *scd;
+ char prtbuf[480]; /* 6 lines */
+
+ ASC_DBG(1, "advansys_proc_info: begin\n");
+
+ /*
+ * User write not supported.
+ */
+ if (inout == TRUE) {
+ return(-ENOSYS);
+ }
+
+ /*
+ * User read of /proc/scsi/advansys file.
+ */
+
+ /* Find the specified board. */
+ for (i = 0; i < asc_board_count; i++) {
+ if (asc_host[i]->host_no == hostno) {
+ break;
+ }
+ }
+ if (i == asc_board_count) {
+ return(-ENOENT);
+ }
+ shp = asc_host[i];
+
+ /* Always copy read data to the beginning of the buffer. */
+ *start = buffer;
+
+ curbuf = buffer;
+ advoffset = 0;
+ totcnt = 0;
+ leftlen = length;
+
+ /* Get board information. */
+ cp = (char *) advansys_info(shp);
+ strcat(cp, "\n");
+ cplen = strlen(cp);
+
+ /* Copy board information. */
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ /*
+ * Get and copy information for each device attached to the board.
+ */
+ cp = &prtbuf[0];
+ sprintf(cp, "\nDevices attached to SCSI Host %d:\n", shp->host_no);
+ cplen = strlen(cp);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+
+ cp = &prtbuf[0];
+ for (scd = scsi_devices; scd; scd = scd->next) {
+ if (scd->host == shp) {
+ proc_print_scsidevice(scd, cp, &cplen, 0);
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+ }
+ }
+
+#ifdef ADVANSYS_STATS
+ /*
+ * prtbuf[] has about 6 lines worth of space. If the statistics ever
+ * get longer than 6 lines, prtbuf[] should be increased in size. If
+ * prtbuf[] is too small it will not be overwritten. Instead the user
+ * just won't get all of the available statistics.
+ */
+ cp = &prtbuf[0];
+ cplen = asc_prt_stats(cp, sizeof(prtbuf));
+ cnt = asc_proc_copy(advoffset, offset, curbuf, leftlen, cp, cplen);
+ totcnt += cnt;
+ leftlen -= cnt;
+ if (leftlen == 0) {
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+ return totcnt;
+ }
+ advoffset += cplen;
+ curbuf += cnt;
+#endif /* ADVANSYS_STATS */
+
+ ASC_DBG1(1, "advansys_proc_info: totcnt %d\n", totcnt);
+
+ return totcnt;
+}
+#endif /* LINUX_1_3 */
+
+
+/*
+ * advansys_detect()
+ *
+ * Detect function for AdvanSys adapters.
+ *
+ * Argument is a pointer to the host driver's scsi_hosts entry.
+ *
+ * Return number of adapters found.
+ *
+ * Note: Because this function is called during system initialization
+ * it must not call SCSI mid-level functions including scsi_malloc()
+ * and scsi_free().
+ */
+int
+advansys_detect(Scsi_Host_Template *tpnt)
+{
+ static int detect_called = ASC_FALSE;
+ int iop;
+ int bus;
+ struct Scsi_Host *shp;
+ ASC_DVC_VAR *boardp;
+ int ioport = 0;
+ PCI_DEVICE pciDevice;
+ PCI_CONFIG_SPACE pciConfig;
+ int ret;
+ extern PortAddr _asc_def_iop_base[ASC_IOADR_TABLE_MAX_IX];
+
+
+ if (detect_called == ASC_FALSE) {
+ detect_called = ASC_TRUE;
+ } else {
+ printk("AdvanSys SCSI: advansys_detect() mulitple calls ignored\n");
+ return 0;
+ }
+
+ ASC_DBG(1, "advansys_detect: begin\n");
+
+#ifdef LINUX_1_3
+ tpnt->proc_dir = &proc_scsi_advansys;
+#endif /* LINUX_1_3 */
+
+#ifdef ADVANSYS_STATS
+ memset(&asc_stats, 0, sizeof(asc_stats));
+#endif /* ADVANSYS_STATS */
+
+ asc_board_count = 0;
+
+ /*
+ * If I/O port probing has been modified, then verify and
+ * clean-up the 'asc_ioport' list.
+ */
+ if (asc_iopflag == ASC_TRUE) {
+ for (ioport = 0; ioport < ASC_NUM_BOARD_SUPPORTED; ioport++) {
+ ASC_DBG2(1, "asdvansys_detect: asc_ioport[%d] %x\n",
+ ioport, asc_ioport[ioport]);
+ if (asc_ioport[ioport] != 0) {
+ for (iop = 0; iop < ASC_IOADR_TABLE_MAX_IX; iop++) {
+ if (_asc_def_iop_base[iop] == asc_ioport[ioport]) {
+ break;
+ }
+ }
+ if (iop == ASC_IOADR_TABLE_MAX_IX) {
+ printk("AdvanSys SCSI: specified I/O Port 0x%X is invalid\n",
+ asc_ioport[ioport]);
+ asc_ioport[ioport] = 0;
+ }
+ }
+ }
+ ioport = 0;
+ }
+
+ memset(&pciDevice, 0, sizeof(PCI_DEVICE));
+ memset(&pciConfig, 0, sizeof(PCI_CONFIG_SPACE));
+ pciDevice.maxBusNumber = PCI_MAX_BUS;
+ pciDevice.endSlot = PCI_MAX_SLOT;
+
+ for (bus = 0; bus < ASC_NUM_BUS; bus++) {
+
+ ASC_DBG2(1, "advansys_detect: bus search type %d (%s)\n",
+ bus, asc_bus_name[bus]);
+ iop = 0;
+
+ while (asc_board_count < ASC_NUM_BOARD_SUPPORTED) {
+
+ ASC_DBG1(2, "advansys_detect: asc_board_count %d\n",
+ asc_board_count);
+
+ switch (asc_bus[bus]) {
+ case ASC_IS_ISA:
+ case ASC_IS_VL:
+ if (asc_iopflag == ASC_FALSE) {
+ iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
+ } else {
+ /*
+ * ISA and VL I/O port scanning has either been
+ * eliminated or limited to selected ports on
+ * the LILO command line, /etc/lilo.conf, or
+ * by setting variables when the module was loaded.
+ */
+ ASC_DBG(1, "advansys_detect: I/O port scanning modified\n");
+ ioport_try_again:
+ iop = 0;
+ for (; ioport < ASC_NUM_BOARD_SUPPORTED; ioport++) {
+ if ((iop = asc_ioport[ioport]) != 0) {
+ break;
+ }
+ }
+ if (iop) {
+ ASC_DBG1(1, "advansys_detect: probing I/O port %x...\n",
+ iop);
+ if (check_region(iop, ASC_IOADR_GAP) != 0) {
+ printk("AdvanSys SCSI: specified I/O Port 0x%X is busy\n", iop);
+ /* Don't try this I/O port twice. */
+ asc_ioport[ioport] = 0;
+ goto ioport_try_again;
+ } else if (AscFindSignature(iop) == ASC_FALSE) {
+ printk("AdvanSys SCSI: specified I/O Port 0x%X has no adapter\n", iop);
+ /* Don't try this I/O port twice. */
+ asc_ioport[ioport] = 0;
+ goto ioport_try_again;
+ } else {
+ /*
+ * If this isn't an ISA board, then it must be
+ * a VL board. If currently looking an ISA
+ * board is being looked for then try for
+ * another ISA board in 'asc_ioport'.
+ */
+ if (asc_bus[bus] == ASC_IS_ISA &&
+ (AscGetChipVersion(iop, ASC_IS_ISA) &
+ ASC_CHIP_VER_ISA_BIT) == 0) {
+ /*
+ * Don't clear 'asc_ioport[ioport]'. Try
+ * this board again for VL. Increment
+ * 'ioport' past this board.
+ */
+ ioport++;
+ goto ioport_try_again;
+ }
+ }
+ /*
+ * This board appears good, don't try the I/O port
+ * again by clearing its value. Increment 'ioport'
+ * for the next iteration.
+ */
+ asc_ioport[ioport++] = 0;
+ }
+ }
+ break;
+
+ case ASC_IS_EISA:
+ iop = AscSearchIOPortAddr(iop, asc_bus[bus]);
+ break;
+
+ case ASC_IS_PCI:
+ if (asc_srch_pci_dev(&pciDevice) != PCI_DEVICE_FOUND) {
+ iop = 0;
+ } else {
+ ASC_DBG2(2,
+ "advansys_detect: slotFound %d, busNumber %d\n",
+ pciDevice.slotFound, pciDevice.busNumber);
+ asc_get_pci_cfg(&pciDevice, &pciConfig);
+ iop = pciConfig.baseAddress[0] & PCI_IOADDRESS_MASK;
+ ASC_DBG2(2, "advansys_detect: iop %x, irqLine %d\n",
+ iop, pciConfig.irqLine);
+ }
+ break;
+
+ default:
+ ASC_DBG(0, "advansys_detect: unknown bus type\n");
+ break;
+ }
+ ASC_DBG1(1, "advansys_detect: iop %x\n", iop);
+
+ /*
+ * Adapter not found, try next bus type.
+ */
+ if (iop == 0) {
+ break;
+ }
+
+ /*
+ * Adapter found.
+ *
+ * Register the adapter, get its configuration, and
+ * initialize it.
+ */
+ ASC_DBG(2, "advansys_detect: scsi_register()\n");
+ shp = scsi_register(tpnt, sizeof(struct asc_board));
+
+ /* Save a pointer to the Scsi_host of each found board. */
+ asc_host[asc_board_count++] = shp;
+
+ /* Initialize private per board data */
+ memset(ASC_BOARD(shp), 0, sizeof(struct asc_board));
+ boardp = &ASC_BOARD(shp)->board;
+ boardp->cfg = &ASC_BOARD(shp)->cfg;
+ boardp->cfg->overrun_buf = &ASC_BOARD(shp)->overrun_buf[0];
+ boardp->iop_base = iop;
+
+ /*
+ * Set the board bus type and PCI IRQ for AscInitGetConfig().
+ */
+ boardp->bus_type = asc_bus[bus];
+ switch (boardp->bus_type) {
+ case ASC_IS_ISA:
+ shp->unchecked_isa_dma = TRUE;
+ break;
+ case ASC_IS_EISA:
+ shp->unchecked_isa_dma = FALSE;
+ break;
+ case ASC_IS_VL:
+ shp->unchecked_isa_dma = FALSE;
+ break;
+ case ASC_IS_PCI:
+ shp->irq = boardp->irq_no = pciConfig.irqLine;
+ boardp->cfg->pci_device_id = pciConfig.deviceID;
+ shp->unchecked_isa_dma = FALSE;
+ break;
+ default:
+ ASC_DBG(0, "advansys_detect: unknown adapter type");
+ shp->unchecked_isa_dma = TRUE;
+ break;
+ }
+
+ /*
+ * Get the board configuration. AscInitGetConfig() may change
+ * the board's bus_type value. The asc_bus[bus] value should no
+ * longer be used.
+ */
+ ASC_DBG(2, "advansys_detect: AscInitGetConfig()\n");
+ switch(ret = AscInitGetConfig(boardp)) {
+ case 0: /* No error */
+ break;
+ case ASC_WARN_IO_PORT_ROTATE:
+ ASC_DBG(0, "AscInitGetConfig: I/O port address modified\n");
+ break;
+ case ASC_WARN_EEPROM_CHKSUM:
+ ASC_DBG(0, "AscInitGetConfig: EEPROM checksum error\n");
+ break;
+ case ASC_WARN_IRQ_MODIFIED:
+ ASC_DBG(0, "AscInitGetConfig: IRQ modified\n");
+ break;
+ case ASC_WARN_CMD_QNG_CONFLICT:
+ ASC_DBG(0,
+ "AscInitGetConfig: Tag queuing enabled w/o disconnects\n");
+ break;
+ default:
+ ASC_DBG1(0, "AscInitGetConfig: Unknown warning: %x\n", ret);
+ break;
+ }
+ if (boardp->err_code != 0) {
+ ASC_DBG2(0,
+ "AscInitGetConfig: error: init_state %x, err_code %x\n",
+ boardp->init_state, boardp->err_code);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Modify board configuration.
+ */
+ boardp->isr_callback = (Ptr2Func) asc_isr_callback;
+ boardp->exe_callback = (Ptr2Func) NULL;
+
+ ASC_DBG(2, "advansys_detect: AscInitSetConfig()\n");
+ switch (ret = AscInitSetConfig(boardp)) {
+ case 0: /* No error. */
+ break;
+ case ASC_WARN_IO_PORT_ROTATE:
+ ASC_DBG(0, "AscInitSetConfig: I/O port address modified\n");
+ break;
+ case ASC_WARN_EEPROM_CHKSUM:
+ ASC_DBG(0, "AscInitSetConfig: EEPROM checksum error\n");
+ break;
+ case ASC_WARN_IRQ_MODIFIED:
+ ASC_DBG(0, "AscInitSetConfig: IRQ modified\n");
+ break;
+ case ASC_WARN_CMD_QNG_CONFLICT:
+ ASC_DBG(0, "AscInitSetConfig: Tag queuing w/o disconnects\n");
+ break;
+ default:
+ ASC_DBG1(0, "AscInitSetConfig: Unknown warning: %x\n", ret);
+ break;
+ }
+ if (boardp->err_code != 0) {
+ ASC_DBG2(0,
+ "AscInitSetConfig: error: init_state %x, err_code %x\n",
+ boardp->init_state, boardp->err_code);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Finish initializing the 'Scsi_Host' structure.
+ */
+
+ /* AscInitSetConfig() will set the IRQ for non-PCI boards. */
+ if (boardp->bus_type != ASC_IS_PCI) {
+ shp->irq = boardp->irq_no;
+ }
+
+ shp->io_port = boardp->iop_base;
+ shp->n_io_port = ASC_IOADR_GAP;
+ shp->this_id = boardp->cfg->chip_scsi_id;
+
+ /* Maximum number of queues this adapter can handle. */
+ shp->can_queue = boardp->max_total_qng;
+
+ /*
+ * XXX - Command queuing limits are maintained per target
+ * by AdvanSys adapters. Set 'cmd_per_lun' to the minimum
+ * value of the all the target settings for the adapter.
+ *
+ * For now set 'cmd_per_lun' to 'max_total_qng'. This
+ * value should be adjusted every time a new device is
+ * found in asc_init_dev().
+ *
+ * XXX - memory allocation is done by the mid-level scsi
+ * driver based on 'cmd_per_lun'. If 'sg_tablesize' is too large
+ * allocation failures can occur in scsi_register_host().
+ * A 'Scsi_Cmnd' structure is pre-allocated for each command
+ * also DMA memory is reserved. Set it artificially low for now.
+ *
+ * shp->cmd_per_lun = boardp->max_total_qng;
+ */
+#ifdef MODULE
+ shp->cmd_per_lun = 1;
+#else /* MODULE */
+ shp->cmd_per_lun = 4;
+#endif /* MODULE */
+ ASC_DBG1(1, "advansys_detect: cmd_per_lun: %d\n", shp->cmd_per_lun);
+
+ /* Maximum number of scatter-gather elements adapter can handle. */
+ /*
+ * XXX - memory allocation is done by the mid-level scsi
+ * driver based on sg_tablesize. If 'sg_tablesize' is too large
+ * allocation failures can occur in scsi_register_host().
+ */
+#ifdef MODULE
+ shp->sg_tablesize = 8;
+#else /* MODULE */
+ shp->sg_tablesize = ASC_MAX_SG_LIST;
+#endif /* MODULE */
+ ASC_DBG1(1, "advansys_detect: sg_tablesize: %d\n",
+ shp->sg_tablesize);
+
+ /* BIOS start address. */
+ shp->base = (char *) ((ulong) AscGetChipBiosAddress(
+ boardp->iop_base,
+ boardp->bus_type));
+
+ /*
+ * Register Board Resources - I/O Port, DMA, IRQ
+ */
+
+ /* Register I/O port range */
+ ASC_DBG(2, "advansys_detect: request_region()\n");
+ request_region(shp->io_port, shp->n_io_port, "advansys");
+
+ /* Register DMA channel for ISA bus. */
+ if ((boardp->bus_type & ASC_IS_ISA) == 0) {
+ shp->dma_channel = NO_ISA_DMA;
+ } else {
+ shp->dma_channel = boardp->cfg->isa_dma_channel;
+ if ((ret = request_dma(shp->dma_channel, "advansys")) != 0) {
+ ASC_DBG2(0, "advansys_detect: request_dma() %d failed %d\n",
+ shp->dma_channel, ret);
+ release_region(shp->io_port, shp->n_io_port);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ AscEnableIsaDma(shp->dma_channel);
+ }
+
+ /* Register IRQ Number. */
+ ASC_DBG1(2, "advansys_detect: request_irq() %d\n", shp->irq);
+ if ((ret = request_irq(shp->irq, advansys_interrupt,
+ SA_INTERRUPT, "advansys")) != 0) {
+ ASC_DBG1(0, "advansys_detect: request_irq() failed %d\n", ret);
+ release_region(shp->io_port, shp->n_io_port);
+ if (shp->dma_channel != NO_ISA_DMA) {
+ free_dma(shp->dma_channel);
+ }
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+
+ /*
+ * Initialize board RISC chip and enable interrupts.
+ */
+ ASC_DBG(2, "advansys_detect: AscInitAsc1000Driver()\n");
+ if (AscInitAsc1000Driver(boardp)) {
+ ASC_DBG2(0,
+ "AscInitAsc1000Driver: error: init_state %x, err_code %x\n",
+ boardp->init_state, boardp->err_code);
+ release_region(shp->io_port, shp->n_io_port);
+ if (shp->dma_channel != NO_ISA_DMA) {
+ free_dma(shp->dma_channel);
+ }
+ free_irq(shp->irq);
+ scsi_unregister(shp);
+ asc_board_count--;
+ continue;
+ }
+ ASC_DBG_PRT_SCSI_HOST(2, shp);
+ }
+ }
+
+ ASC_DBG1(1, "advansys_detect: done: asc_board_count %d\n", asc_board_count);
+ return asc_board_count;
+}
+
+/*
+ * advansys_release()
+ *
+ * Release resources allocated for a single AdvanSys adapter.
+ */
+int
+advansys_release(struct Scsi_Host *shp)
+{
+ ASC_DBG(1, "advansys_release: begin\n");
+ free_irq(shp->irq);
+ if (shp->dma_channel != NO_ISA_DMA) {
+ ASC_DBG(1, "advansys_release: free_dma()\n");
+ free_dma(shp->dma_channel);
+ }
+ release_region(shp->io_port, shp->n_io_port);
+ scsi_unregister(shp);
+ ASC_DBG(1, "advansys_release: end\n");
+ return 0;
+}
+
+/*
+ * advansys_info()
+ *
+ * Return suitable for printing on the console with the argument
+ * adapter's configuration information.
+ */
+const char *
+advansys_info(struct Scsi_Host *shp)
+{
+ static char info[128];
+ ASC_DVC_VAR *boardp;
+ char *busname;
+
+ boardp = &ASC_BOARD(shp)->board;
+ ASC_DBG(1, "advansys_info: begin\n");
+ if (boardp->bus_type & ASC_IS_ISA) {
+ sprintf(info,
+ "AdvanSys SCSI %s: ISA (%u CDB): BIOS %X, IO %X-%X, IRQ %u, DMA %u",
+ ASC_VERSION, ASC_BOARD(shp)->board.max_total_qng,
+ (unsigned) shp->base, shp->io_port,
+ shp->io_port + (shp->n_io_port - 1), shp->irq, shp->dma_channel);
+ } else {
+ switch (boardp->bus_type) {
+ case ASC_IS_EISA:
+ busname = "EISA";
+ break;
+ case ASC_IS_VL:
+ busname = "VL";
+ break;
+ case ASC_IS_PCI:
+ busname = "PCI";
+ break;
+ default:
+ busname = "?";
+ ASC_DBG1(0, "advansys_info: unknown bus type %d\n",
+ boardp->bus_type);
+ break;
+ }
+ /* No DMA channel for non-ISA busses. */
+ sprintf(info,
+ "AdvanSys SCSI %s: %s (%u CDB): BIOS %X, IO %X-%X, IRQ %u",
+ ASC_VERSION, busname, ASC_BOARD(shp)->board.max_total_qng,
+ (unsigned) shp->base, shp->io_port,
+ shp->io_port + (shp->n_io_port - 1), shp->irq);
+ }
+ ASC_DBG(1, "advansys_info: end\n");
+ return info;
+}
+
+/*
+ * advansys_command()
+ *
+ * Polled-I/O. Apparently host driver shouldn't return until
+ * command is finished.
+ *
+ * XXX - Can host driver block here instead of spinning on command status?
+ */
+int
+advansys_command(Scsi_Cmnd *scp)
+{
+ ASC_DBG1(1, "advansys_command: scp %x\n", (unsigned) scp);
+ ASC_STATS(command);
+ scp->SCp.Status = 0; /* Set to a known state */
+ advansys_queuecommand(scp, advansys_command_done);
+ while (scp->SCp.Status == 0) {
+ continue;
+ }
+ ASC_DBG1(1, "advansys_command: result %x\n", scp->result);
+ return scp->result;
+}
+
+/*
+ * advansys_queuecommand()
+ *
+ * This function always returns 0. Command return status is saved
+ * in the 'scp' result field.
+ */
+int
+advansys_queuecommand(Scsi_Cmnd *scp, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shp;
+ int flags = 0;
+ int interrupts_disabled;
+
+ ASC_STATS(queuecommand);
+ shp = scp->host;
+
+#ifdef LINUX_1_2
+ /*
+ * For LINUX_1_3, if statistics are enabled they can be accessed
+ * by reading /proc/scsi/advansys/[0-9].
+ */
+#ifdef ADVANSYS_STATS_1_2_PRINT
+ /* Display statistics every 10000 commands. */
+ if ((asc_stats.queuecommand % 10000) == 0) {
+ printk("\n");
+ (void) asc_prt_stats(NULL, 0);
+ printk("\n");
+ }
+#endif /* ADVANSYS_STATS_1_2_PRINT */
+#endif /* LINUX_1_2 */
+
+ /*
+ * If there are any pending commands for this board before trying
+ * to execute them, disable interrupts to preserve request ordering.
+ *
+ * The typical case will be no pending commands and interrupts
+ * not disabled.
+ */
+ if (ASC_BOARD(shp)->pending_tidmask == 0) {
+ interrupts_disabled = ASC_FALSE;
+ } else {
+ ASC_STATS(cmd_disable);
+ /* Disable interrupts */
+ interrupts_disabled = ASC_TRUE;
+ save_flags(flags);
+ cli();
+ ASC_DBG1(1, "advansys_queuecommand: asc_execute_pending() %x\n",
+ ASC_BOARD(shp)->pending_tidmask);
+ asc_execute_pending(shp);
+ }
+
+ /*
+ * Save the function pointer to Linux mid-level 'done' function and
+ * execute the command.
+ */
+ scp->scsi_done = done;
+ if (asc_execute_scsi_cmnd(scp) == ASC_BUSY) {
+ if (interrupts_disabled == ASC_FALSE) {
+ save_flags(flags);
+ cli();
+ interrupts_disabled = ASC_TRUE;
+ }
+ asc_enqueue(shp, scp, scp->target, ASC_BACK);
+ }
+
+ if (interrupts_disabled == ASC_TRUE) {
+ restore_flags(flags);
+ }
+
+ return 0;
+}
+
+/*
+ * advansys_abort()
+ *
+ * Abort the specified command and reset the device
+ * associated with the command 'scp'.
+ */
+int
+advansys_abort(Scsi_Cmnd *scp)
+{
+ ASC_DVC_VAR *boardp;
+ int flags;
+ int ret;
+
+ ASC_DBG1(1, "advansys_abort: scp %x\n", (unsigned) scp);
+ save_flags(flags);
+ cli();
+ ASC_STATS(abort);
+ if (scp->host == NULL) {
+ scp->result = HOST_BYTE(DID_ERROR);
+ ret = SCSI_ABORT_ERROR;
+ } else if (asc_rmqueue(scp->host, scp, scp->target) == ASC_TRUE) {
+ scp->result = HOST_BYTE(DID_ABORT);
+ ret = SCSI_ABORT_SUCCESS;
+ (void) AscResetDevice(&ASC_BOARD(scp->host)->board, scp->target);
+ } else {
+ /* Must enable interrupts for AscAbortSRB() */
+ sti();
+ boardp = &ASC_BOARD(scp->host)->board;
+ scp->result = HOST_BYTE(DID_ABORT);
+ switch (AscAbortSRB(boardp, (ulong) scp)) {
+ case ASC_TRUE:
+ /* asc_isr_callback() will be called */
+ ASC_DBG(1, "advansys_abort: AscAbortSRB() TRUE\n");
+ ret = SCSI_ABORT_PENDING;
+ break;
+ case ASC_FALSE:
+ /* Request has apparently already completed. */
+ ASC_DBG(1, "advansys_abort: AscAbortSRB() FALSE\n");
+ ret = SCSI_ABORT_NOT_RUNNING;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "advansys_abort: AscAbortSRB() ERROR\n");
+ ret = SCSI_ABORT_ERROR;
+ break;
+ }
+ (void) AscResetDevice(boardp, scp->target);
+ }
+ restore_flags(flags);
+ ASC_DBG1(1, "advansys_abort: ret %d\n", ret);
+ return ret;
+}
+
+/*
+ * advansys_reset()
+ *
+ * Reset all devices and the SCSI bus for the board
+ * associated with 'scp'.
+ */
+int
+advansys_reset(Scsi_Cmnd *scp)
+{
+ ASC_DVC_VAR *boardp;
+ int flags;
+ Scsi_Cmnd *tscp;
+ int i;
+ int ret;
+
+ ASC_DBG1(1, "advansys_reset: %x\n", (unsigned) scp);
+ save_flags(flags);
+ cli();
+ ASC_STATS(reset);
+ if (scp->host == NULL) {
+ scp->result = HOST_BYTE(DID_ERROR);
+ ret = SCSI_RESET_ERROR;
+ } else {
+ /* Remove any pending commands, set DID_RESET, and done them. */
+ for (i = 0; i < ASC_MAX_TID; i++) {
+ while ((tscp = asc_dequeue(scp->host, i)) != NULL) {
+ tscp->result = HOST_BYTE(DID_RESET);
+ tscp->scsi_done(tscp);
+ }
+ }
+ /* Must enable interrupts for AscResetSB() */
+ sti();
+ boardp = &ASC_BOARD(scp->host)->board;
+ scp->result = HOST_BYTE(DID_RESET);
+ switch (AscResetSB(boardp)) {
+ case ASC_TRUE:
+ ASC_DBG(1, "advansys_abort: AscResetSB() TRUE\n");
+ ret = SCSI_RESET_SUCCESS;
+ break;
+ case ASC_ERROR:
+ default:
+ ASC_DBG(1, "advansys_abort: AscResetSB() ERROR\n");
+ ret = SCSI_RESET_ERROR;
+ break;
+ }
+ }
+ restore_flags(flags);
+ ASC_DBG1(1, "advansys_reset: ret %d", ret);
+ return ret;
+}
+
+/*
+ * advansys_biosparam()
+ *
+ * Translate disk drive geometry if the "BIOS greater than 1 GB"
+ * support is enabled for a drive.
+ *
+ * ip (information pointer) is an int array with the following definition:
+ * ip[0]: heads
+ * ip[1]: sectors
+ * ip[2]: cylinders
+ */
+int
+#ifdef LINUX_1_2
+advansys_biosparam(Disk *dp, int dep, int ip[])
+#else /* LINUX_1_3 */
+advansys_biosparam(Disk *dp, kdev_t dep, int ip[])
+#endif /* LINUX_1_3 */
+{
+ ASC_DBG(1, "advansys_biosparam: begin\n");
+ ASC_STATS(biosparam);
+ if ((ASC_BOARD(dp->device->host)->board.dvc_cntl & ASC_CNTL_BIOS_GT_1GB) &&
+ dp->capacity > 0x200000) {
+ ip[0] = 255;
+ ip[1] = 64;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ }
+ ip[2] = dp->capacity / (ip[0] * ip[1]);
+ ASC_DBG(1, "advansys_biosparam: end\n");
+ return 0;
+}
+
+/*
+ * advansys_setup()
+ *
+ * This function is called from init/main.c at boot time.
+ * It it passed LILO parameters that can be set from the
+ * LILO command line or in /etc/lilo.conf.
+ *
+ * It is used by the AdvanSys driver to either disable I/O
+ * port scanning or to limit scanning to 1 - 4 I/O ports.
+ * Regardless of the option setting EISA and PCI boards
+ * will still be searched for and detected. This option
+ * only affects searching for ISA and VL boards.
+ *
+ * If ADVANSYS_DEBUG is defined the driver debug level may
+ * be set using the 5th (ASC_NUM_BOARD_SUPPORTED + 1) I/O Port.
+ *
+ * Examples:
+ * 1. Eliminate I/O port scanning:
+ * boot: linux advansys=
+ * or
+ * boot: linux advansys=0x0
+ * 2. Limit I/O port scanning to one I/O port:
+ * boot: linux advansys=0x110
+ * 3. Limit I/O port scanning to four I/O ports:
+ * boot: linux advansys=0x110,0x210,0x230,0x330
+ * 4. If ADVANSYS_DEBUG, limit I/O port scanning to four I/O ports and
+ * set the driver debug level to 2.
+ * boot: linux advansys=0x110,0x210,0x230,0x330,0xdeb2
+ *
+ * ints[0] - number of arguments
+ * ints[1] - first argument
+ * ints[2] - second argument
+ * ...
+ */
+void
+advansys_setup(char *str, int *ints)
+{
+ int i;
+
+ if (asc_iopflag == ASC_TRUE) {
+ printk("AdvanSys SCSI: 'advansys' LILO option may appear only once\n");
+ return;
+ }
+
+ asc_iopflag = ASC_TRUE;
+
+ if (ints[0] > ASC_NUM_BOARD_SUPPORTED) {
+#ifdef ADVANSYS_DEBUG
+ if ((ints[0] == ASC_NUM_BOARD_SUPPORTED + 1) &&
+ (ints[ASC_NUM_BOARD_SUPPORTED + 1] >> 4 == 0xdeb)) {
+ asc_dbglvl = ints[ASC_NUM_BOARD_SUPPORTED + 1] & 0xf;
+ } else {
+#endif /* ADVANSYS_DEBUG */
+ printk("AdvanSys SCSI: only %d I/O ports accepted\n",
+ ASC_NUM_BOARD_SUPPORTED);
+#ifdef ADVANSYS_DEBUG
+ }
+#endif /* ADVANSYS_DEBUG */
+ }
+
+#ifdef ADVANSYS_DEBUG
+ ASC_DBG1(1, "advansys_setup: ints[0] %d\n", ints[0]);
+ for (i = 1; i < ints[0]; i++) {
+ ASC_DBG2(1, " ints[%d] %x", i, ints[i]);
+ }
+ ASC_DBG(1, "\n");
+#endif /* ADVANSYS_DEBUG */
+
+ for (i = 1; i <= ints[0] && i <= ASC_NUM_BOARD_SUPPORTED; i++) {
+ asc_ioport[i-1] = ints[i];
+ ASC_DBG2(1, "advansys_setup: asc_ioport[%d] %x\n",
+ i - 1, asc_ioport[i-1]);
+ }
+}
+
+
+/*
+ * --- Loadable Driver Support
+ */
+
+#ifdef MODULE
+Scsi_Host_Template driver_template = ADVANSYS;
+# include "scsi_module.c"
+#endif /* MODULE */
+
+
+/*
+ * --- Miscellaneous Driver Functions
+ */
+
+#ifdef LINUX_1_3
+/*
+ * asc_proc_copy()
+ *
+ * Copy proc information to a read buffer considering the current read
+ * offset in the file and the remaining space in the read buffer.
+ */
+STATIC int
+asc_proc_copy(off_t advoffset, off_t offset, char *curbuf, int leftlen,
+ char *cp, int cplen)
+{
+ int cnt = 0;
+
+ ASC_DBG3(2, "asc_proc_copy: offset %d, advoffset %d, cplen %d\n",
+ (unsigned) offset, (unsigned) advoffset, cplen);
+ if (offset <= advoffset) {
+ /* Read offset below current offset, copy everything. */
+ cnt = min(cplen, leftlen);
+ ASC_DBG3(2, "asc_proc_copy: curbuf %x, cp %x, cnt %d\n",
+ (unsigned) curbuf, (unsigned) cp, cnt);
+ memcpy(curbuf, cp, cnt);
+ } else if (offset < advoffset + cplen) {
+ /* Read offset within current range, partial copy. */
+ cnt = (advoffset + cplen) - offset;
+ cp = (cp + cplen) - cnt;
+ cnt = min(cnt, leftlen);
+ ASC_DBG3(2, "asc_proc_copy: curbuf %x, cp %x, cnt %d\n",
+ (unsigned) curbuf, (unsigned) cp, cnt);
+ memcpy(curbuf, cp, cnt);
+ }
+ return cnt;
+}
+#endif /* LINUX_1_3 */
+
+/*
+ * First-level interrupt handler.
+ */
+STATIC void
+advansys_interrupt(int irq, struct pt_regs *regs)
+{
+ int i;
+ int flags;
+ Scsi_Cmnd *scp;
+ Scsi_Cmnd *tscp;
+
+ /* Disable interrupts, if the aren't already disabled. */
+ save_flags(flags);
+ cli();
+
+ ASC_DBG(1, "advansys_interrupt: begin\n");
+ ASC_STATS(interrupt);
+ /*
+ * Check for interrupts on all boards.
+ * AscISR() will call asc_isr_callback().
+ */
+ for (i = 0; i < asc_board_count; i++) {
+ while (AscIsIntPending(asc_host[i]->io_port)) {
+ ASC_DBG(1, "advansys_interrupt: before AscISR()\n");
+ AscISR(&ASC_BOARD(asc_host[i])->board);
+ }
+ }
+ ASC_DBG(1, "advansys_interrupt: end\n");
+
+ /*
+ * While interrupts are still disabled save the list of requests that
+ * need their done function called. After re-enabling interrupts call
+ * the done function which may re-enable interrupts anyway.
+ */
+ if ((scp = asc_scsi_done) != NULL) {
+ asc_scsi_done = NULL;
+ }
+
+ /* Re-enable interrupts, if they were enabled on entry. */
+ restore_flags(flags);
+
+ while (scp) {
+ tscp = (Scsi_Cmnd *) scp->host_scribble;
+ scp->scsi_done(scp);
+ scp = tscp;
+ }
+
+ return;
+}
+
+/*
+ * Function used only with polled I/O requests that are initiated by
+ * advansys_command().
+ */
+STATIC void
+advansys_command_done(Scsi_Cmnd *scp)
+{
+ ASC_DBG1(1, "advansys_command_done: scp %x\n", (unsigned) scp);
+ scp->SCp.Status = 1;
+}
+
+/*
+ * Execute a single 'Scsi_Cmnd'.
+ *
+ * The function 'done' is called when the request has been completed.
+ *
+ * Scsi_Cmnd:
+ *
+ * host - board controlling device
+ * device - device to send command
+ * target - target of device
+ * lun - lun of device
+ * cmd_len - length of SCSI CDB
+ * cmnd - buffer for SCSI 8, 10, or 12 byte CDB
+ * use_sg - if non-zero indicates scatter-gather request with use_sg elements
+ *
+ * if (use_sg == 0)
+ * request_buffer - buffer address for request
+ * request_bufflen - length of request buffer
+ * else
+ * request_buffer - pointer to scatterlist structure
+ *
+ * sense_buffer - sense command buffer
+ *
+ * result (4 bytes of an int):
+ * Byte Meaning
+ * 0 SCSI Status Byte Code
+ * 1 SCSI One Byte Message Code
+ * 2 Host Error Code
+ * 3 Mid-Level Error Code
+ *
+ * host driver fields:
+ * SCp - Scsi_Pointer used for command processing status
+ * scsi_done - used to save caller's done function
+ * host_scribble - used for pointer to another Scsi_Cmnd
+ *
+ * If this function returns ASC_NOERROR or ASC_ERROR the done
+ * function has been called. If ASC_BUSY is returned the request
+ * must be enqueued by the caller and re-tried later.
+ */
+STATIC int
+asc_execute_scsi_cmnd(Scsi_Cmnd *scp)
+{
+ ASC_DVC_VAR *boardp;
+ ASC_SCSI_Q scsiq;
+ ASC_SG_HEAD sghead;
+ int ret;
+
+ ASC_DBG2(1, "asc_execute_scsi_cmnd: scp %x, done %x\n",
+ (unsigned) scp, (unsigned) scp->scsi_done);
+
+ boardp = &ASC_BOARD(scp->host)->board;
+
+ /*
+ * If this is the first command, then initialize the device. If
+ * no device is found set 'DID_BAD_TARGET' and return.
+ */
+ if ((ASC_BOARD(scp->host)->init_tidmask &
+ ASC_TIX_TO_TARGET_ID(scp->target)) == 0) {
+ if (asc_init_dev(boardp, scp) == ASC_FALSE) {
+ scp->result = HOST_BYTE(DID_BAD_TARGET);
+ scp->scsi_done(scp);
+ return ASC_ERROR;
+ }
+ ASC_BOARD(scp->host)->init_tidmask |= ASC_TIX_TO_TARGET_ID(scp->target);
+ }
+
+ memset(&scsiq, 0, sizeof(ASC_SCSI_Q));
+
+ /*
+ * Point the ASC_SCSI_Q to the 'Scsi_Cmnd'.
+ */
+ scsiq.q2.srb_ptr = (ulong) scp;
+
+ /*
+ * Build the ASC_SCSI_Q request.
+ */
+ scsiq.cdbptr = &scp->cmnd[0];
+ scsiq.q2.cdb_len = scp->cmd_len;
+ scsiq.q1.target_id = ASC_TID_TO_TARGET_ID(scp->target);
+ scsiq.q1.target_lun = scp->lun;
+ scsiq.q2.target_ix = ASC_TIDLUN_TO_IX(scp->target, scp->lun);
+ scsiq.q1.sense_addr = (ulong) &scp->sense_buffer[0];
+ scsiq.q1.sense_len = sizeof(scp->sense_buffer);
+ scsiq.q2.tag_code = M2_QTAG_MSG_SIMPLE;
+
+ /*
+ * Build ASC_SCSI_Q for a contiguous buffer or a scatter-gather
+ * buffer command.
+ */
+ if (scp->use_sg == 0) {
+ /*
+ * CDB request of single contiguous buffer.
+ */
+ ASC_STATS(cont_cnt);
+ /* request_buffer is already a real address. */
+ scsiq.q1.data_addr = (ulong) scp->request_buffer;
+ scsiq.q1.data_cnt = scp->request_bufflen;
+ ASC_STATS_ADD(cont_xfer, (scp->request_bufflen + 511) >> 9);
+ scsiq.q1.sg_queue_cnt = 0;
+ scsiq.sg_head = NULL;
+ } else {
+ /*
+ * CDB scatter-gather request list.
+ */
+ int sgcnt;
+ struct scatterlist *slp;
+
+ if (scp->use_sg > ASC_MAX_SG_LIST) {
+ ASC_DBG2(0, "asc_execute_scsi_cmnd: use_sg %d > %d\n",
+ scp->use_sg, ASC_MAX_SG_LIST);
+ scp->result = HOST_BYTE(DID_ERROR);
+ scp->scsi_done(scp);
+ return ASC_ERROR;
+ }
+
+ ASC_STATS(sg_cnt);
+
+ /*
+ * Allocate a ASC_SG_HEAD structure and set the ASC_SCSI_Q
+ * to point to it.
+ */
+ memset(&sghead, 0, sizeof(ASC_SG_HEAD));
+
+ scsiq.q1.cntl |= QC_SG_HEAD;
+ scsiq.sg_head = &sghead;
+ scsiq.q1.data_cnt = 0;
+ scsiq.q1.data_addr = 0;
+ sghead.entry_cnt = scsiq.q1.sg_queue_cnt = scp->use_sg;
+ ASC_STATS_ADD(sg_elem, sghead.entry_cnt);
+
+ /*
+ * Convert scatter-gather list into ASC_SG_HEAD list.
+ */
+ slp = (struct scatterlist *) scp->request_buffer;
+ for (sgcnt = 0; sgcnt < scp->use_sg; sgcnt++, slp++) {
+ sghead.sg_list[sgcnt].addr = (ulong) slp->address;
+ sghead.sg_list[sgcnt].bytes = slp->length;
+ ASC_STATS_ADD(sg_xfer, (slp->length + 511) >> 9);
+ }
+ }
+
+ ASC_DBG_PRT_SCSI_Q(2, &scsiq);
+ ASC_DBG_PRT_CDB(1, scp->cmnd, scp->cmd_len);
+
+ switch (ret = AscExeScsiQueue(boardp, &scsiq)) {
+ case ASC_NOERROR:
+ ASC_DBG(1, "asc_execute_scsi_cmnd: AscExeScsiQueue() ASC_NOERROR\n");
+ break;
+ case ASC_BUSY:
+ /* Caller must enqueue request and retry later. */
+ break;
+ case ASC_ERROR:
+ ASC_DBG1(0,
+ "asc_execute_scsi_cmnd: AscExeScsiQueue() ASC_ERROR err_code %x\n",
+ boardp->err_code);
+ ASC_STATS(error);
+ scp->result = HOST_BYTE(DID_ERROR);
+ scp->scsi_done(scp);
+ break;
+ }
+
+ ASC_DBG(1, "asc_execute_scsi_cmnd: end\n");
+ return ret;
+}
+
+/*
+ * asc_isr_callback() - Second Level Interrupt Handler called by AscISR().
+ */
+void
+asc_isr_callback(ASC_DVC_VAR *boardp, ASC_QDONE_INFO *qdonep)
+{
+ Scsi_Cmnd *scp;
+ struct Scsi_Host *shp;
+ int flags;
+ Scsi_Cmnd **scpp;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_DBG2(1, "asc_isr_callback: boardp %x, qdonep %x\n",
+ (unsigned) boardp, (unsigned) qdonep);
+ ASC_STATS(callback);
+ ASC_DBG_PRT_QDONE_INFO(2, qdonep);
+
+ /*
+ * Get the Scsi_Cmnd structure and Scsi_Host structure for the
+ * command that has been completed.
+ */
+ scp = (Scsi_Cmnd *) qdonep->d2.srb_ptr;
+ ASC_DBG1(1, "asc_isr_callback: scp %x\n", (unsigned) scp);
+ ASC_DBG_PRT_CDB(2, scp->cmnd, scp->cmd_len);
+
+ shp = scp->host;
+ ASC_ASSERT(shp);
+ ASC_DBG1(1, "asc_isr_callback: shp %x\n", (unsigned) shp);
+
+ /*
+ * 'qdonep' contains the command's ending status.
+ */
+ switch (qdonep->d3.done_stat) {
+ case QD_NO_ERROR:
+ ASC_DBG(2, "asc_isr_callback: QD_NO_ERROR\n");
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ scp->result = 0;
+ break;
+ default:
+ /* QHSTA error occurred */
+ scp->result = HOST_BYTE(DID_ERROR);
+ break;
+ }
+ break;
+
+ case QD_WITH_ERROR:
+ ASC_DBG(2, "asc_isr_callback: QD_WITH_ERROR\n");
+ switch (qdonep->d3.host_stat) {
+ case QHSTA_NO_ERROR:
+ if (qdonep->d3.scsi_stat == SS_CHK_CONDITION) {
+ ASC_DBG(2, "asc_isr_callback: SS_CHK_CONDITION\n");
+ ASC_DBG_PRT_SENSE(2, scp->sense_buffer,
+ sizeof(scp->sense_buffer));
+ /*
+ * Note: The status_byte() macro used by target drivers
+ * defined in scsi.h shifts the status byte returned by
+ * host drivers right by 1 bit. This is why target drivers
+ * also use left shifted status byte definitions. For instance
+ * target drivers use CHECK_CONDITION, defined to 0x1, instead
+ * of the SCSI defined check condition value of 0x2.
+ */
+ scp->result = DRIVER_BYTE(DRIVER_SENSE) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ } else {
+ scp->result = STATUS_BYTE(qdonep->d3.scsi_stat);
+ }
+ break;
+
+ default:
+ /* QHSTA error occurred */
+ ASC_DBG1(2, "asc_isr_callback: host_stat %x\n",
+ qdonep->d3.host_stat);
+ scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+ }
+ break;
+
+ case QD_ABORTED_BY_HOST:
+ ASC_DBG(1, "asc_isr_callback: QD_ABORTED_BY_HOST\n");
+ scp->result = HOST_BYTE(DID_ABORT) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+
+ default:
+ ASC_DBG1(0, "asc_isr_callback: done_stat %x\n", qdonep->d3.done_stat );
+ scp->result = HOST_BYTE(DID_ERROR) | MSG_BYTE(qdonep->d3.scsi_msg) |
+ STATUS_BYTE(qdonep->d3.scsi_stat);
+ break;
+ }
+
+ /*
+ * Before calling 'scsi_done' for the current 'Scsi_Cmnd' and possibly
+ * triggering more commands to be issued, try to start any pending
+ * commands.
+ */
+ if (ASC_BOARD(shp)->pending_tidmask != 0) {
+ /*
+ * If there are any pending commands for this board before trying
+ * to execute them, disable interrupts to preserve request ordering.
+ */
+ ASC_STATS(intr_disable);
+ save_flags(flags);
+ cli();
+ ASC_DBG1(1, "asc_isr_callback: asc_execute_pending() %x\n",
+ ASC_BOARD(shp)->pending_tidmask);
+ asc_execute_pending(shp);
+ restore_flags(flags);
+ }
+
+ /*
+ * Because interrupts may be enabled by the 'Scsi_Cmnd' done function,
+ * add the command to the end of the global done list. The done function
+ * for the command will be called in advansys_interrupt().
+ */
+ for (scpp = &asc_scsi_done; *scpp;
+ scpp = (Scsi_Cmnd **) &(*scpp)->host_scribble) {
+ ;
+ }
+ *scpp = scp;
+ scp->host_scribble = NULL;
+ return;
+}
+
+/*
+ * Execute as many pending requests as possible for the
+ * board specified by 'Scsi_Host'.
+ */
+STATIC void
+asc_execute_pending(struct Scsi_Host *shp)
+{
+ ASC_SCSI_BIT_ID_TYPE scan_tidmask;
+ Scsi_Cmnd *scp;
+ int i;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ /*
+ * Execute pending commands for devices attached to
+ * the current board in round-robin fashion.
+ */
+ scan_tidmask = ASC_BOARD(shp)->pending_tidmask;
+ do {
+ for (i = 0; i < ASC_MAX_TID; i++) {
+ if (scan_tidmask & ASC_TIX_TO_TARGET_ID(i)) {
+ if ((scp = asc_dequeue(shp, i)) == NULL) {
+ scan_tidmask &= ~ASC_TIX_TO_TARGET_ID(i);
+ } else if (asc_execute_scsi_cmnd(scp) == ASC_BUSY) {
+ scan_tidmask &= ~ASC_TIX_TO_TARGET_ID(i);
+ /* Put the request back at front of the list. */
+ asc_enqueue(shp, scp, i, ASC_FRONT);
+ }
+ }
+ }
+ } while (scan_tidmask);
+ return;
+}
+
+/*
+ * asc_init_dev()
+ *
+ * Perform one-time initialization of a device.
+ */
+STATIC int
+asc_init_dev(ASC_DVC_VAR *boardp, Scsi_Cmnd *scp)
+{
+ ASC_SCSI_REQ_Q *scsireqq;
+ ASC_CAP_INFO *cap_info;
+ ASC_SCSI_INQUIRY *inquiry;
+ int found;
+ ASC_SCSI_BIT_ID_TYPE save_use_tagged_qng;
+ ASC_SCSI_BIT_ID_TYPE save_can_tagged_qng;
+ int ret;
+#ifdef ADVANSYS_DEBUG
+ ASC_SCSI_BIT_ID_TYPE tidmask; /* target id bit mask: 1 - 128 */
+#endif /* ADVANSYS_DEBUG */
+
+ ASC_DBG1(1, "asc_init_dev: target %d\n", (unsigned) scp->target);
+
+ /* Return true for the board's target id. */
+ if (boardp->cfg->chip_scsi_id == scp->target) {
+ return ASC_TRUE;
+ }
+
+ /*
+ * XXX - Host drivers should not modify the timeout field.
+ * But on the first command only add some extra time to
+ * allow the driver to complete its initialization for the
+ * device.
+ */
+ scp->timeout += 2000; /* Add 5 seconds to the request timeout. */
+
+ /* Set-up AscInitPollTarget() arguments. */
+ scsireqq = &ASC_BOARD(scp->host)->scsireqq;
+ memset(scsireqq, 0, sizeof(ASC_SCSI_REQ_Q));
+ cap_info = &ASC_BOARD(scp->host)->cap_info;
+ memset(cap_info, 0, sizeof(ASC_CAP_INFO));
+ inquiry = &ASC_BOARD(scp->host)->inquiry;
+ memset(inquiry, 0, sizeof(ASC_SCSI_INQUIRY));
+
+ /*
+ * XXX - AscInitPollBegin() re-initializes these fields to
+ * zero. 'Or' in the new values and restore them before calling
+ * AscInitPollEnd(). Normally all targets are initialized within
+ * a call to AscInitPollBegin() and AscInitPollEnd().
+ */
+ save_use_tagged_qng = boardp->use_tagged_qng;
+ save_can_tagged_qng = boardp->cfg->can_tagged_qng;
+
+ ASC_DBG(2, "asc_init_dev: AscInitPollBegin()\n");
+ if (AscInitPollBegin(boardp)) {
+ ASC_DBG(0, "asc_init_dev: AscInitPollBegin() failed\n");
+ return ASC_FALSE;
+ }
+
+ scsireqq->sense_ptr = &scsireqq->sense[0];
+ scsireqq->r1.sense_len = ASC_MIN_SENSE_LEN;
+ scsireqq->r1.target_id = ASC_TID_TO_TARGET_ID(scp->target);
+ scsireqq->r1.target_lun = 0;
+ scsireqq->r2.target_ix = ASC_TIDLUN_TO_IX(scp->target, 0);
+
+ found = ASC_FALSE;
+ ASC_DBG(2, "asc_init_dev: AscInitPollTarget()\n");
+ switch (ret = AscInitPollTarget(boardp, scsireqq, inquiry, cap_info)) {
+ case ASC_TRUE:
+ found = ASC_TRUE;
+#ifdef ADVANSYS_DEBUG
+ tidmask = ASC_TIX_TO_TARGET_ID(scp->target);
+ ASC_DBG2(1, "asc_init_dev: lba %lu, blk_size %lu\n",
+ cap_info->lba, cap_info->blk_size);
+ ASC_DBG1(1, "asc_init_dev: peri_dvc_type %x\n",
+ inquiry->byte0.peri_dvc_type);
+ if (boardp->use_tagged_qng & tidmask) {
+ ASC_DBG1(1, "asc_init_dev: command queuing enabled: %d\n",
+ boardp->max_dvc_qng[scp->target]);
+ } else {
+ ASC_DBG(1, "asc_init_dev: command queuing disabled\n");
+ }
+ if (boardp->init_sdtr & tidmask) {
+ ASC_DBG(1, "asc_init_dev: synchronous transfers enabled\n");
+ } else {
+ ASC_DBG(1, "asc_init_dev: synchronous transfers disabled\n");
+ }
+ /* Set bit means fix disabled. */
+ if (boardp->pci_fix_asyn_xfer & tidmask) {
+ ASC_DBG(1, "asc_init_dev: synchronous transfer fix disabled\n");
+ } else {
+ ASC_DBG(1, "asc_init_dev: synchronous transfer fix enabled\n");
+ }
+#endif /* ADVANSYS_DEBUG */
+ break;
+ case ASC_FALSE:
+ ASC_DBG(1, "asc_init_dev: no device found\n");
+ break;
+ case ASC_ERROR:
+ ASC_DBG(0, "asc_init_dev: AscInitPollTarget() ASC_ERROR\n");
+ break;
+ default:
+ ASC_DBG1(0, "asc_init_dev: AscInitPollTarget() unknown ret %d\n", ret);
+ break;
+ }
+
+ /* XXX - 'Or' in original tag bits. */
+ boardp->use_tagged_qng |= save_use_tagged_qng;
+ boardp->cfg->can_tagged_qng |= save_can_tagged_qng;
+
+ ASC_DBG(2, "asc_init_dev: AscInitPollEnd()\n");
+ AscInitPollEnd(boardp);
+
+#ifdef ASC_SET_CMD_PER_LUN
+ /*
+ * XXX - Refer to the comment in advansys_detect()
+ * regarding cmd_per_lun.
+ */
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ if (boardp->max_dvc_qng[i] < scp->host->cmd_per_lun) {
+ scp->host->cmd_per_lun = boardp->max_dvc_qng[i];
+ }
+ }
+#endif /* ASC_SET_CMD_PER_LUN */
+
+ return found;
+}
+
+/*
+ * Search for an AdvanSys PCI device in the PCI configuration space.
+ */
+STATIC int
+asc_srch_pci_dev(PCI_DEVICE *pciDevice)
+{
+ int ret;
+ static int scan = 1;
+
+ ASC_DBG(2, "asc_srch_pci_dev: begin\n");
+
+ if (scan) {
+ pciDevice->type = asc_scan_method(pciDevice);
+ scan = 0;
+ ASC_DBG1(2, "asc_srch_pci_dev: type %d\n", pciDevice->type);
+ }
+ ret = asc_pci_find_dev(pciDevice);
+ ASC_DBG1(2, "asc_srch_pci_dev: asc_pci_find_dev() return %d\n", ret);
+ if (ret == PCI_DEVICE_FOUND) {
+ pciDevice->slotNumber = pciDevice->slotFound + 1;
+ pciDevice->startSlot = pciDevice->slotFound + 1;
+ } else {
+ if (pciDevice->bridge > pciDevice->busNumber) {
+ ASC_DBG2(2, "asc_srch_pci_dev: bridge %x, busNumber %x\n",
+ pciDevice->bridge, pciDevice->busNumber);
+ pciDevice->busNumber++;
+ pciDevice->slotNumber = 0;
+ pciDevice->startSlot = 0;
+ pciDevice->endSlot = 0x0f;
+ ret = asc_srch_pci_dev(pciDevice);
+ ASC_DBG1(2, "asc_srch_pci_dev recursive call return %d\n", ret);
+ }
+ }
+ ASC_DBG1(2, "asc_srch_pci_dev: return %d\n", ret);
+ return ret;
+}
+
+/*
+ * Determine the access method to be used for 'pciDevice'.
+ */
+STATIC uchar
+asc_scan_method(PCI_DEVICE *pciDevice)
+{
+ ushort data;
+ PCI_DATA pciData;
+ uchar type;
+ uchar slot;
+
+ ASC_DBG(2, "asc_scan_method: begin\n");
+ memset(&pciData, 0, sizeof(pciData));
+ for (type = 1; type < 3; type++) {
+ pciData.type = type;
+ for (slot = 0; slot < PCI_MAX_SLOT; slot++) {
+ pciData.slot = slot;
+ data = asc_get_cfg_word(&pciData);
+ if ((data != 0xFFFF) && (data != 0x0000)) {
+ ASC_DBG2(4, "asc_scan_method: data %x, type %d\n", data, type);
+ return (type);
+ }
+ }
+ }
+ ASC_DBG1(4, "asc_scan_method: type %d\n", type);
+ return (type);
+}
+
+/*
+ * Check for an AdvanSys PCI device in 'pciDevice'.
+ *
+ * Return PCI_DEVICE_FOUND if found, otherwise return PCI_DEVICE_NOT_FOUND.
+ */
+STATIC int
+asc_pci_find_dev(PCI_DEVICE *pciDevice)
+{
+ PCI_DATA pciData;
+ ushort vendorid, deviceid;
+ uchar classcode, subclass;
+ uchar lslot;
+
+ ASC_DBG(3, "asc_pci_find_dev: begin\n");
+ pciData.type = pciDevice->type;
+ pciData.bus = pciDevice->busNumber;
+ pciData.func = pciDevice->devFunc;
+ lslot = pciDevice->startSlot;
+ for (; lslot < pciDevice->endSlot; lslot++) {
+ pciData.slot = lslot;
+ pciData.offset = VENDORID_OFFSET;
+ vendorid = asc_get_cfg_word(&pciData);
+ ASC_DBG1(3, "asc_pci_find_dev: vendorid %x\n", vendorid);
+ if (vendorid != 0xffff) {
+ pciData.offset = DEVICEID_OFFSET;
+ deviceid = asc_get_cfg_word(&pciData);
+ ASC_DBG1(3, "asc_pci_find_dev: deviceid %x\n", deviceid);
+ if ((vendorid == ASC_PCI_VENDORID) &&
+ ((deviceid == ASC_PCI_DEVICE_ID_REV_A) ||
+ (deviceid == ASC_PCI_DEVICE_ID_REV_B))) {
+ pciDevice->slotFound = lslot;
+ ASC_DBG(3, "asc_pci_find_dev: PCI_DEVICE_FOUND\n");
+ return PCI_DEVICE_FOUND;
+ } else {
+ pciData.offset = SUBCLASS_OFFSET;
+ subclass = asc_get_cfg_byte(&pciData);
+ pciData.offset = CLASSCODE_OFFSET;
+ classcode = asc_get_cfg_byte(&pciData);
+ if ((classcode & PCI_BASE_CLASS_BRIDGE_DEVICE) &&
+ (subclass & PCI_SUB_CLASS_PCI_TO_PCI_BRIDGE_CONTROLLER)) {
+ pciDevice->bridge++;
+ }
+ ASC_DBG2(3, "asc_pci_find_dev: subclass %x, classcode %x\n",
+ subclass, classcode);
+ }
+ }
+ }
+ return PCI_DEVICE_NOT_FOUND;
+}
+
+/*
+ * Read PCI configuration data into 'pciConfig'.
+ */
+STATIC void
+asc_get_pci_cfg(PCI_DEVICE *pciDevice, PCI_CONFIG_SPACE *pciConfig)
+{
+ PCI_DATA pciData;
+ uchar counter;
+ uchar *localConfig;
+
+ ASC_DBG1(4, "asc_get_pci_cfg: slot found - %d\n ",
+ pciDevice->slotFound);
+
+ pciData.type = pciDevice->type;
+ pciData.bus = pciDevice->busNumber;
+ pciData.slot = pciDevice->slotFound;
+ pciData.func = pciDevice->devFunc;
+ localConfig = (uchar *) pciConfig;
+
+ for (counter = 0; counter < sizeof(PCI_CONFIG_SPACE); counter++) {
+ pciData.offset = counter;
+ *localConfig = asc_get_cfg_byte(&pciData);
+ ASC_DBG1(4, "asc_get_pci_cfg: byte %x\n", *localConfig);
+ localConfig++;
+ }
+ ASC_DBG1(4, "asc_get_pci_cfg: counter %d\n", counter);
+}
+
+/*
+ * Read a word (16 bits) from the PCI configuration space.
+ *
+ * The configuration mechanism is checked for the correct access method.
+ */
+STATIC ushort
+asc_get_cfg_word(PCI_DATA *pciData)
+{
+ ushort tmp;
+ ulong address;
+ ulong lbus = pciData->bus;
+ ulong lslot = pciData->slot;
+ ulong lfunc = pciData->func;
+ uchar t2CFA, t2CF8;
+ ushort t1CF8, t1CFA, t1CFC, t1CFE;
+
+ ASC_DBG4(4, "asc_get_cfg_word: type %d, bus %lu, slot %lu, func %lu\n",
+ pciData->type, lbus, lslot, lfunc);
+
+ /*
+ * check type of configuration mechanism
+ */
+ if (pciData->type == 2) {
+ /*
+ * save these registers so we can restore them after we are done
+ */
+ t2CFA = inp(0xCFA); /* save PCI bus register */
+ t2CF8 = inp(0xCF8); /* save config space enable register */
+
+ /*
+ * go out and write the bus and enable registers
+ */
+ /* set for type 1 cycle, if needed */
+ outp(0xCFA, pciData->bus);
+ /* set the function number */
+ outp(0xCF8, 0x10 | (pciData->func << 1)) ;
+
+ /*
+ * read the configuration space type 2 locations
+ */
+ tmp = (ushort) inpw(0xC000 | ((pciData->slot << 8) + pciData->offset));
+ } else {
+ /*
+ * type 1 configuration mechanism
+ *
+ * save the CONFIG_ADDRESS and CONFIG_DATA register values
+ */
+ t1CFC = inpw(0xCFC);
+ t1CFE = inpw(0xCFE);
+ t1CF8 = inpw(0xCF8);
+ t1CFA = inpw(0xCFA);
+
+ /*
+ * enable <31>, bus = <23:16>, slot = <15:11>,
+ * func = <10:8>, reg = <7:2>
+ */
+ address = (ulong) ((lbus << 16) | (lslot << 11) |
+ (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L);
+
+ /*
+ * write out the address to CONFIG_ADDRESS
+ */
+ outl(address, 0xCF8);
+
+ /*
+ * read in the word from CONFIG_DATA
+ */
+ tmp = (ushort) ((inl(0xCFC) >>
+ ((pciData->offset & 2) * 8)) & 0xFFFF);
+ }
+ ASC_DBG1(4, "asc_get_cfg_word: config data: %x\n", tmp);
+ return tmp;
+}
+
+/*
+ * Reads a byte from the PCI configuration space.
+ *
+ * The configuration mechanism is checked for the correct access method.
+ */
+STATIC uchar
+asc_get_cfg_byte(PCI_DATA *pciData)
+{
+ uchar tmp;
+ ulong address;
+ ulong lbus = pciData->bus, lslot = pciData->slot, lfunc = pciData->func;
+ uchar t2CFA, t2CF8;
+ ushort t1CF8, t1CFA, t1CFC, t1CFE;
+
+ ASC_DBG1(4, "asc_get_cfg_byte: type: %d\n", pciData->type);
+
+ /*
+ * check type of configuration mechanism
+ */
+ if (pciData->type == 2) {
+ /*
+ * save these registers so we can restore them after we are done
+ */
+ t2CFA = inp(0xCFA); /* save PCI bus register */
+ t2CF8 = inp(0xCF8); /* save config space enable register */
+
+ /*
+ * go out and write the bus and enable registers
+ */
+ /* set for type 1 cycle, if needed */
+ outp(0xCFA, pciData->bus);
+ /* set the function number */
+ outp(0xCF8, 0x10 | (pciData->func << 1));
+
+ /*
+ * read the configuration space type 2 locations
+ */
+ tmp = inp(0xC000 | ((pciData->slot << 8) + pciData->offset));
+
+ /*
+ * restore the registers used for our transaction
+ */
+ outp(0xCF8, t2CF8); /* restore the enable register */
+ outp(0xCFA, t2CFA); /* restore PCI bus register */
+ } else {
+ /*
+ * type 1 configuration mechanism
+ *
+ * save the CONFIG_ADDRESS and CONFIG_DATA register values
+ */
+ t1CFC = inpw(0xCFC);
+ t1CFE = inpw(0xCFE);
+ t1CF8 = inpw(0xCF8);
+ t1CFA = inpw(0xCFA);
+
+ /*
+ * enable <31>, bus = <23:16>, slot = <15:11>, func = <10:8>,
+ * reg = <7:2>
+ */
+ address = (ulong) ((lbus << 16) | (lslot << 11) |
+ (lfunc << 8) | (pciData->offset & 0xFC) | 0x80000000L);
+
+ /*
+ * write out the address to CONFIG_ADDRESS
+ */
+ outl(address, 0xCF8);
+
+ /*
+ * read in the word from CONFIG_DATA
+ */
+ tmp = (uchar) ((inl(0xCFC) >> ((pciData->offset & 3) * 8)) & 0xFF);
+ }
+ ASC_DBG1(4, "asc_get_cfg_byte: config data: %x\n", tmp);
+ return tmp;
+}
+
+/*
+ * Add a 'Scsi_Cmnd' to the end of specified 'Scsi_Host'
+ * target device pending command list. Set 'pending_tidmask'
+ * to indicate a command is queued for the device.
+ *
+ * 'flag' may be either ASC_FRONT or ASC_BACK.
+ *
+ * The 'Scsi_Cmnd' host_scribble field is used as a next pointer.
+ */
+STATIC void
+asc_enqueue(struct Scsi_Host *shp, Scsi_Cmnd *scp, int tid, int flag)
+{
+ Scsi_Cmnd **scpp;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ASC_ASSERT(flag == ASC_FRONT || flag == ASC_BACK);
+ ASC_STATS(enqueue);
+ if (flag == ASC_FRONT) {
+ scp->host_scribble = (unsigned char *) ASC_BOARD(shp)->pending[tid];
+ ASC_BOARD(shp)->pending[tid] = (Scsi_Cmnd *) scp;
+ } else { /* ASC_BACK */
+ for (scpp = &ASC_BOARD(shp)->pending[tid]; *scpp;
+ scpp = (Scsi_Cmnd **) &(*scpp)->host_scribble) {
+ ;
+ }
+ *scpp = scp;
+ scp->host_scribble = NULL;
+ }
+ ASC_BOARD(shp)->pending_tidmask |= ASC_TIX_TO_TARGET_ID(tid);
+}
+
+/*
+ * Return first pending 'Scsi_Cmnd' on the specified 'Scsi_Host'
+ * for the specified target device. Clear the 'pending_tidmask'
+ * bit for the device if no more commands are left queued for it.
+ *
+ * The 'Scsi_Cmnd' host_scribble field is used as a next pointer.
+ */
+STATIC Scsi_Cmnd *
+asc_dequeue(struct Scsi_Host *shp, int tid)
+{
+ Scsi_Cmnd *scp;
+
+ ASC_STATS(dequeue);
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ if ((scp = ASC_BOARD(shp)->pending[tid]) != NULL) {
+ ASC_BOARD(shp)->pending[tid] = (Scsi_Cmnd *) scp->host_scribble;
+ }
+ if (ASC_BOARD(shp)->pending[tid] == NULL) {
+ ASC_BOARD(shp)->pending_tidmask &= ~ASC_TIX_TO_TARGET_ID(tid);
+ }
+ return scp;
+}
+
+/*
+ * Remove the specified 'Scsi_Cmnd' from the specified 'Scsi_Host'
+ * for the specified target device. Clear the 'pending_tidmask'
+ * bit for the device if no more commands are left queued for it.
+ *
+ * The 'Scsi_Cmnd' host_scribble field is used as a next pointer.
+ *
+ * Return ASC_TRUE if the command was found and removed, otherwise
+ * return ASC_FALSE if the command was not found.
+ */
+STATIC int
+asc_rmqueue(struct Scsi_Host *shp, Scsi_Cmnd *scp, int tid)
+{
+ Scsi_Cmnd **scpp;
+ int ret;
+
+ ASC_ASSERT(interrupts_enabled() == ASC_FALSE);
+ ret = ASC_FALSE;
+ for (scpp = &ASC_BOARD(shp)->pending[tid];
+ *scpp; scpp = (Scsi_Cmnd **) &(*scpp)->host_scribble) {
+ if (*scpp == scp) {
+ *scpp = (Scsi_Cmnd *) scp->host_scribble;
+ scp->host_scribble = NULL;
+ ASC_STATS(rmqueue);
+ ret = ASC_TRUE;
+ }
+ }
+ if (ASC_BOARD(shp)->pending[tid] == NULL) {
+ ASC_BOARD(shp)->pending_tidmask &= ~ASC_TIX_TO_TARGET_ID(tid);
+ }
+ return ret;
+}
+
+
+/*
+ * --- Functions Required by the Asc Library
+ */
+
+/*
+ * Delay for 'n' milliseconds. Don't use the 'jiffies'
+ * global variable which is incremented once every 5 ms
+ * from a timer interrupt, because this function may be
+ * called when interrupts are disabled.
+ */
+void
+DvcSleepMilliSecond(ulong n)
+{
+ ulong i;
+
+ ASC_DBG1(4, "DvcSleepMilliSecond: %lu\n", n);
+ for (i = 0; i < n; i++) {
+ udelay(1000);
+ }
+}
+
+void
+DvcDisplayString(uchar *s)
+{
+ printk(s);
+}
+
+int
+DvcEnterCritical(void)
+{
+ int flags;
+
+ save_flags(flags);
+ cli();
+ return flags;
+}
+
+void
+DvcLeaveCritical(int flags)
+{
+ restore_flags(flags);
+}
+
+/*
+ * Convert a virtual address to a virtual address.
+ *
+ * Apparently Linux is loaded V=R (virtual equals real). Just return
+ * the virtual address.
+ */
+ulong
+DvcGetPhyAddr(uchar *buf_addr, ulong buf_len)
+{
+ ulong phys_addr;
+
+ phys_addr = (ulong) buf_addr;
+ return phys_addr;
+}
+
+ulong
+DvcGetSGList(ASC_DVC_VAR *asc_dvc_sg, uchar *buf_addr, ulong buf_len,
+ ASC_SG_HEAD *asc_sg_head_ptr)
+{
+ ulong buf_size;
+
+ buf_size = buf_len;
+ asc_sg_head_ptr->entry_cnt = 1;
+ asc_sg_head_ptr->sg_list[0].addr = (ulong) buf_addr;
+ asc_sg_head_ptr->sg_list[0].bytes = buf_size;
+ return buf_size;
+}
+
+/*
+ * void
+ * DvcPutScsiQ(PortAddr iop_base, ushort s_addr, ushort *outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * Output an ASC_SCSI_Q structure to the chip
+ */
+void
+DvcPutScsiQ(PortAddr iop_base, ushort s_addr, ushort *outbuf, int words)
+{
+ int i;
+
+ ASC_DBG_PRT_HEX(2, "DvcPutScsiQ", (uchar *) outbuf, 2 * words);
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++, outbuf++) {
+ if (i == 2 || i == 10) {
+ continue;
+ }
+ AscPutChipLramData(iop_base, *outbuf);
+ }
+}
+
+/*
+ * void
+ * DvcGetQinfo(PortAddr iop_base, ushort s_addr, ushort *inbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * Input an ASC_QDONE_INFO structure from the chip
+ */
+void
+DvcGetQinfo(PortAddr iop_base, ushort s_addr, ushort *inbuf, int words)
+{
+ int i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++, inbuf++) {
+ if (i == 5) {
+ continue;
+ }
+ *inbuf = AscGetChipLramData(iop_base);
+ }
+ ASC_DBG_PRT_HEX(2, "DvcGetQinfo", (uchar *) inbuf, 2 * words);
+}
+
+/*
+ * void DvcOutPortWords(ushort iop_base, ushort &outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * output a buffer to an i/o port address
+ */
+void
+DvcOutPortWords(ushort iop_base, ushort *outbuf, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++, outbuf++)
+ outpw(iop_base, *outbuf);
+}
+
+/*
+ * void DvcInPortWords(ushort iop_base, ushort &outbuf, int words)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * input a buffer from an i/o port address
+ */
+void
+DvcInPortWords(ushort iop_base, ushort *inbuf, int words)
+{
+ int i;
+
+ for (i = 0; i < words; i++, inbuf++)
+ *inbuf = inpw(iop_base);
+}
+
+
+/*
+ * void DvcOutPortDWords(PortAddr port, ulong *pdw, int dwords)
+ *
+ * Calling/Exit State:
+ * none
+ *
+ * Description:
+ * output a buffer of 32-bit integers to an i/o port address in
+ * 16 bit integer units
+ */
+void
+DvcOutPortDWords(PortAddr port, ulong *pdw, int dwords)
+{
+ int i;
+ int words;
+ ushort *pw;
+
+ pw = (ushort *) pdw;
+ words = dwords << 1;
+ for(i = 0; i < words; i++, pw++) {
+ outpw(port, *pw);
+ }
+ return;
+}
+
+
+/*
+ * --- Tracing and Debugging Functions
+ */
+
+#ifdef ADVANSYS_STATS
+
+#define ASC_PRT_STATS_NEXT() \
+ if (cp) { \
+ totlen += len; \
+ leftlen -= len; \
+ if (leftlen == 0) { \
+ return totlen; \
+ } \
+ cp += len; \
+ }
+
+/*
+ * asc_prt_stats()
+ *
+ * Note: no single line should be greater than 160 characters, cf.
+ * asc_prt_stats_line().
+ *
+ * Return the number of characters copied into 'cp'. No more than
+ * 'cplen' characters will be copied to 'cp'.
+ */
+STATIC int
+asc_prt_stats(char *cp, int cplen)
+{
+ struct asc_stats *s;
+ int leftlen;
+ int totlen;
+ int len;
+
+ s = &asc_stats;
+ leftlen = cplen;
+ totlen = len = 0;
+
+ len = asc_prt_stats_line(cp, leftlen,
+"\nAdvanSys SCSI Host Driver Statistics:\n");
+ ASC_PRT_STATS_NEXT();
+
+ len = asc_prt_stats_line(cp, leftlen,
+" command %lu, queuecommand %lu, abort %lu, reset %lu, biosparam %lu,\n",
+ s->command, s->queuecommand, s->abort, s->reset, s->biosparam);
+ ASC_PRT_STATS_NEXT();
+
+ len = asc_prt_stats_line(cp, leftlen,
+" interrupt %lu, callback %lu, cmd_disable %lu, intr_disable %lu,\n",
+ s->interrupt, s->callback, s->cmd_disable, s->intr_disable);
+ ASC_PRT_STATS_NEXT();
+
+ len = asc_prt_stats_line(cp, leftlen,
+" error %lu, enqueue %lu, dequeue %lu, rmqueue %lu,\n",
+ s->error, s->enqueue, s->dequeue, s->rmqueue);
+ ASC_PRT_STATS_NEXT();
+
+ if (s->cont_cnt > 0) {
+ len = asc_prt_stats_line(cp, leftlen,
+" cont_cnt %lu, cont_xfer %lu: avg_xfer=%lu kb\n",
+ s->cont_cnt, s->cont_xfer, (s->cont_xfer/2)/s->cont_cnt);
+ ASC_PRT_STATS_NEXT();
+ }
+
+ if (s->sg_cnt > 0) {
+ len = asc_prt_stats_line(cp, leftlen,
+" sg_cnt %lu, sg_elem %lu, sg_xfer %lu: avg_elem=%lu, avg_size=%lu kb\n",
+ s->sg_cnt, s->sg_elem, s->sg_xfer,
+ s->sg_elem/s->sg_cnt, (s->sg_xfer/2)/s->sg_cnt);
+ ASC_PRT_STATS_NEXT();
+ }
+
+ return totlen;
+}
+
+/*
+ * asc_prt_stats_line()
+ *
+ * If 'cp' is NULL print to the console, otherwise print to a buffer.
+ *
+ * Return 0 if printing to the console, otherwise return the number of
+ * bytes written to the buffer.
+ *
+ * Note: If any single line is greater than 160 bytes the stack
+ * will be corrupted. 's[]' is defined to be 160 bytes.
+ */
+int
+asc_prt_stats_line(char *buf, int buflen, char *fmt, ...)
+{
+ va_list args;
+ int ret;
+ char s[160]; /* 2 lines */
+
+ va_start(args, fmt);
+ ret = vsprintf(s, fmt, args);
+ if (buf == NULL) {
+ (void) printk(s);
+ ret = 0;
+ } else {
+ ret = min(buflen, ret);
+ memcpy(buf, s, ret);
+ }
+ va_end(args);
+ return ret;
+}
+#endif /* ADVANSYS_STATS */
+
+#ifdef ADVANSYS_DEBUG
+/*
+ * asc_prt_scsi_host()
+ */
+STATIC void
+asc_prt_scsi_host(struct Scsi_Host *s)
+{
+ printk("Scsi_Host at addr %x\n", (unsigned) s);
+ printk(
+" next %x, extra_bytes %u, host_busy %u, host_no %d, last_reset %d,\n",
+ (unsigned) s->next, s->extra_bytes, s->host_busy, s->host_no,
+ s->last_reset);
+
+ printk(
+" host_wait %x, host_queue %x, hostt %x, block %x,\n",
+ (unsigned) s->host_wait, (unsigned) s->host_queue,
+ (unsigned) s->hostt, (unsigned) s->block);
+
+ printk(
+" wish_block %d, base %x, io_port %d, n_io_port %d, irq %d, dma_channel %d,\n",
+ s->wish_block, (unsigned) s->base, s->io_port, s->n_io_port,
+ s->irq, s->dma_channel);
+
+ printk(
+" this_id %d, can_queue %d,\n", s->this_id, s->can_queue);
+
+ printk(
+" cmd_per_lun %d, sg_tablesize %d, unchecked_isa_dma %d, loaded_as_module %d\n",
+ s->cmd_per_lun, s->sg_tablesize, s->unchecked_isa_dma,
+ s->loaded_as_module);
+
+ printk("hostdata (struct asc_board)\n");
+ asc_prt_dvc_var(&ASC_BOARD(s)->board);
+ asc_prt_dvc_cfg(&ASC_BOARD(s)->cfg);
+ printk(" overrun_buf %x\n", (unsigned) &ASC_BOARD(s)->overrun_buf[0]);
+}
+
+/*
+ * asc_prt_dvc_var()
+ */
+STATIC void
+asc_prt_dvc_var(ASC_DVC_VAR *h)
+{
+ printk("ASC_DVC_VAR at addr %x\n", (unsigned) h);
+
+ printk(
+" iop_base %x, err_code %x, dvc_cntl %x, bug_fix_cntl %d,\n",
+ h->iop_base, h->err_code, h->dvc_cntl, h->bug_fix_cntl);
+
+ printk(
+" bus_type %d, isr_callback %x, exe_callback %x, init_sdtr %x,\n",
+ h->bus_type, (unsigned) h->isr_callback, (unsigned) h->exe_callback,
+ (unsigned) h->init_sdtr);
+
+ printk(
+" sdtr_done %x, use_tagged_qng %x, unit_not_ready %x, chip_no %x,\n",
+ (unsigned) h->sdtr_done, (unsigned) h->use_tagged_qng,
+ (unsigned) h->unit_not_ready, (unsigned) h->chip_no);
+
+ printk(
+" queue_full_or_busy %x, start_motor %x, scsi_reset_wait %x, irq_no %x,\n",
+ (unsigned) h->queue_full_or_busy, (unsigned) h->start_motor,
+ (unsigned) h->scsi_reset_wait, (unsigned) h->irq_no);
+
+ printk(
+" is_in_int %x, max_total_qng %x, cur_total_qng %x, in_critical_cnt %x,\n",
+ (unsigned) h->is_in_int, (unsigned) h->max_total_qng,
+ (unsigned) h->cur_total_qng, (unsigned) h->in_critical_cnt);
+
+ printk(
+" last_q_shortage %x, init_state %x, no_scam %x, pci_fix_asyn_xfer %x,\n",
+ (unsigned) h->last_q_shortage, (unsigned) h->init_state,
+ (unsigned) h->no_scam, (unsigned) h->pci_fix_asyn_xfer);
+
+ printk(
+" int_count %ld, req_count %ld, busy_count %ld, cfg %x, saved_ptr2func %x\n",
+ h->int_count, h->req_count, h->busy_count, (unsigned) h->cfg,
+ (unsigned) h->saved_ptr2func);
+}
+
+/*
+ * asc_prt_dvc_cfg()
+ */
+STATIC void
+asc_prt_dvc_cfg(ASC_DVC_CFG *h)
+{
+ printk("ASC_DVC_CFG at addr %x\n", (unsigned) h);
+
+ printk(
+" can_tagged_qng %x, cmd_qng_enabled %x, disc_enable %x, res %x,\n",
+ h->can_tagged_qng, h->cmd_qng_enabled, h->disc_enable, h->res);
+
+ printk(
+" chip_scsi_id %d, isa_dma_speed %d, isa_dma_channel %d, chip_version %d,\n",
+ h->chip_scsi_id, h->isa_dma_speed, h->isa_dma_channel,
+ h->chip_version);
+
+ printk(
+" pci_device_id %d, lib_serial_no %d, lib_version %d, mcode_date %d,\n",
+ h->pci_device_id, h->lib_serial_no, h->lib_version, h->mcode_date);
+
+ printk(
+" mcode_version %d, overrun_buf %x\n",
+ h->mcode_version, (unsigned) h->overrun_buf);
+}
+
+/*
+ * asc_prt_scsi_q()
+ */
+STATIC void
+asc_prt_scsi_q(ASC_SCSI_Q *q)
+{
+ ASC_SG_HEAD *sgp;
+ int i;
+
+ printk("ASC_SCSI_Q at addr %x\n", (unsigned) q);
+
+ printk(
+" target_ix %u, target_lun %u, srb_ptr %x, tag_code %u,\n",
+ q->q2.target_ix, q->q1.target_lun,
+ (unsigned) q->q2.srb_ptr, q->q2.tag_code);
+
+ printk(
+" data_addr %x, data_cnt %lu, sense_addr %x, sense_len %u,\n",
+ (unsigned) q->q1.data_addr, q->q1.data_cnt,
+ (unsigned) q->q1.sense_addr, q->q1.sense_len);
+
+ printk(
+" cdbptr %x, cdb_len %u, sg_head %x, sg_queue_cnt %u\n",
+ (unsigned) q->cdbptr, q->q2.cdb_len,
+ (unsigned) q->sg_head, q->q1.sg_queue_cnt);
+
+ if (q->sg_head) {
+ sgp = q->sg_head;
+ printk("ASC_SG_HEAD at addr %x\n", (unsigned) sgp);
+ printk(" entry_cnt %u, queue_cnt %u\n", sgp->entry_cnt, sgp->queue_cnt);
+ for (i = 0; i < sgp->entry_cnt; i++) {
+ printk(" [%u]: addr %x, bytes %lu\n",
+ i, (unsigned) sgp->sg_list[i].addr, sgp->sg_list[i].bytes);
+ }
+
+ }
+}
+
+/*
+ * asc_prt_qdone_info()
+ */
+STATIC void
+asc_prt_qdone_info(ASC_QDONE_INFO *q)
+{
+ printk("ASC_QDONE_INFO at addr %x\n", (unsigned) q);
+ printk(
+" srb_ptr %x, target_ix %u, cdb_len %u, tag_code %u, done_stat %x\n",
+ (unsigned) q->d2.srb_ptr, q->d2.target_ix, q->d2.cdb_len,
+ q->d2.tag_code, q->d3.done_stat);
+ printk(
+" host_stat %x, scsi_stat %x, scsi_msg %x\n",
+ q->d3.host_stat, q->d3.scsi_stat, q->d3.scsi_msg);
+}
+
+/*
+ * asc_prt_hex()
+ *
+ * Print hexadecimal output in 4 byte groupings 32 bytes
+ * or 8 double-words per line.
+ */
+STATIC void
+asc_prt_hex(char *f, uchar *s, int l)
+{
+ int i;
+ int j;
+ int k;
+ int m;
+
+ printk("%s: (%d bytes)\n", f, l);
+
+ for (i = 0; i < l; i += 32) {
+
+ /* Display a maximum of 8 double-words per line. */
+ if ((k = (l - i) / 4) >= 8) {
+ k = 8;
+ m = 0;
+ } else {
+ m = (l - i) % 4 ;
+ }
+
+ for (j = 0; j < k; j++) {
+ printk(" %2.2X%2.2X%2.2X%2.2X",
+ (unsigned) s[i+(j*4)], (unsigned) s[i+(j*4)+1],
+ (unsigned) s[i+(j*4)+2], (unsigned) s[i+(j*4)+3]);
+ }
+
+ switch (m) {
+ case 0:
+ default:
+ break;
+ case 1:
+ printk(" %2.2X",
+ (unsigned) s[i+(j*4)+4]);
+ break;
+ case 2:
+ printk(" %2.2X%2.2X",
+ (unsigned) s[i+(j*4)+4],
+ (unsigned) s[i+(j*4)+5]);
+ break;
+ case 3:
+ printk(" %2.2X%2.2X%2.2X",
+ (unsigned) s[i+(j*4)+4],
+ (unsigned) s[i+(j*4)+5],
+ (unsigned) s[i+(j*4)+6]);
+ break;
+ }
+
+ printk("\n");
+ }
+}
+
+/*
+ * interrupts_enabled()
+ *
+ * Return 1 if interrupts are enabled, otherwise return 0.
+ */
+STATIC int
+interrupts_enabled(void)
+{
+ int flags;
+
+ save_flags(flags);
+ if (flags & 0x0200) {
+ return ASC_TRUE;
+ } else {
+ return ASC_FALSE;
+ }
+}
+
+#endif /* ADVANSYS_DEBUG */
+
+
+/*
+ * --- Asc Library Functions
+ */
+
+ushort
+AscGetEisaChipCfg(
+ PortAddr iop_base
+)
+{
+ PortAddr eisa_cfg_iop;
+
+ eisa_cfg_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
+ (PortAddr) (ASC_EISA_CFG_IOP_MASK);
+ return (inpw(eisa_cfg_iop));
+}
+
+uchar
+AscSetChipScsiID(
+ PortAddr iop_base,
+ uchar new_host_id
+)
+{
+ ushort cfg_lsw;
+
+ if (AscGetChipScsiID(iop_base) == new_host_id) {
+ return (new_host_id);
+ }
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ cfg_lsw &= 0xF8FF;
+ cfg_lsw |= (ushort) ((new_host_id & ASC_MAX_TID) << 8);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetChipScsiID(iop_base));
+}
+
+ushort
+AscGetChipBiosAddress(
+ PortAddr iop_base,
+ ushort bus_type
+)
+{
+ ushort cfg_lsw;
+ ushort bios_addr;
+
+ if ((bus_type & ASC_IS_EISA) != 0) {
+ cfg_lsw = AscGetEisaChipCfg(iop_base);
+ cfg_lsw &= 0x000F;
+ bios_addr = (ushort) (ASC_BIOS_MIN_ADDR +
+ (cfg_lsw * ASC_BIOS_BANK_SIZE));
+ return (bios_addr);
+ }
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+ bios_addr = (ushort) (((cfg_lsw >> 12) * ASC_BIOS_BANK_SIZE) + ASC_BIOS_MIN_ADDR);
+ return (bios_addr);
+}
+
+uchar
+AscGetChipVersion(
+ PortAddr iop_base,
+ ushort bus_type
+)
+{
+ if ((bus_type & ASC_IS_EISA) != 0) {
+
+ PortAddr eisa_iop;
+ uchar revision;
+
+ eisa_iop = (PortAddr) ASC_GET_EISA_SLOT(iop_base) |
+ (PortAddr) ASC_EISA_REV_IOP_MASK;
+ revision = inp(eisa_iop);
+ return ((uchar) ((ASC_CHIP_MIN_VER_EISA - 1) + revision));
+ }
+ return (AscGetChipVerNo(iop_base));
+}
+
+ushort
+AscGetChipBusType(
+ PortAddr iop_base
+)
+{
+ ushort chip_ver;
+
+ chip_ver = AscGetChipVerNo(iop_base);
+ if ((chip_ver >= ASC_CHIP_MIN_VER_VL) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_VL)) {
+ if (((iop_base & 0x0C30) == 0x0C30) ||
+ ((iop_base & 0x0C50) == 0x0C50)) {
+ return (ASC_IS_EISA);
+ }
+ return (ASC_IS_VL);
+ } else if ((chip_ver >= ASC_CHIP_MIN_VER_ISA) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_ISA)) {
+ if (chip_ver >= ASC_CHIP_MIN_VER_ISA_PNP) {
+ return (ASC_IS_ISAPNP);
+ }
+ return (ASC_IS_ISA);
+ } else if ((chip_ver >= ASC_CHIP_MIN_VER_PCI) &&
+ (chip_ver <= ASC_CHIP_MAX_VER_PCI)) {
+ return (ASC_IS_PCI);
+ } else {
+ return (0);
+ }
+}
+
+void
+AscEnableIsaDma(
+ uchar dma_channel
+)
+{
+ if (dma_channel < 4) {
+ outp(0x000B, (ushort) (0xC0 | dma_channel));
+ outp(0x000A, dma_channel);
+ } else if (dma_channel < 8) {
+
+ outp(0x00D6, (ushort) (0xC0 | (dma_channel - 4)));
+ outp(0x00D4, (ushort) (dma_channel - 4));
+ }
+ return;
+}
+
+ulong
+AscLoadMicroCode(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort dosfar * mcode_buf,
+ ushort mcode_size
+)
+{
+ ulong chksum;
+ ushort mcode_word_size;
+ ushort mcode_chksum;
+
+ mcode_word_size = (ushort) (mcode_size >> 1);
+ AscMemWordSetLram(iop_base, s_addr, 0, mcode_word_size);
+ AscMemWordCopyToLram(iop_base, s_addr, mcode_buf, mcode_word_size);
+
+ chksum = AscMemSumLramWord(iop_base, s_addr, mcode_word_size);
+ mcode_chksum = (ushort) AscMemSumLramWord(iop_base,
+ (ushort) ASC_CODE_SEC_BEG,
+ (ushort) ((mcode_size - s_addr - (ushort) ASC_CODE_SEC_BEG) / 2));
+ AscWriteLramWord(iop_base, ASCV_MCODE_CHKSUM_W, mcode_chksum);
+ AscWriteLramWord(iop_base, ASCV_MCODE_SIZE_W, mcode_size);
+ return (chksum);
+}
+
+uchar _hextbl_[16] =
+{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
+ 'A', 'B', 'C', 'D', 'E', 'F'};
+
+uchar _isa_pnp_inited = 0;
+
+PortAddr _asc_def_iop_base[ASC_IOADR_TABLE_MAX_IX] =
+{
+ 0x100, ASC_IOADR_1, 0x120, ASC_IOADR_2, 0x140, ASC_IOADR_3, ASC_IOADR_4,
+ ASC_IOADR_5, ASC_IOADR_6, ASC_IOADR_7, ASC_IOADR_8
+};
+
+PortAddr
+AscSearchIOPortAddr(
+ PortAddr iop_beg,
+ ushort bus_type
+)
+{
+ if (bus_type & ASC_IS_VL) {
+ while ((iop_beg = AscSearchIOPortAddr11(iop_beg)) != 0) {
+ if (AscGetChipVersion(iop_beg, bus_type) <= ASC_CHIP_MAX_VER_VL) {
+ return (iop_beg);
+ }
+ }
+ return (0);
+ }
+ if (bus_type & ASC_IS_ISA) {
+ if (_isa_pnp_inited == 0) {
+ AscSetISAPNPWaitForKey();
+ _isa_pnp_inited++;
+ }
+ while ((iop_beg = AscSearchIOPortAddr11(iop_beg)) != 0) {
+ if ((AscGetChipVersion(iop_beg, bus_type) & ASC_CHIP_VER_ISA_BIT) != 0) {
+ return (iop_beg);
+ }
+ }
+ return (0);
+ }
+ if (bus_type & ASC_IS_EISA) {
+ if ((iop_beg = AscSearchIOPortAddrEISA(iop_beg)) != 0) {
+ return (iop_beg);
+ }
+ return (0);
+ }
+ return (0);
+}
+
+PortAddr
+AscSearchIOPortAddr11(
+ PortAddr s_addr
+)
+{
+
+ int i;
+ PortAddr iop_base;
+
+ for (i = 0; i < ASC_IOADR_TABLE_MAX_IX; i++) {
+ if (_asc_def_iop_base[i] > s_addr) {
+ break;
+ }
+ }
+ for (; i < ASC_IOADR_TABLE_MAX_IX; i++) {
+ iop_base = _asc_def_iop_base[i];
+ if (AscFindSignature(iop_base)) {
+ return (iop_base);
+ }
+ }
+ return (0);
+}
+
+int
+AscFindSignature(
+ PortAddr iop_base
+)
+{
+ ushort sig_word;
+
+ if ((inp((PortAddr) (iop_base + 1)) & 0xFF) == (uchar) ASC_1000_ID1B) {
+ sig_word = inpw(iop_base);
+ if ((sig_word == (ushort) ASC_1000_ID0W) ||
+ (sig_word == (ushort) ASC_1000_ID0W_FIX)) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+void
+AscToggleIRQAct(
+ PortAddr iop_base
+)
+{
+ AscSetChipStatus(iop_base, CIW_IRQ_ACT);
+ AscSetChipStatus(iop_base, 0);
+ return;
+}
+
+#if CC_INIT_INQ_DISPLAY
+
+#endif
+
+void
+AscSetISAPNPWaitForKey(
+ void)
+{
+
+ outp(ASC_ISA_PNP_PORT_ADDR, 0x02);
+ outp(ASC_ISA_PNP_PORT_WRITE, 0x02);
+ return;
+}
+
+uchar
+AscGetChipIRQ(
+ PortAddr iop_base,
+ ushort bus_type
+)
+{
+ ushort cfg_lsw;
+ uchar chip_irq;
+
+ if ((bus_type & ASC_IS_EISA) != 0) {
+
+ cfg_lsw = AscGetEisaChipCfg(iop_base);
+ chip_irq = (uchar) (((cfg_lsw >> 8) & 0x07) + 10);
+ if ((chip_irq == 13) || (chip_irq > 15)) {
+
+ return (0);
+ }
+ return (chip_irq);
+ } else {
+
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+
+ if ((bus_type & ASC_IS_VL) != 0) {
+
+ chip_irq = (uchar) (((cfg_lsw >> 2) & 0x07));
+ if ((chip_irq == 0) ||
+ (chip_irq == 4) ||
+ (chip_irq == 7)) {
+ return (0);
+ }
+ return ((uchar) (chip_irq + (ASC_MIN_IRQ_NO - 1)));
+ }
+ chip_irq = (uchar) (((cfg_lsw >> 2) & 0x03));
+ if (chip_irq == 3)
+ chip_irq += (uchar) 2;
+ return ((uchar) (chip_irq + ASC_MIN_IRQ_NO));
+ }
+}
+
+uchar
+AscSetChipIRQ(
+ PortAddr iop_base,
+ uchar irq_no,
+ ushort bus_type
+)
+{
+ ushort cfg_lsw;
+
+ if ((bus_type & ASC_IS_VL) != 0) {
+
+ if (irq_no != 0) {
+ if ((irq_no < ASC_MIN_IRQ_NO) || (irq_no > ASC_MAX_IRQ_NO)) {
+ irq_no = 0;
+ } else {
+ irq_no -= (uchar) ((ASC_MIN_IRQ_NO - 1));
+ }
+ }
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFE3);
+ cfg_lsw |= (ushort) 0x0010;
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ AscToggleIRQAct(iop_base);
+
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFE0);
+ cfg_lsw |= (ushort) ((irq_no & 0x07) << 2);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ AscToggleIRQAct(iop_base);
+
+ return (AscGetChipIRQ(iop_base, bus_type));
+
+ } else if ((bus_type & (ASC_IS_ISA)) != 0) {
+
+ if (irq_no == 15)
+ irq_no -= (uchar) 2;
+ irq_no -= (uchar) ASC_MIN_IRQ_NO;
+ cfg_lsw = (ushort) (AscGetChipCfgLsw(iop_base) & 0xFFF3);
+ cfg_lsw |= (ushort) ((irq_no & 0x03) << 2);
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetChipIRQ(iop_base, bus_type));
+ } else {
+
+ return (0);
+ }
+}
+
+uchar
+AscGetChipScsiCtrl(
+ PortAddr iop_base
+)
+{
+ uchar sc;
+
+ AscSetBank(iop_base, 1);
+ sc = inp(iop_base + IOP_REG_SC);
+ AscSetBank(iop_base, 0);
+ return (sc);
+}
+
+extern uchar _sdtr_period_tbl_[];
+
+int
+AscIsrChipHalted(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ SDTR_XMSG sdtr_xmsg;
+ SDTR_XMSG out_msg;
+ ushort halt_q_addr;
+ int sdtr_accept;
+ ushort int_halt_code;
+ ASC_SCSI_BIT_ID_TYPE scsi_busy;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ PortAddr iop_base;
+ uchar tag_code;
+ uchar q_status;
+ uchar halt_qp;
+ uchar sdtr_data;
+ uchar target_ix;
+ uchar q_cntl, tid_no;
+ uchar cur_dvc_qng;
+ uchar asyn_sdtr;
+ uchar scsi_status;
+
+ iop_base = asc_dvc->iop_base;
+ int_halt_code = AscReadLramWord(iop_base, ASCV_HALTCODE_W);
+
+ halt_qp = AscReadLramByte(iop_base, ASCV_CURCDB_B);
+ halt_q_addr = ASC_QNO_TO_QADDR(halt_qp);
+ target_ix = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TARGET_IX));
+ q_cntl = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL));
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ target_id = (uchar) ASC_TID_TO_TARGET_ID(tid_no);
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+
+ asyn_sdtr = ASYN_SDTR_DATA_FIX_PCI_REV_AB;
+ } else {
+ asyn_sdtr = 0;
+ }
+ if (int_halt_code == ASC_HALT_EXTMSG_IN) {
+
+ AscMemWordCopyFromLram(iop_base,
+ ASCV_MSGIN_BEG,
+ (ushort dosfar *) & sdtr_xmsg,
+ (ushort) (sizeof (SDTR_XMSG) >> 1));
+ if ((sdtr_xmsg.msg_type == MS_EXTEND) &&
+ (sdtr_xmsg.msg_len == MS_SDTR_LEN)) {
+ sdtr_accept = TRUE;
+ if (sdtr_xmsg.msg_req == MS_SDTR_CODE) {
+ if (sdtr_xmsg.req_ack_offset > ASC_SYN_MAX_OFFSET) {
+
+ sdtr_accept = FALSE;
+ sdtr_xmsg.req_ack_offset = ASC_SYN_MAX_OFFSET;
+ }
+ sdtr_data = AscCalSDTRData(sdtr_xmsg.xfer_period,
+ sdtr_xmsg.req_ack_offset);
+ if (sdtr_xmsg.req_ack_offset == 0) {
+
+ q_cntl &= ~QC_MSG_OUT;
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ } else if ((sdtr_data == 0xFF)) {
+
+ q_cntl |= QC_MSG_OUT;
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+ } else {
+ if (sdtr_accept && (q_cntl & QC_MSG_OUT)) {
+
+ q_cntl &= ~QC_MSG_OUT;
+ asc_dvc->sdtr_done |= target_id;
+ asc_dvc->init_sdtr |= target_id;
+ asc_dvc->pci_fix_asyn_xfer &= ~target_id;
+ AscSetChipSDTR(iop_base, sdtr_data, tid_no);
+ } else {
+
+ q_cntl |= QC_MSG_OUT;
+
+ AscMsgOutSDTR(iop_base,
+ sdtr_xmsg.xfer_period,
+ sdtr_xmsg.req_ack_offset);
+ asc_dvc->pci_fix_asyn_xfer &= ~target_id;
+ AscSetChipSDTR(iop_base, sdtr_data, tid_no);
+ asc_dvc->sdtr_done |= target_id;
+ asc_dvc->init_sdtr |= target_id;
+ }
+ }
+
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ }
+ }
+ } else if (int_halt_code == ASC_HALT_CHK_CONDITION) {
+
+ q_cntl |= QC_REQ_SENSE;
+ if (((asc_dvc->init_sdtr & target_id) != 0) &&
+ ((asc_dvc->sdtr_done & target_id) != 0)) {
+
+ sdtr_data = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASCV_SDTR_DATA_BEG + (ushort) tid_no));
+ AscMsgOutSDTR(iop_base,
+ _sdtr_period_tbl_[(sdtr_data >> 4) & (uchar) (ASC_SYN_XFER_NO - 1)],
+ (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET));
+ q_cntl |= QC_MSG_OUT;
+ }
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+
+ tag_code = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE));
+ tag_code &= 0xDC;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE),
+ tag_code);
+
+ q_status = AscReadLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_STATUS));
+ q_status |= (QS_READY | QS_BUSY);
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ q_status);
+
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ AscWriteLramByte(iop_base, (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_SDTR_REJECTED) {
+
+ AscMemWordCopyFromLram(iop_base,
+ ASCV_MSGOUT_BEG,
+ (ushort dosfar *) & out_msg,
+ (ushort) (sizeof (SDTR_XMSG) >> 1));
+
+ if ((out_msg.msg_type == MS_EXTEND) &&
+ (out_msg.msg_len == MS_SDTR_LEN) &&
+ (out_msg.msg_req == MS_SDTR_CODE)) {
+
+ asc_dvc->init_sdtr &= ~target_id;
+ asc_dvc->sdtr_done &= ~target_id;
+ AscSetChipSDTR(iop_base, asyn_sdtr, tid_no);
+
+ } else {
+
+ }
+
+ q_cntl &= ~QC_MSG_OUT;
+ AscWriteLramByte(iop_base,
+ (ushort) (halt_q_addr + (ushort) ASC_SCSIQ_B_CNTL),
+ q_cntl);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ } else if (int_halt_code == ASC_HALT_SS_QUEUE_FULL) {
+
+ scsi_status = AscReadLramByte(iop_base,
+ (ushort) ((ushort) halt_q_addr + (ushort) ASC_SCSIQ_SCSI_STATUS));
+ cur_dvc_qng = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASC_QADR_BEG + (ushort) target_ix));
+ if ((cur_dvc_qng > 0) &&
+ (asc_dvc->cur_dvc_qng[tid_no] > 0)) {
+
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy |= target_id;
+ AscWriteLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy |= target_id;
+
+ if (scsi_status == SS_QUEUE_FULL) {
+ if (cur_dvc_qng > ASC_MIN_TAGGED_CMD) {
+ cur_dvc_qng -= 1;
+ asc_dvc->max_dvc_qng[tid_no] = cur_dvc_qng;
+
+ AscWriteLramByte(iop_base,
+ (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG + (ushort) tid_no),
+ cur_dvc_qng);
+ }
+ }
+ }
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ return (0);
+ }
+ return (0);
+}
+
+uchar
+_AscCopyLramScsiDoneQ(
+ PortAddr iop_base,
+ ushort q_addr,
+ ASC_QDONE_INFO dosfar * scsiq,
+ ulong max_dma_count
+)
+{
+ ushort _val;
+ uchar sg_queue_cnt;
+
+ DvcGetQinfo(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_DONE_INFO_BEG),
+ (ushort dosfar *) scsiq,
+ (ushort) ((sizeof (ASC_SCSIQ_2) + sizeof (ASC_SCSIQ_3)) / 2));
+
+#if !CC_LITTLE_ENDIAN_HOST
+ AscAdjEndianQDoneInfo(scsiq);
+#endif
+
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS));
+ scsiq->q_status = (uchar) _val;
+ scsiq->q_no = (uchar) (_val >> 8);
+
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_CNTL));
+ scsiq->cntl = (uchar) _val;
+ sg_queue_cnt = (uchar) (_val >> 8);
+
+ _val = AscReadLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_SENSE_LEN));
+ scsiq->sense_len = (uchar) _val;
+ scsiq->user_def = (uchar) (_val >> 8);
+
+ scsiq->remain_bytes = AscReadLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_DW_REMAIN_XFER_CNT));
+ scsiq->remain_bytes &= max_dma_count;
+
+ return (sg_queue_cnt);
+}
+
+int
+AscIsrQDone(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ uchar next_qp;
+ uchar i;
+ uchar n_q_used;
+ uchar sg_list_qp;
+ uchar sg_queue_cnt;
+ uchar done_q_tail;
+
+ uchar tid_no;
+ ASC_SCSI_BIT_ID_TYPE scsi_busy;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ PortAddr iop_base;
+ ushort q_addr;
+ ushort sg_q_addr;
+ uchar cur_target_qng;
+ ASC_QDONE_INFO scsiq_buf;
+ ASC_QDONE_INFO dosfar *scsiq;
+ int false_overrun;
+ ASC_ISR_CALLBACK asc_isr_callback;
+
+ uchar tag_code;
+
+#if CC_LINK_BUSY_Q
+ ushort n_busy_q_done;
+
+#endif
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+
+ n_q_used = 1;
+ scsiq = (ASC_QDONE_INFO dosfar *) & scsiq_buf;
+ done_q_tail = (uchar) AscGetVarDoneQTail(iop_base);
+ q_addr = ASC_QNO_TO_QADDR(done_q_tail);
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_FWD));
+ if (next_qp != ASC_QLINK_END) {
+
+ AscPutVarDoneQTail(iop_base, next_qp);
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+
+ sg_queue_cnt = _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq, asc_dvc->max_dma_count);
+
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ (uchar) (scsiq->q_status & (uchar) ~ (QS_READY | QS_ABORTED)));
+ tid_no = ASC_TIX_TO_TID(scsiq->d2.target_ix);
+ target_id = ASC_TIX_TO_TARGET_ID(scsiq->d2.target_ix);
+ if ((scsiq->cntl & QC_SG_HEAD) != 0) {
+ sg_q_addr = q_addr;
+ sg_list_qp = next_qp;
+ for (i = 0; i < sg_queue_cnt; i++) {
+ sg_list_qp = AscReadLramByte(iop_base,
+ (ushort) (sg_q_addr + (ushort) ASC_SCSIQ_B_FWD));
+ sg_q_addr = ASC_QNO_TO_QADDR(sg_list_qp);
+ if (sg_list_qp == ASC_QLINK_END) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SG_Q_LINKS);
+ scsiq->d3.done_stat = QD_WITH_ERROR;
+ scsiq->d3.host_stat = QHSTA_D_QDONE_SG_LIST_CORRUPTED;
+ goto FATAL_ERR_QDONE;
+ }
+ AscWriteLramByte(iop_base,
+ (ushort) (sg_q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ QS_FREE);
+ }
+
+ n_q_used = sg_queue_cnt + 1;
+ AscPutVarDoneQTail(iop_base, sg_list_qp);
+ }
+ if (asc_dvc->queue_full_or_busy & target_id) {
+
+ cur_target_qng = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASC_QADR_BEG + (ushort) scsiq->d2.target_ix));
+ if (cur_target_qng < asc_dvc->max_dvc_qng[tid_no]) {
+ scsi_busy = AscReadLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B);
+ scsi_busy &= ~target_id;
+ AscWriteLramByte(iop_base,
+ (ushort) ASCV_SCSIBUSY_B, scsi_busy);
+ asc_dvc->queue_full_or_busy &= ~target_id;
+ }
+ }
+ if (asc_dvc->cur_total_qng >= n_q_used) {
+ asc_dvc->cur_total_qng -= n_q_used;
+ if (asc_dvc->cur_dvc_qng[tid_no] != 0) {
+ asc_dvc->cur_dvc_qng[tid_no]--;
+ }
+ } else {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CUR_QNG);
+ scsiq->d3.done_stat = QD_WITH_ERROR;
+ goto FATAL_ERR_QDONE;
+ }
+
+ if ((scsiq->d2.srb_ptr == 0UL) ||
+ ((scsiq->q_status & QS_ABORTED) != 0)) {
+
+ return (0x11);
+ } else if (scsiq->q_status == QS_DONE) {
+
+ false_overrun = FALSE;
+
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ADD_ONE_BYTE) {
+ tag_code = AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_TAG_CODE));
+ if (tag_code & ASC_TAG_FLAG_ADD_ONE_BYTE) {
+ if (scsiq->remain_bytes != 0UL) {
+ scsiq->remain_bytes--;
+ if (scsiq->remain_bytes == 0UL) {
+ false_overrun = TRUE;
+ }
+ }
+ }
+ }
+ }
+ if ((scsiq->d3.done_stat == QD_WITH_ERROR) &&
+ (scsiq->d3.host_stat == QHSTA_M_DATA_OVER_RUN)) {
+ if ((scsiq->cntl & (QC_DATA_IN | QC_DATA_OUT)) == 0) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ } else if (false_overrun) {
+ scsiq->d3.done_stat = QD_NO_ERROR;
+ scsiq->d3.host_stat = QHSTA_NO_ERROR;
+ }
+ }
+#if CC_CLEAR_LRAM_SRB_PTR
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR),
+ asc_dvc->int_count);
+#endif
+
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ } else {
+ if ((AscReadLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CDB_BEG)) ==
+ SCSICMD_StartStopUnit)) {
+
+ asc_dvc->unit_not_ready &= ~target_id;
+ if (scsiq->d3.done_stat != QD_NO_ERROR) {
+ asc_dvc->start_motor &= ~target_id;
+ }
+ }
+ }
+
+#if CC_LINK_BUSY_Q
+ n_busy_q_done = AscIsrExeBusyQueue(asc_dvc, tid_no);
+ if (n_busy_q_done == 0) {
+
+ i = tid_no + 1;
+ while (TRUE) {
+ if (i > ASC_MAX_TID)
+ i = 0;
+ if (i == tid_no)
+ break;
+ n_busy_q_done = AscIsrExeBusyQueue(asc_dvc, i);
+ if (n_busy_q_done != 0)
+ break;
+ i++;
+ }
+ }
+ if (n_busy_q_done == 0xFFFF)
+ return (0x80);
+#endif
+
+ return (1);
+ } else {
+
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_Q_STATUS);
+
+ FATAL_ERR_QDONE:
+ if ((scsiq->cntl & QC_NO_CALLBACK) == 0) {
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ }
+ return (0x80);
+ }
+ }
+ return (0);
+}
+
+int
+AscISR(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ ASC_CS_TYPE chipstat;
+ PortAddr iop_base;
+ ushort saved_ram_addr;
+ uchar ctrl_reg;
+ uchar saved_ctrl_reg;
+ int int_pending;
+ int status;
+ uchar host_flag;
+
+ iop_base = asc_dvc->iop_base;
+ int_pending = FALSE;
+
+ asc_dvc->int_count++;
+
+ if (((asc_dvc->init_state & ASC_INIT_STATE_END_LOAD_MC) == 0) ||
+ (asc_dvc->isr_callback == 0)) {
+
+ return (ERR);
+ }
+ if (asc_dvc->in_critical_cnt != 0) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_ON_CRITICAL);
+ return (ERR);
+ }
+ if (asc_dvc->is_in_int) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_ISR_RE_ENTRY);
+ asc_dvc->busy_count++;
+ return (ERR);
+ }
+ asc_dvc->is_in_int = TRUE;
+ ctrl_reg = AscGetChipControl(iop_base);
+ saved_ctrl_reg = ctrl_reg & (~(CC_SCSI_RESET | CC_CHIP_RESET |
+ CC_SINGLE_STEP | CC_DIAG | CC_TEST));
+
+ if ((chipstat = AscGetChipStatus(iop_base)) & CSW_INT_PENDING) {
+ int_pending = TRUE;
+ AscAckInterrupt(iop_base);
+
+ host_flag = AscReadLramByte(iop_base, ASCV_HOST_FLAG_B);
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B,
+ (uchar) (host_flag | (uchar) ASC_HOST_FLAG_IN_ISR));
+ saved_ram_addr = AscGetChipLramAddr(iop_base);
+
+ if ((chipstat & CSW_HALTED) &&
+ (ctrl_reg & CC_SINGLE_STEP)) {
+ if (AscIsrChipHalted(asc_dvc) == ERR) {
+
+ goto ISR_REPORT_QDONE_FATAL_ERROR;
+
+ } else {
+ saved_ctrl_reg &= ~CC_HALT;
+ }
+ } else {
+ ISR_REPORT_QDONE_FATAL_ERROR:
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_INT_MULTI_Q) != 0) {
+ while (((status = AscIsrQDone(asc_dvc)) & 0x01) != 0) {
+
+ }
+ } else {
+ do {
+ if ((status = AscIsrQDone(asc_dvc)) == 1) {
+
+ break;
+ }
+ } while (status == 0x11);
+ }
+ if ((status & 0x80) != 0)
+ int_pending = ERR;
+ }
+ AscSetChipLramAddr(iop_base, saved_ram_addr);
+ if (AscGetChipLramAddr(iop_base) != saved_ram_addr) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SET_LRAM_ADDR);
+ }
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
+ }
+ AscSetChipControl(iop_base, saved_ctrl_reg);
+ asc_dvc->is_in_int = FALSE;
+ return (int_pending);
+}
+
+int
+AscScsiSetupCmdQ(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ uchar dosfar * buf_addr,
+ ulong buf_len
+)
+{
+ ulong phy_addr;
+
+ scsiq->r1.cntl = 0;
+ scsiq->r1.sg_queue_cnt = 0;
+ scsiq->r1.q_no = 0;
+ scsiq->r1.user_def = 0;
+ scsiq->cdbptr = (uchar dosfar *) scsiq->cdb;
+ scsiq->r3.scsi_stat = 0;
+ scsiq->r3.scsi_msg = 0;
+ scsiq->r3.host_stat = 0;
+ scsiq->r3.done_stat = 0;
+ scsiq->r2.vm_id = 0;
+ scsiq->r1.data_cnt = buf_len;
+
+ scsiq->r2.tag_code = (uchar) M2_QTAG_MSG_SIMPLE;
+ scsiq->r2.flag = (uchar) ASC_FLAG_SCSIQ_REQ;
+ scsiq->r2.srb_ptr = (ulong) scsiq;
+ scsiq->r1.status = (uchar) QS_READY;
+ scsiq->r1.data_addr = 0L;
+
+ if (buf_len != 0L) {
+ if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
+ (uchar dosfar *) buf_addr, scsiq->r1.data_cnt)) == 0L) {
+ return (ERR);
+ }
+ scsiq->r1.data_addr = phy_addr;
+ }
+ return (0);
+}
+
+uchar _mcode_buf[] =
+{
+ 0x01, 0x03, 0x01, 0x19, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x0F, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xDD, 0x0A, 0x01, 0x05, 0x01, 0x00, 0x00, 0x00, 0x00, 0xFF, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0xFF, 0x80, 0xFF, 0xFF, 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x23, 0x00, 0x16, 0x00, 0x00, 0x00, 0x07, 0x00, 0xFF, 0x00, 0x00, 0x00, 0x00,
+ 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xA4, 0x88, 0x00, 0x00, 0x00, 0x00,
+ 0x80, 0x73, 0x48, 0x04, 0x36, 0x00, 0x00, 0xA2, 0xC8, 0x00, 0x80, 0x73, 0x03, 0x23, 0x36, 0x40,
+ 0xB6, 0x00, 0x36, 0x00, 0x06, 0xD6, 0x0D, 0xD2, 0x15, 0xDE, 0x12, 0xDA, 0x00, 0xA2, 0xC8, 0x00,
+ 0x92, 0x80, 0xE0, 0x97, 0x50, 0x00, 0xF5, 0x00, 0x0A, 0x98, 0xDF, 0x23, 0x36, 0x60, 0xB6, 0x00,
+ 0x92, 0x80, 0x4F, 0x00, 0xF5, 0x00, 0x0A, 0x98, 0xEF, 0x23, 0x36, 0x60, 0xB6, 0x00, 0x92, 0x80,
+ 0x80, 0x62, 0x92, 0x80, 0x00, 0x62, 0x92, 0x80, 0x00, 0x46, 0x17, 0xEE, 0x13, 0xEA, 0x02, 0x01,
+ 0x09, 0xD8, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xDC, 0x00, 0x68, 0x97, 0x7F, 0x23, 0x04, 0x61,
+ 0x84, 0x01, 0xB2, 0x84, 0xCF, 0xC1, 0x80, 0x73, 0xCD, 0x04, 0x4D, 0x00, 0x00, 0xA3, 0xE8, 0x01,
+ 0x68, 0x97, 0xD4, 0x81, 0x00, 0x33, 0x02, 0x00, 0x82, 0x88, 0x80, 0x73, 0x80, 0x77, 0x00, 0x01,
+ 0x01, 0xA1, 0x08, 0x01, 0x4F, 0x00, 0x46, 0x97, 0x07, 0xA6, 0x12, 0x01, 0x00, 0x33, 0x03, 0x00,
+ 0x82, 0x88, 0x03, 0x03, 0x03, 0xDE, 0x00, 0x33, 0x05, 0x00, 0x82, 0x88, 0xCE, 0x00, 0x69, 0x60,
+ 0xCE, 0x00, 0x02, 0x03, 0x4A, 0x60, 0x00, 0xA2, 0x86, 0x01, 0x80, 0x63, 0x07, 0xA6, 0x32, 0x01,
+ 0x86, 0x81, 0x03, 0x03, 0x80, 0x63, 0xE2, 0x00, 0x07, 0xA6, 0x42, 0x01, 0x00, 0x33, 0x04, 0x00,
+ 0x82, 0x88, 0x03, 0x07, 0x02, 0x01, 0x04, 0xCA, 0x0D, 0x23, 0x2A, 0x98, 0x4D, 0x04, 0xD0, 0x84,
+ 0x05, 0xD8, 0x0D, 0x23, 0x2A, 0x98, 0xCD, 0x04, 0x15, 0x23, 0xB8, 0x88, 0xFB, 0x23, 0x02, 0x61,
+ 0x82, 0x01, 0x80, 0x63, 0x02, 0x03, 0x06, 0xA3, 0x70, 0x01, 0x00, 0x33, 0x0A, 0x00, 0x82, 0x88,
+ 0x4E, 0x00, 0x07, 0xA3, 0x7C, 0x01, 0x00, 0x33, 0x0B, 0x00, 0x82, 0x88, 0xCD, 0x04, 0x36, 0x2D,
+ 0x00, 0x33, 0x1A, 0x00, 0x82, 0x88, 0x50, 0x04, 0x96, 0x81, 0x06, 0xAB, 0x90, 0x01, 0x96, 0x81,
+ 0x4E, 0x00, 0x07, 0xA3, 0xA0, 0x01, 0x50, 0x00, 0x00, 0xA3, 0x4A, 0x01, 0x00, 0x05, 0x8A, 0x81,
+ 0x08, 0x97, 0x02, 0x01, 0x05, 0xC6, 0x04, 0x23, 0xA0, 0x01, 0x15, 0x23, 0xA1, 0x01, 0xCC, 0x81,
+ 0xFD, 0x23, 0x02, 0x61, 0x82, 0x01, 0x0A, 0xDA, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA0, 0xC2, 0x01,
+ 0x80, 0x63, 0xCD, 0x04, 0x36, 0x2D, 0x00, 0x33, 0x1B, 0x00, 0x82, 0x88, 0x06, 0x23, 0x2A, 0x98,
+ 0xCD, 0x04, 0xB2, 0x84, 0x06, 0x01, 0x00, 0xA2, 0xE2, 0x01, 0x57, 0x60, 0x00, 0xA0, 0xE8, 0x01,
+ 0xB2, 0x84, 0x80, 0x23, 0xA0, 0x01, 0xB2, 0x84, 0x80, 0x73, 0x4B, 0x00, 0x06, 0x61, 0x00, 0xA2,
+ 0x10, 0x02, 0x04, 0x01, 0x0D, 0xDE, 0x02, 0x01, 0x03, 0xCC, 0x4F, 0x00, 0x46, 0x97, 0x0A, 0x82,
+ 0x08, 0x23, 0x02, 0x41, 0x82, 0x01, 0x4F, 0x00, 0x24, 0x97, 0x48, 0x04, 0xFF, 0x23, 0x84, 0x80,
+ 0xB2, 0x97, 0x00, 0x46, 0x56, 0x00, 0x03, 0xC0, 0x01, 0x23, 0xE8, 0x00, 0x81, 0x73, 0x06, 0x29,
+ 0x03, 0x42, 0x06, 0xE2, 0x03, 0xEE, 0x66, 0xEB, 0x11, 0x23, 0xB8, 0x88, 0xC6, 0x97, 0xFA, 0x80,
+ 0x80, 0x73, 0x80, 0x77, 0x06, 0xA6, 0x3E, 0x02, 0x00, 0x33, 0x31, 0x00, 0x82, 0x88, 0x04, 0x01,
+ 0x03, 0xD8, 0x74, 0x98, 0x02, 0x96, 0x50, 0x82, 0xA2, 0x95, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63,
+ 0xB6, 0x2D, 0x02, 0xA6, 0x7A, 0x02, 0x07, 0xA6, 0x68, 0x02, 0x06, 0xA6, 0x6C, 0x02, 0x03, 0xA6,
+ 0x70, 0x02, 0x00, 0x33, 0x10, 0x00, 0x82, 0x88, 0x4A, 0x95, 0x52, 0x82, 0xF8, 0x95, 0x52, 0x82,
+ 0x04, 0x23, 0xA0, 0x01, 0x14, 0x23, 0xA1, 0x01, 0x16, 0x84, 0x04, 0x01, 0x0C, 0xDC, 0xE0, 0x23,
+ 0x25, 0x61, 0xEF, 0x00, 0x14, 0x01, 0x4F, 0x04, 0xA8, 0x01, 0x6F, 0x00, 0xA5, 0x01, 0x03, 0x23,
+ 0xA4, 0x01, 0x06, 0x23, 0x9C, 0x01, 0x24, 0x2B, 0x1C, 0x01, 0x02, 0xA6, 0xAC, 0x02, 0x07, 0xA6,
+ 0x68, 0x02, 0x06, 0xA6, 0x6C, 0x02, 0x00, 0x33, 0x12, 0x00, 0x82, 0x88, 0x00, 0x0E, 0x80, 0x63,
+ 0x00, 0x43, 0x00, 0xA0, 0x9A, 0x02, 0x4D, 0x04, 0x04, 0x01, 0x0B, 0xDC, 0xE7, 0x23, 0x04, 0x61,
+ 0x84, 0x01, 0x10, 0x31, 0x12, 0x35, 0x14, 0x01, 0xEC, 0x00, 0x6C, 0x38, 0x00, 0x3F, 0x00, 0x00,
+ 0xEC, 0x82, 0x18, 0x23, 0x04, 0x61, 0x18, 0xA0, 0xE4, 0x02, 0x04, 0x01, 0x8E, 0xC8, 0x00, 0x33,
+ 0x1F, 0x00, 0x82, 0x88, 0x08, 0x31, 0x0A, 0x35, 0x0C, 0x39, 0x0E, 0x3D, 0x40, 0x98, 0xB6, 0x2D,
+ 0x01, 0xA6, 0x0E, 0x03, 0x00, 0xA6, 0x1C, 0x03, 0x07, 0xA6, 0x2A, 0x03, 0x06, 0xA6, 0x2E, 0x03,
+ 0x03, 0xA6, 0xFA, 0x03, 0x02, 0xA6, 0x7A, 0x02, 0x00, 0x33, 0x33, 0x00, 0x82, 0x88, 0x08, 0x23,
+ 0xB3, 0x01, 0x04, 0x01, 0x0E, 0xD0, 0x00, 0x33, 0x14, 0x00, 0x82, 0x88, 0x10, 0x23, 0xB3, 0x01,
+ 0x04, 0x01, 0x07, 0xCC, 0x00, 0x33, 0x15, 0x00, 0x82, 0x88, 0x4A, 0x95, 0xF0, 0x82, 0xF8, 0x95,
+ 0xF0, 0x82, 0x44, 0x98, 0x80, 0x42, 0x40, 0x98, 0x48, 0xE4, 0x04, 0x01, 0x29, 0xC8, 0x31, 0x05,
+ 0x07, 0x01, 0x00, 0xA2, 0x72, 0x03, 0x00, 0x43, 0x87, 0x01, 0x05, 0x05, 0x48, 0x98, 0x40, 0x98,
+ 0x00, 0xA6, 0x34, 0x03, 0x07, 0xA6, 0x6A, 0x03, 0x03, 0xA6, 0x16, 0x04, 0x06, 0xA6, 0x6E, 0x03,
+ 0x01, 0xA6, 0x34, 0x03, 0x00, 0x33, 0x25, 0x00, 0x82, 0x88, 0x4A, 0x95, 0x50, 0x83, 0xF8, 0x95,
+ 0x50, 0x83, 0x04, 0x01, 0x0C, 0xCE, 0x03, 0xC8, 0x00, 0x33, 0x42, 0x00, 0x82, 0x88, 0x00, 0x01,
+ 0x05, 0x05, 0xFF, 0xA2, 0x90, 0x03, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x4C, 0x83, 0x05, 0x05,
+ 0x01, 0xA6, 0x9A, 0x03, 0x00, 0xA6, 0xAA, 0x03, 0xF0, 0x83, 0x68, 0x98, 0x80, 0x42, 0x01, 0xA6,
+ 0x9A, 0x03, 0xBA, 0x83, 0x00, 0x33, 0x2F, 0x00, 0x82, 0x88, 0x68, 0x98, 0x80, 0x42, 0x00, 0xA6,
+ 0xAA, 0x03, 0xBA, 0x83, 0x00, 0x33, 0x26, 0x00, 0x82, 0x88, 0x38, 0x2B, 0x80, 0x32, 0x80, 0x36,
+ 0x04, 0x23, 0xA0, 0x01, 0x12, 0x23, 0xA1, 0x01, 0xF0, 0x83, 0x04, 0xF0, 0x80, 0x6B, 0x00, 0x33,
+ 0x20, 0x00, 0x82, 0x88, 0x03, 0xA6, 0xEE, 0x03, 0x07, 0xA6, 0xE6, 0x03, 0x06, 0xA6, 0xEA, 0x03,
+ 0x00, 0x33, 0x17, 0x00, 0x82, 0x88, 0x4A, 0x95, 0xD4, 0x83, 0xF8, 0x95, 0xD4, 0x83, 0xFA, 0x83,
+ 0x04, 0xF0, 0x80, 0x6B, 0x00, 0x33, 0x20, 0x00, 0x82, 0x88, 0xB6, 0x2D, 0x03, 0xA6, 0x16, 0x04,
+ 0x07, 0xA6, 0x0E, 0x04, 0x06, 0xA6, 0x12, 0x04, 0x00, 0x33, 0x30, 0x00, 0x82, 0x88, 0x4A, 0x95,
+ 0xFA, 0x83, 0xF8, 0x95, 0xFA, 0x83, 0xA2, 0x0D, 0x80, 0x63, 0x07, 0xA6, 0x24, 0x04, 0x00, 0x33,
+ 0x18, 0x00, 0x82, 0x88, 0x03, 0x03, 0x80, 0x63, 0xA3, 0x01, 0x07, 0xA4, 0x2E, 0x04, 0x23, 0x01,
+ 0x00, 0xA2, 0x50, 0x04, 0x0A, 0xA0, 0x40, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1D, 0x00, 0x82, 0x88,
+ 0x0B, 0xA0, 0x4C, 0x04, 0xE0, 0x00, 0x00, 0x33, 0x1E, 0x00, 0x82, 0x88, 0x42, 0x23, 0xB8, 0x88,
+ 0x00, 0x23, 0x22, 0xA3, 0xB2, 0x04, 0x08, 0x23, 0x22, 0xA3, 0x6C, 0x04, 0x28, 0x23, 0x22, 0xA3,
+ 0x78, 0x04, 0x02, 0x23, 0x22, 0xA3, 0x8E, 0x04, 0x42, 0x23, 0xB8, 0x88, 0x4A, 0x00, 0x06, 0x61,
+ 0x00, 0xA0, 0x78, 0x04, 0x45, 0x23, 0xB8, 0x88, 0xC6, 0x97, 0x00, 0xA2, 0x8A, 0x04, 0x74, 0x98,
+ 0x00, 0x33, 0x00, 0x82, 0xC0, 0x20, 0x81, 0x62, 0xF6, 0x81, 0x47, 0x23, 0xB8, 0x88, 0x04, 0x01,
+ 0x0C, 0xDE, 0x14, 0x01, 0x00, 0xA2, 0xA6, 0x04, 0xC6, 0x97, 0x74, 0x98, 0x00, 0x33, 0x00, 0x81,
+ 0xC0, 0x20, 0x81, 0x62, 0x10, 0x82, 0x43, 0x23, 0xB8, 0x88, 0x04, 0x23, 0xA0, 0x01, 0x44, 0x23,
+ 0xA1, 0x01, 0x80, 0x73, 0x4D, 0x00, 0x03, 0xA3, 0xC0, 0x04, 0x00, 0x33, 0x27, 0x00, 0x82, 0x88,
+ 0x04, 0x01, 0x04, 0xDC, 0x02, 0x23, 0xA2, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xC6, 0x97, 0xF4, 0x94,
+ 0x4B, 0x00, 0xF6, 0x00, 0x4F, 0x04, 0x4F, 0x00, 0x00, 0xA3, 0xEE, 0x04, 0x00, 0x05, 0x76, 0x00,
+ 0x06, 0x61, 0x00, 0xA2, 0xE8, 0x04, 0xD6, 0x84, 0x08, 0x97, 0xCD, 0x04, 0xF2, 0x84, 0x48, 0x04,
+ 0xFF, 0x23, 0x84, 0x80, 0x02, 0x01, 0x03, 0xDA, 0x80, 0x23, 0x82, 0x01, 0x02, 0x85, 0x02, 0x23,
+ 0xA0, 0x01, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x0E, 0x05, 0x1D, 0x01, 0x04, 0xD6, 0xFF, 0x23,
+ 0x86, 0x41, 0x4B, 0x60, 0xCB, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x49, 0x00, 0x81, 0x01, 0x04, 0x01,
+ 0x02, 0xC8, 0x30, 0x01, 0x80, 0x01, 0xF7, 0x04, 0x03, 0x01, 0x49, 0x04, 0x80, 0x01, 0xC9, 0x00,
+ 0x00, 0x05, 0x00, 0x01, 0xFF, 0xA0, 0x2E, 0x05, 0x77, 0x04, 0x01, 0x23, 0xEA, 0x00, 0x5D, 0x00,
+ 0xFE, 0xC7, 0x00, 0x62, 0x00, 0x23, 0xEA, 0x00, 0x00, 0x63, 0x07, 0xA4, 0xA0, 0x05, 0x03, 0x03,
+ 0x02, 0xA0, 0x5C, 0x05, 0x9C, 0x85, 0x00, 0x33, 0x2D, 0x00, 0x82, 0x88, 0x04, 0xA0, 0x82, 0x05,
+ 0x80, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0x6E, 0x05, 0x1D, 0x01, 0x06, 0xD6, 0x02, 0x23,
+ 0x02, 0x41, 0x82, 0x01, 0x50, 0x00, 0x24, 0x97, 0xD0, 0x84, 0x04, 0x23, 0x02, 0x41, 0x82, 0x01,
+ 0xD0, 0x84, 0x08, 0xA0, 0x88, 0x05, 0x9C, 0x85, 0x03, 0xA0, 0x8E, 0x05, 0x9C, 0x85, 0x01, 0xA0,
+ 0x9A, 0x05, 0x88, 0x00, 0x80, 0x63, 0x78, 0x96, 0x4A, 0x85, 0x88, 0x86, 0x80, 0x63, 0x4A, 0x85,
+ 0x00, 0x63, 0x4A, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xDE, 0x05, 0x1D, 0x01, 0x18, 0xD4, 0xC0, 0x23,
+ 0x07, 0x41, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6, 0xC0, 0x05, 0x00, 0x33, 0x37, 0x00, 0x82, 0x88,
+ 0x1D, 0x01, 0x02, 0xD6, 0x46, 0x23, 0xB8, 0x88, 0x63, 0x60, 0x83, 0x03, 0x80, 0x63, 0x06, 0xA6,
+ 0xD8, 0x05, 0x00, 0x33, 0x38, 0x00, 0x82, 0x88, 0xEF, 0x04, 0x6F, 0x00, 0x00, 0x63, 0x4B, 0x00,
+ 0x06, 0x41, 0xCB, 0x00, 0x52, 0x00, 0x06, 0x61, 0x00, 0xA2, 0xF2, 0x05, 0xC0, 0x23, 0x07, 0x41,
+ 0x00, 0x63, 0x80, 0x23, 0x07, 0x41, 0x00, 0x63, 0x80, 0x67, 0x08, 0x23, 0x83, 0x03, 0x80, 0x63,
+ 0x00, 0x63, 0x06, 0xA6, 0x14, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x02, 0xA6, 0xBC, 0x06, 0x00, 0x33,
+ 0x39, 0x00, 0x82, 0x88, 0x00, 0x00, 0x01, 0xA0, 0xD6, 0x06, 0xA2, 0x95, 0x83, 0x03, 0x80, 0x63,
+ 0x06, 0xA6, 0x28, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x00, 0x00, 0x00, 0x2B, 0x40, 0x0E, 0x80, 0x63,
+ 0x01, 0x00, 0x06, 0xA6, 0x40, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x00, 0x33, 0x3A, 0x00, 0x82, 0x88,
+ 0x40, 0x0E, 0x80, 0x63, 0x00, 0x43, 0x00, 0xA0, 0x32, 0x06, 0x06, 0xA6, 0x58, 0x06, 0x07, 0xA6,
+ 0x64, 0x06, 0x00, 0x33, 0x3B, 0x00, 0x82, 0x88, 0x80, 0x67, 0x40, 0x0E, 0x80, 0x63, 0x07, 0xA6,
+ 0x64, 0x06, 0x00, 0x63, 0x03, 0x03, 0x80, 0x63, 0x88, 0x00, 0x01, 0xA2, 0x78, 0x06, 0x07, 0xA2,
+ 0xBC, 0x06, 0x00, 0x33, 0x35, 0x00, 0x82, 0x88, 0x07, 0xA6, 0x82, 0x06, 0x00, 0x33, 0x2A, 0x00,
+ 0x82, 0x88, 0x03, 0x03, 0x03, 0xA2, 0x8E, 0x06, 0x07, 0x23, 0x80, 0x00, 0xC8, 0x86, 0x80, 0x63,
+ 0x89, 0x00, 0x0A, 0x2B, 0x07, 0xA6, 0x9E, 0x06, 0x00, 0x33, 0x29, 0x00, 0x82, 0x88, 0x00, 0x43,
+ 0x00, 0xA2, 0xAA, 0x06, 0xC0, 0x0E, 0x80, 0x63, 0x94, 0x86, 0xC0, 0x0E, 0x00, 0x33, 0x00, 0x80,
+ 0xC0, 0x20, 0x81, 0x62, 0x04, 0x01, 0x08, 0xDA, 0x80, 0x63, 0x00, 0x63, 0x80, 0x67, 0x00, 0x33,
+ 0x00, 0x40, 0xC0, 0x20, 0x81, 0x62, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6, 0x20, 0x06,
+ 0x00, 0x33, 0x2C, 0x00, 0x82, 0x88, 0x0C, 0xA2, 0xF0, 0x06, 0xA2, 0x95, 0x83, 0x03, 0x80, 0x63,
+ 0x06, 0xA6, 0xEE, 0x06, 0x07, 0xA6, 0x64, 0x06, 0x00, 0x33, 0x3D, 0x00, 0x82, 0x88, 0x00, 0x00,
+ 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x0C, 0xA0, 0x06, 0x07, 0x07, 0xA6, 0x64, 0x06, 0xBF, 0x23,
+ 0x04, 0x61, 0x84, 0x01, 0xB2, 0x84, 0x00, 0x63, 0xF0, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x00, 0x01,
+ 0xF2, 0x00, 0x01, 0x05, 0x80, 0x01, 0x72, 0x04, 0x71, 0x00, 0x81, 0x01, 0x70, 0x04, 0x80, 0x05,
+ 0x81, 0x05, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x01, 0x01, 0xF1, 0x00, 0x70, 0x00,
+ 0x81, 0x01, 0x70, 0x04, 0x71, 0x00, 0x81, 0x01, 0x72, 0x00, 0x80, 0x01, 0x71, 0x04, 0x70, 0x00,
+ 0x80, 0x01, 0x70, 0x04, 0x00, 0x63, 0xF0, 0x04, 0xF2, 0x00, 0x72, 0x04, 0x00, 0x01, 0xF1, 0x00,
+ 0x70, 0x00, 0x80, 0x01, 0x70, 0x04, 0x71, 0x00, 0x80, 0x01, 0x72, 0x00, 0x81, 0x01, 0x71, 0x04,
+ 0x70, 0x00, 0x81, 0x01, 0x70, 0x04, 0x00, 0x63, 0x00, 0x23, 0xB3, 0x01, 0x83, 0x05, 0xA3, 0x01,
+ 0xA2, 0x01, 0xA1, 0x01, 0x01, 0x23, 0xA0, 0x01, 0x00, 0x01, 0xC8, 0x00, 0x03, 0xA1, 0x86, 0x07,
+ 0x00, 0x33, 0x07, 0x00, 0x82, 0x88, 0x80, 0x05, 0x81, 0x05, 0x04, 0x01, 0x11, 0xC8, 0x48, 0x00,
+ 0xB0, 0x01, 0xB1, 0x01, 0x08, 0x23, 0xB2, 0x01, 0x05, 0x01, 0x48, 0x04, 0x00, 0x43, 0x00, 0xA2,
+ 0xA6, 0x07, 0x00, 0x05, 0x9C, 0x87, 0x00, 0x01, 0xC8, 0x00, 0xFF, 0x23, 0x80, 0x01, 0x05, 0x05,
+ 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02, 0x80, 0x43, 0x76, 0x08,
+ 0x80, 0x02, 0x77, 0x04, 0x00, 0x63, 0xF7, 0x04, 0x1A, 0x09, 0xF6, 0x08, 0x6E, 0x04, 0x00, 0x02,
+ 0x00, 0xA0, 0xD6, 0x07, 0xD8, 0x87, 0x00, 0x43, 0x76, 0x08, 0x80, 0x02, 0x77, 0x04, 0x00, 0x63,
+ 0xF3, 0x04, 0x00, 0x23, 0xF4, 0x00, 0x74, 0x00, 0x80, 0x43, 0xF4, 0x00, 0xCF, 0x40, 0x00, 0xA2,
+ 0x06, 0x08, 0x74, 0x04, 0x02, 0x01, 0xF7, 0xC9, 0xF6, 0xD9, 0x00, 0x01, 0x01, 0xA1, 0xE6, 0x07,
+ 0xC6, 0x97, 0xF4, 0x94, 0xE6, 0x87, 0x73, 0x04, 0x00, 0x63, 0xF3, 0x04, 0x75, 0x04, 0x1C, 0x88,
+ 0x02, 0x01, 0x04, 0xD8, 0x08, 0x97, 0xC6, 0x97, 0xF4, 0x94, 0x0C, 0x88, 0x75, 0x00, 0x00, 0xA3,
+ 0x26, 0x08, 0x00, 0x05, 0x10, 0x88, 0x73, 0x04, 0x00, 0x63, 0x80, 0x7B, 0x80, 0x63, 0x06, 0xA6,
+ 0x38, 0x08, 0x00, 0x33, 0x3E, 0x00, 0x82, 0x88, 0x80, 0x67, 0x83, 0x03, 0x80, 0x63, 0x00, 0x63,
+ 0x38, 0x2B, 0x5E, 0x88, 0x38, 0x2B, 0x54, 0x88, 0x32, 0x09, 0x31, 0x05, 0x54, 0x98, 0x05, 0x05,
+ 0xB2, 0x09, 0x00, 0x63, 0x00, 0x32, 0x00, 0x36, 0x00, 0x3A, 0x00, 0x3E, 0x00, 0x63, 0x80, 0x32,
+ 0x80, 0x36, 0x80, 0x3A, 0x80, 0x3E, 0x00, 0x63, 0x38, 0x2B, 0x40, 0x32, 0x40, 0x36, 0x40, 0x3A,
+ 0x40, 0x3E, 0x00, 0x63, 0x5A, 0x20, 0xC9, 0x40, 0x00, 0xA0, 0x74, 0x08, 0x5D, 0x00, 0xFE, 0xC3,
+ 0x00, 0x63, 0x80, 0x73, 0xE6, 0x20, 0x02, 0x23, 0xE8, 0x00, 0x82, 0x73, 0xFF, 0xFD, 0x80, 0x73,
+ 0x13, 0x23, 0xB8, 0x88, 0x66, 0x20, 0xC0, 0x20, 0x04, 0x23, 0xA0, 0x01, 0xA1, 0x23, 0xA1, 0x01,
+ 0x81, 0x62, 0xA2, 0x88, 0x80, 0x73, 0x80, 0x77, 0x68, 0x00, 0x00, 0xA2, 0x80, 0x00, 0x03, 0xC2,
+ 0xF1, 0xC7, 0x41, 0x23, 0xB8, 0x88, 0x11, 0x23, 0xA1, 0x01, 0x04, 0x23, 0xA0, 0x01, 0xB2, 0x84,
+
+};
+
+ushort _mcode_size = sizeof (_mcode_buf);
+ulong _mcode_chksum = 0x012258FBUL;
+
+extern uchar _sdtr_period_tbl_[];
+
+int
+AscExeScsiQueue(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_Q dosfar * scsiq
+)
+{
+ PortAddr iop_base;
+ int last_int_level;
+ int sta;
+ ulong addr;
+ uchar sg_entry_cnt;
+ uchar target_ix;
+ int n_q_required;
+ uchar sg_entry_cnt_minus_one;
+ uchar tid_no;
+ uchar sdtr_data;
+ ASC_EXE_CALLBACK asc_exe_callback;
+
+#if CC_DEBUG_SG_LIST
+ int i;
+
+#endif
+#if CC_LINK_BUSY_Q
+ ASC_SCSI_Q dosfar *scsiq_tail;
+ ASC_SCSI_Q dosfar *scsiq_next;
+ ASC_SCSI_Q dosfar *scsiq_prev;
+
+#endif
+
+ iop_base = asc_dvc->iop_base;
+ asc_exe_callback = (ASC_EXE_CALLBACK) asc_dvc->exe_callback;
+ if (asc_dvc->err_code != 0)
+ return (ERR);
+ if (scsiq == (ASC_SCSI_Q dosfar *) 0L) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SCSIQ_NULL_PTR);
+ return (ERR);
+ }
+ scsiq->q1.q_no = 0;
+ sta = 0;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+
+ n_q_required = 1;
+
+ if (scsiq->cdbptr[0] == SCSICMD_RequestSense) {
+
+ if (((asc_dvc->init_sdtr & scsiq->q1.target_id) != 0) &&
+ ((asc_dvc->sdtr_done & scsiq->q1.target_id) != 0)) {
+ sdtr_data = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASCV_SDTR_DATA_BEG + (ushort) tid_no));
+ AscMsgOutSDTR(iop_base,
+ _sdtr_period_tbl_[(sdtr_data >> 4) & (uchar) (ASC_SYN_XFER_NO - 1)],
+ (uchar) (sdtr_data & (uchar) ASC_SYN_MAX_OFFSET));
+ scsiq->q1.cntl |= (QC_MSG_OUT | QC_URGENT);
+ }
+ }
+ last_int_level = DvcEnterCritical();
+ if (asc_dvc->in_critical_cnt != 0) {
+ DvcLeaveCritical(last_int_level);
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_CRITICAL_RE_ENTRY);
+ return (ERR);
+ }
+ asc_dvc->in_critical_cnt++;
+
+ if ((scsiq->q1.cntl & QC_SG_HEAD) != 0) {
+
+ if ((sg_entry_cnt = scsiq->sg_head->entry_cnt) == 0) {
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return (ERR);
+ }
+ if (sg_entry_cnt == 1) {
+ scsiq->q1.data_addr = scsiq->sg_head->sg_list[0].addr;
+ scsiq->q1.data_cnt = scsiq->sg_head->sg_list[0].bytes;
+ scsiq->q1.cntl &= ~(QC_SG_HEAD | QC_SG_SWAP_QUEUE);
+ goto NON_SG_LIST_REQ;
+ }
+ if (sg_entry_cnt > ASC_MAX_SG_LIST) {
+
+ return (ERR);
+ }
+ sg_entry_cnt_minus_one = sg_entry_cnt - 1;
+
+#if CC_DEBUG_SG_LIST
+ if (asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL | ASC_IS_EISA)) {
+ for (i = 0; i < sg_entry_cnt_minus_one; i++) {
+
+ addr = scsiq->sg_head->sg_list[i].addr +
+ scsiq->sg_head->sg_list[i].bytes;
+
+ if (((ushort) addr & 0x0003) != 0) {
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SG_LIST_ODD_ADDRESS);
+ return (ERR);
+ }
+ }
+ }
+#endif
+
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ADD_ONE_BYTE) {
+
+ addr = scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].addr +
+ scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].bytes;
+ if (((ushort) addr & 0x0003) != 0) {
+ if ((scsiq->cdbptr[0] == SCSICMD_Read6) ||
+ (scsiq->cdbptr[0] == SCSICMD_Read10)) {
+ if ((scsiq->q2.tag_code & ASC_TAG_FLAG_ADD_ONE_BYTE) == 0) {
+
+ scsiq->sg_head->sg_list[sg_entry_cnt_minus_one].bytes++;
+ scsiq->q2.tag_code |= ASC_TAG_FLAG_ADD_ONE_BYTE;
+ }
+ }
+ }
+ }
+ }
+ scsiq->sg_head->entry_to_copy = scsiq->sg_head->entry_cnt;
+ n_q_required = AscSgListToQueue(sg_entry_cnt);
+
+#if CC_LINK_BUSY_Q
+ scsiq_next = (ASC_SCSI_Q dosfar *) asc_dvc->scsiq_busy_head[tid_no];
+ if (scsiq_next != (ASC_SCSI_Q dosfar *) 0L) {
+ goto link_scisq_to_busy_list;
+ }
+#endif
+
+ if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, n_q_required)
+ >= (uint) n_q_required) ||
+ ((scsiq->q1.cntl & QC_URGENT) != 0)) {
+ if ((sta = AscSendScsiQueue(asc_dvc, scsiq,
+ n_q_required)) == 1) {
+
+ asc_dvc->in_critical_cnt--;
+ if (asc_exe_callback != 0) {
+ (*asc_exe_callback) (asc_dvc, scsiq);
+ }
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+ }
+ }
+ } else {
+
+ NON_SG_LIST_REQ:
+
+ if (asc_dvc->bug_fix_cntl) {
+ if (asc_dvc->bug_fix_cntl & ASC_BUG_FIX_ADD_ONE_BYTE) {
+
+ addr = scsiq->q1.data_addr + scsiq->q1.data_cnt;
+ if ((scsiq->cdbptr[0] == SCSICMD_Read6) ||
+ (scsiq->cdbptr[0] == SCSICMD_Read10)) {
+ if (((ushort) addr & 0x0003) != 0) {
+ if (((ushort) scsiq->q1.data_cnt & 0x01FF) == 0) {
+
+ if ((scsiq->q2.tag_code & ASC_TAG_FLAG_ADD_ONE_BYTE) == 0) {
+
+ scsiq->q2.tag_code |= ASC_TAG_FLAG_ADD_ONE_BYTE;
+ scsiq->q1.data_cnt++;
+ }
+ }
+ }
+ }
+ }
+ }
+ n_q_required = 1;
+
+#if CC_LINK_BUSY_Q
+ scsiq_next = (ASC_SCSI_Q dosfar *) asc_dvc->scsiq_busy_head[tid_no];
+ if (scsiq_next != (ASC_SCSI_Q dosfar *) 0L) {
+ goto link_scisq_to_busy_list;
+ }
+#endif
+ if ((AscGetNumOfFreeQueue(asc_dvc, target_ix, 1) >= 1) ||
+ ((scsiq->q1.cntl & QC_URGENT) != 0)) {
+ if ((sta = AscSendScsiQueue(asc_dvc, scsiq,
+ n_q_required)) == 1) {
+
+ asc_dvc->in_critical_cnt--;
+ if (asc_exe_callback != 0) {
+ (*asc_exe_callback) (asc_dvc, scsiq);
+ }
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+ }
+ }
+ }
+
+#if CC_LINK_BUSY_Q
+ if (sta == 0) {
+
+ link_scisq_to_busy_list:
+ scsiq->ext.q_required = n_q_required;
+ if (scsiq_next == (ASC_SCSI_Q dosfar *) 0L) {
+ asc_dvc->scsiq_busy_head[tid_no] = (ASC_SCSI_Q dosfar *) scsiq;
+ asc_dvc->scsiq_busy_tail[tid_no] = (ASC_SCSI_Q dosfar *) scsiq;
+ scsiq->ext.next = (ASC_SCSI_Q dosfar *) 0L;
+ scsiq->ext.join = (ASC_SCSI_Q dosfar *) 0L;
+ scsiq->q1.status = QS_BUSY;
+ sta = 1;
+ } else {
+ scsiq_tail = (ASC_SCSI_Q dosfar *) asc_dvc->scsiq_busy_tail[tid_no];
+ if (scsiq_tail->ext.next == (ASC_SCSI_Q dosfar *) 0L) {
+ if ((scsiq->q1.cntl & QC_URGENT) != 0) {
+
+ asc_dvc->scsiq_busy_head[tid_no] = (ASC_SCSI_Q dosfar *) scsiq;
+ scsiq->ext.next = scsiq_next;
+ scsiq->ext.join = (ASC_SCSI_Q dosfar *) 0L;
+ } else {
+ if (scsiq->ext.cntl & QCX_SORT) {
+ do {
+ scsiq_prev = scsiq_next;
+ scsiq_next = scsiq_next->ext.next;
+ if (scsiq->ext.lba < scsiq_prev->ext.lba)
+ break;
+ } while (scsiq_next != (ASC_SCSI_Q dosfar *) 0L);
+
+ scsiq_prev->ext.next = scsiq;
+ scsiq->ext.next = scsiq_next;
+ if (scsiq_next == (ASC_SCSI_Q dosfar *) 0L) {
+ asc_dvc->scsiq_busy_tail[tid_no] = (ASC_SCSI_Q dosfar *) scsiq;
+ }
+ scsiq->ext.join = (ASC_SCSI_Q dosfar *) 0L;
+ } else {
+
+ scsiq_tail->ext.next = (ASC_SCSI_Q dosfar *) scsiq;
+ asc_dvc->scsiq_busy_tail[tid_no] = (ASC_SCSI_Q dosfar *) scsiq;
+ scsiq->ext.next = (ASC_SCSI_Q dosfar *) 0L;
+ scsiq->ext.join = (ASC_SCSI_Q dosfar *) 0L;
+ }
+ }
+ scsiq->q1.status = QS_BUSY;
+ sta = 1;
+ } else {
+
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_SCSIQ_BAD_NEXT_PTR);
+ sta = ERR;
+ }
+ }
+ }
+#endif
+ asc_dvc->in_critical_cnt--;
+ DvcLeaveCritical(last_int_level);
+ return (sta);
+}
+
+int
+AscSendScsiQueue(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_Q dosfar * scsiq,
+ uchar n_q_required
+)
+{
+ PortAddr iop_base;
+ uchar free_q_head;
+ uchar next_qp;
+ uchar tid_no;
+ uchar target_ix;
+ int sta;
+
+ iop_base = asc_dvc->iop_base;
+ target_ix = scsiq->q2.target_ix;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ sta = 0;
+ free_q_head = (uchar) AscGetVarFreeQHead(iop_base);
+ if (n_q_required > 1) {
+ if ((next_qp = AscAllocMultipleFreeQueue(iop_base,
+ free_q_head, (uchar) (n_q_required)))
+ != (uchar) ASC_QLINK_END) {
+ asc_dvc->last_q_shortage = 0;
+ scsiq->sg_head->queue_cnt = n_q_required - 1;
+ scsiq->q1.q_no = free_q_head;
+
+ if ((sta = AscPutReadySgListQueue(asc_dvc, scsiq,
+ free_q_head)) == 1) {
+
+#if CC_WRITE_IO_COUNT
+ asc_dvc->req_count++;
+#endif
+
+ AscPutVarFreeQHead(iop_base, next_qp);
+ asc_dvc->cur_total_qng += (uchar) (n_q_required);
+ asc_dvc->cur_dvc_qng[tid_no]++;
+ }
+ return (sta);
+ }
+ } else if (n_q_required == 1) {
+
+ if ((next_qp = AscAllocFreeQueue(iop_base,
+ free_q_head)) != ASC_QLINK_END) {
+
+ scsiq->q1.q_no = free_q_head;
+ if ((sta = AscPutReadyQueue(asc_dvc, scsiq,
+ free_q_head)) == 1) {
+
+#if CC_WRITE_IO_COUNT
+ asc_dvc->req_count++;
+#endif
+
+ AscPutVarFreeQHead(iop_base, next_qp);
+ asc_dvc->cur_total_qng++;
+ asc_dvc->cur_dvc_qng[tid_no]++;
+ }
+ return (sta);
+ }
+ }
+ return (sta);
+}
+
+int
+AscSgListToQueue(
+ int sg_list
+)
+{
+ int n_sg_list_qs;
+
+ n_sg_list_qs = ((sg_list - 1) / ASC_SG_LIST_PER_Q);
+ if (((sg_list - 1) % ASC_SG_LIST_PER_Q) != 0)
+ n_sg_list_qs++;
+ return (n_sg_list_qs + 1);
+}
+
+uint
+AscGetNumOfFreeQueue(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix, uchar n_qs
+)
+{
+ uint cur_used_qs;
+ uint cur_free_qs;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ uchar tid_no;
+
+ target_id = ASC_TIX_TO_TARGET_ID(target_ix);
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ if ((asc_dvc->unit_not_ready & target_id) ||
+ (asc_dvc->queue_full_or_busy & target_id)) {
+ return (0);
+ }
+ if (n_qs == 1) {
+ cur_used_qs = (uint) asc_dvc->cur_total_qng +
+ (uint) asc_dvc->last_q_shortage +
+ (uint) ASC_MIN_FREE_Q;
+ } else {
+ cur_used_qs = (uint) asc_dvc->cur_total_qng +
+ (uint) ASC_MIN_FREE_Q;
+ }
+
+ if ((uint) (cur_used_qs + n_qs) <= (uint) asc_dvc->max_total_qng) {
+ cur_free_qs = (uint) asc_dvc->max_total_qng - cur_used_qs;
+ if (asc_dvc->cur_dvc_qng[tid_no] >=
+ asc_dvc->max_dvc_qng[tid_no]) {
+ return (0);
+ }
+ return (cur_free_qs);
+ }
+ if (n_qs > 1) {
+ if (n_qs > asc_dvc->last_q_shortage) {
+ asc_dvc->last_q_shortage = n_qs;
+ }
+ }
+ return (0);
+}
+
+int
+AscPutReadyQueue(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_Q dosfar * scsiq,
+ uchar q_no
+)
+{
+ ushort q_addr;
+ uchar tid_no;
+ uchar sdtr_data;
+ uchar syn_period_ix;
+ uchar syn_offset;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+
+ if (((asc_dvc->init_sdtr & scsiq->q1.target_id) != 0) &&
+ ((asc_dvc->sdtr_done & scsiq->q1.target_id) == 0)) {
+
+ tid_no = ASC_TIX_TO_TID(scsiq->q2.target_ix);
+
+ sdtr_data = AscReadLramByte(iop_base,
+ (ushort) ((ushort) ASCV_SDTR_DATA_BEG + (ushort) tid_no));
+ syn_period_ix = (sdtr_data >> 4) & (ASC_SYN_XFER_NO - 1);
+ syn_offset = sdtr_data & ASC_SYN_MAX_OFFSET;
+ AscMsgOutSDTR(iop_base,
+ _sdtr_period_tbl_[syn_period_ix],
+ syn_offset);
+
+ scsiq->q1.cntl |= QC_MSG_OUT;
+ }
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+
+ if ((scsiq->q1.target_id & asc_dvc->use_tagged_qng) == 0) {
+ scsiq->q2.tag_code &= ~M2_QTAG_MSG_SIMPLE;
+ }
+ scsiq->q1.status = QS_FREE;
+
+ AscMemWordCopyToLram(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CDB_BEG),
+ (ushort dosfar *) scsiq->cdbptr,
+ (ushort) ((ushort) scsiq->q2.cdb_len >> 1));
+
+#if !CC_LITTLE_ENDIAN_HOST
+ AscAdjEndianScsiQ(scsiq);
+#endif
+
+ DvcPutScsiQ(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CPY_BEG),
+ (ushort dosfar *) & scsiq->q1.cntl,
+ (ushort) ((((sizeof (ASC_SCSIQ_1) + sizeof (ASC_SCSIQ_2)) / 2) - 1)));
+
+#if CC_WRITE_IO_COUNT
+ AscWriteLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_W_REQ_COUNT),
+ (ushort) asc_dvc->req_count);
+
+#endif
+
+#if CC_VERIFY_LRAM_COPY
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_NO_VERIFY_COPY) == 0) {
+
+ if (AscMemWordCmpToLram(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CDB_BEG),
+ (ushort dosfar *) scsiq->cdbptr,
+ (ushort) (scsiq->q2.cdb_len >> 1)) != 0) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_LOCAL_MEM);
+ return (ERR);
+ }
+ if (AscMemWordCmpToLram(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_CPY_BEG),
+ (ushort dosfar *) & scsiq->q1.cntl,
+ (ushort) (((sizeof (ASC_SCSIQ_1) + sizeof (ASC_SCSIQ_2)) / 2) - 1))
+ != 0) {
+ AscSetLibErrorCode(asc_dvc, ASCQ_ERR_LOCAL_MEM);
+ return (ERR);
+ }
+ }
+#endif
+
+#if CC_CLEAR_DMA_REMAIN
+
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_DW_REMAIN_XFER_ADDR), 0UL);
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_DW_REMAIN_XFER_CNT), 0UL);
+
+#endif
+
+ AscWriteLramWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ (ushort) (((ushort) scsiq->q1.q_no << 8) | (ushort) QS_READY));
+ return (1);
+}
+
+int
+AscPutReadySgListQueue(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_Q dosfar * scsiq,
+ uchar q_no
+)
+{
+ uchar sg_list_dwords;
+ uchar sg_index, i;
+ uchar sg_entry_cnt;
+ uchar next_qp;
+ ushort q_addr;
+ int sta;
+ ASC_SG_HEAD dosfar *sg_head;
+ ASC_SG_LIST_Q scsi_sg_q;
+ ulong saved_data_addr;
+ ulong saved_data_cnt;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+
+ sg_head = scsiq->sg_head;
+
+ saved_data_addr = scsiq->q1.data_addr;
+ saved_data_cnt = scsiq->q1.data_cnt;
+ scsiq->q1.data_addr = sg_head->sg_list[0].addr;
+ scsiq->q1.data_cnt = sg_head->sg_list[0].bytes;
+ sg_entry_cnt = sg_head->entry_cnt - 1;
+ if (sg_entry_cnt != 0) {
+ scsiq->q1.cntl |= QC_SG_HEAD;
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ sg_index = 1;
+ scsiq->q1.sg_queue_cnt = sg_head->queue_cnt;
+ scsi_sg_q.sg_head_qp = q_no;
+ scsi_sg_q.cntl = QCSG_SG_XFER_LIST;
+ for (i = 0; i < sg_head->queue_cnt; i++) {
+ scsi_sg_q.seq_no = i + 1;
+ if (sg_entry_cnt > ASC_SG_LIST_PER_Q) {
+ sg_list_dwords = (uchar) (ASC_SG_LIST_PER_Q * 2);
+ sg_entry_cnt -= ASC_SG_LIST_PER_Q;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q;
+ } else {
+ scsi_sg_q.sg_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ scsi_sg_q.sg_cur_list_cnt = ASC_SG_LIST_PER_Q - 1;
+ }
+ } else {
+
+ scsi_sg_q.cntl |= QCSG_SG_XFER_END;
+ sg_list_dwords = sg_entry_cnt << 1;
+ if (i == 0) {
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt;
+ } else {
+ scsi_sg_q.sg_list_cnt = sg_entry_cnt - 1;
+ scsi_sg_q.sg_cur_list_cnt = sg_entry_cnt - 1;
+ }
+ sg_entry_cnt = 0;
+ }
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FWD));
+ scsi_sg_q.q_no = next_qp;
+ q_addr = ASC_QNO_TO_QADDR(next_qp);
+
+ AscMemWordCopyToLram(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_SGHD_CPY_BEG),
+ (ushort dosfar *) & scsi_sg_q,
+ (ushort) (sizeof (ASC_SG_LIST_Q) >> 1));
+
+ AscMemDWordCopyToLram(iop_base,
+ (ushort) (q_addr + ASC_SGQ_LIST_BEG),
+ (ulong dosfar *) & sg_head->sg_list[sg_index],
+ (ushort) sg_list_dwords);
+
+ sg_index += ASC_SG_LIST_PER_Q;
+ }
+ } else {
+
+ scsiq->q1.cntl &= ~QC_SG_HEAD;
+ }
+ sta = AscPutReadyQueue(asc_dvc, scsiq, q_no);
+
+ scsiq->q1.data_addr = saved_data_addr;
+ scsiq->q1.data_cnt = saved_data_cnt;
+ return (sta);
+}
+
+int
+AscAbortSRB(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ulong srb_ptr
+)
+{
+ int sta;
+ ASC_SCSI_BIT_ID_TYPE saved_unit_not_ready;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ sta = ERR;
+ saved_unit_not_ready = asc_dvc->unit_not_ready;
+ asc_dvc->unit_not_ready = 0xFF;
+ AscWaitISRDone(asc_dvc);
+ if (AscStopQueueExe(iop_base) == 1) {
+ if (AscRiscHaltedAbortSRB(asc_dvc, srb_ptr) == 1) {
+ sta = 1;
+ AscCleanUpBusyQueue(iop_base);
+ AscStartQueueExe(iop_base);
+
+ } else {
+ sta = 0;
+ AscStartQueueExe(iop_base);
+ }
+ }
+ asc_dvc->unit_not_ready = saved_unit_not_ready;
+ return (sta);
+}
+
+int
+AscResetDevice(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix
+)
+{
+ PortAddr iop_base;
+ int sta;
+ uchar tid_no;
+ ASC_SCSI_BIT_ID_TYPE target_id;
+ int i;
+ ASC_SCSI_REQ_Q scsiq_buf;
+ ASC_SCSI_REQ_Q dosfar *scsiq;
+ uchar dosfar *buf;
+ ASC_SCSI_BIT_ID_TYPE saved_unit_not_ready;
+
+ iop_base = asc_dvc->iop_base;
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ target_id = ASC_TID_TO_TARGET_ID(tid_no);
+ saved_unit_not_ready = asc_dvc->unit_not_ready;
+ asc_dvc->unit_not_ready = target_id;
+ sta = ERR;
+ AscWaitTixISRDone(asc_dvc, target_ix);
+ if (AscStopQueueExe(iop_base) == 1) {
+ if (AscRiscHaltedAbortTIX(asc_dvc, target_ix) == 1) {
+
+ AscCleanUpBusyQueue(iop_base);
+ AscStartQueueExe(iop_base);
+
+ AscWaitTixISRDone(asc_dvc, target_ix);
+
+ sta = TRUE;
+ scsiq = (ASC_SCSI_REQ_Q dosfar *) & scsiq_buf;
+ buf = (uchar dosfar *) & scsiq_buf;
+ for (i = 0; i < sizeof (ASC_SCSI_REQ_Q); i++) {
+ *buf++ = 0x00;
+ }
+
+ scsiq->r1.status = (uchar) QS_READY;
+ scsiq->r2.cdb_len = 6;
+ scsiq->r2.tag_code = M2_QTAG_MSG_SIMPLE;
+ scsiq->r1.target_id = target_id;
+
+ scsiq->r2.target_ix = ASC_TIDLUN_TO_IX(tid_no, 0);
+ scsiq->cdbptr = (uchar dosfar *) scsiq->cdb;
+
+ scsiq->r1.cntl = QC_NO_CALLBACK | QC_MSG_OUT | QC_URGENT;
+ AscWriteLramByte(asc_dvc->iop_base, ASCV_MSGOUT_BEG,
+ M1_BUS_DVC_RESET);
+
+ asc_dvc->unit_not_ready &= ~target_id;
+
+ asc_dvc->sdtr_done |= target_id;
+
+ if (AscExeScsiQueue(asc_dvc, (ASC_SCSI_Q dosfar *) scsiq)
+ == 1) {
+ asc_dvc->unit_not_ready = target_id;
+ DvcSleepMilliSecond(1000);
+ _AscWaitQDone(iop_base, (ASC_SCSI_Q dosfar *) scsiq);
+ if (AscStopQueueExe(iop_base) == 1) {
+
+ AscCleanUpDiscQueue(iop_base);
+ AscStartQueueExe(iop_base);
+ if (asc_dvc->pci_fix_asyn_xfer & target_id) {
+
+ AscSetRunChipSynRegAtID(iop_base, tid_no,
+ ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ AscWaitTixISRDone(asc_dvc, target_ix);
+ }
+ } else {
+
+ sta = 0;
+ }
+
+ asc_dvc->sdtr_done &= ~target_id;
+ } else {
+ sta = ERR;
+ AscStartQueueExe(iop_base);
+ }
+ }
+ asc_dvc->unit_not_ready = saved_unit_not_ready;
+ return (sta);
+}
+
+int
+AscResetSB(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ int sta;
+ int i;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ asc_dvc->unit_not_ready = 0xFF;
+ sta = TRUE;
+ AscWaitISRDone(asc_dvc);
+ AscStopQueueExe(iop_base);
+
+ asc_dvc->sdtr_done = 0;
+ AscResetChipAndScsiBus(iop_base);
+
+ DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000));
+
+#if CC_SCAM
+ if (!(asc_dvc->dvc_cntl & ASC_CNTL_NO_SCAM)) {
+ AscSCAM(asc_dvc);
+ }
+#endif
+ AscReInitLram(asc_dvc);
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->cur_dvc_qng[i] = 0;
+ if (asc_dvc->pci_fix_asyn_xfer & (0x01 << i)) {
+
+ AscSetChipSynRegAtID(iop_base, i, ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ }
+
+ asc_dvc->err_code = 0;
+
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ sta = ERR;
+ }
+ if (AscStartChip(iop_base) == 0) {
+ sta = ERR;
+ }
+ AscStartQueueExe(iop_base);
+ asc_dvc->unit_not_ready = 0;
+ asc_dvc->queue_full_or_busy = 0;
+ return (sta);
+}
+
+int
+AscSetRunChipSynRegAtID(
+ PortAddr iop_base,
+ uchar tid_no,
+ uchar sdtr_data
+)
+{
+ int sta = FALSE;
+
+ if (AscHostReqRiscHalt(iop_base)) {
+ sta = AscSetChipSynRegAtID(iop_base, tid_no, sdtr_data);
+
+ AscStartChip(iop_base);
+ return (sta);
+ }
+ return (sta);
+}
+
+int
+AscSetChipSynRegAtID(
+ PortAddr iop_base,
+ uchar id,
+ uchar sdtr_data
+)
+{
+ AscSetBank(iop_base, 1);
+ AscWriteChipScsiID(iop_base, id);
+ if (AscReadChipScsiID(iop_base) != (0x01 << id)) {
+ return (FALSE);
+ }
+ AscSetBank(iop_base, 0);
+ AscWriteChipSyn(iop_base, sdtr_data);
+ if (AscReadChipSyn(iop_base) != sdtr_data) {
+ return (FALSE);
+ }
+ return (TRUE);
+}
+
+int
+AscReInitLram(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ AscInitLram(asc_dvc);
+ AscInitQLinkVar(asc_dvc);
+ return (0);
+}
+
+ushort
+AscInitLram(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc)
+{
+ uchar i;
+ ushort s_addr;
+ PortAddr iop_base;
+ ushort warn_code;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+
+ AscMemWordSetLram(iop_base, ASC_QADR_BEG, 0,
+ (ushort) (((int) (asc_dvc->max_total_qng + 2 + 1) * 64) >> 1)
+ );
+
+ i = ASC_MIN_ACTIVE_QNO;
+ s_addr = ASC_QADR_BEG + ASC_QBLK_SIZE;
+
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) (i + 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (asc_dvc->max_total_qng));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) i);
+ i++;
+ s_addr += ASC_QBLK_SIZE;
+ for (; i < asc_dvc->max_total_qng; i++, s_addr += ASC_QBLK_SIZE) {
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) (i + 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (i - 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) i);
+ }
+
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_FWD),
+ (uchar) ASC_QLINK_END);
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_BWD),
+ (uchar) (asc_dvc->max_total_qng - 1));
+ AscWriteLramByte(iop_base, (ushort) (s_addr + ASC_SCSIQ_B_QNO),
+ (uchar) asc_dvc->max_total_qng);
+ i++;
+ s_addr += ASC_QBLK_SIZE;
+
+ for (; i <= (uchar) (asc_dvc->max_total_qng + 3);
+ i++, s_addr += ASC_QBLK_SIZE) {
+
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_FWD), i);
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_BWD), i);
+ AscWriteLramByte(iop_base,
+ (ushort) (s_addr + (ushort) ASC_SCSIQ_B_QNO), i);
+ }
+
+ return (warn_code);
+}
+
+ushort
+AscInitQLinkVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ PortAddr iop_base;
+ int i;
+ ushort lram_addr;
+
+ iop_base = asc_dvc->iop_base;
+ AscPutRiscVarFreeQHead(iop_base, 1);
+ AscPutRiscVarDoneQTail(iop_base, asc_dvc->max_total_qng);
+
+ AscPutVarFreeQHead(iop_base, 1);
+ AscPutVarDoneQTail(iop_base, asc_dvc->max_total_qng);
+
+ AscWriteLramByte(iop_base, ASCV_BUSY_QHEAD_B,
+ (uchar) ((int) asc_dvc->max_total_qng + 1));
+ AscWriteLramByte(iop_base, ASCV_DISC1_QHEAD_B,
+ (uchar) ((int) asc_dvc->max_total_qng + 2));
+
+ AscWriteLramByte(iop_base, (ushort) ASCV_TOTAL_READY_Q_B,
+ asc_dvc->max_total_qng);
+
+ AscWriteLramWord(iop_base, ASCV_ASCDVC_ERR_CODE_W, 0);
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0);
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, 0);
+ AscWriteLramByte(iop_base, ASCV_SCSIBUSY_B, 0);
+ AscWriteLramByte(iop_base, ASCV_WTM_FLAG_B, 0);
+
+ AscWriteLramByte(iop_base, (ushort) ASCV_CDBCNT_B, 0);
+
+ lram_addr = ASC_QADR_BEG;
+ for (i = 0; i < 32; i++, lram_addr += 2) {
+ AscWriteLramWord(iop_base, lram_addr, 0);
+ }
+
+ return (0);
+}
+
+int
+AscSetLibErrorCode(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ushort err_code
+)
+{
+ if (asc_dvc->err_code == 0) {
+
+ asc_dvc->err_code = err_code;
+ AscWriteLramWord(asc_dvc->iop_base, ASCV_ASCDVC_ERR_CODE_W,
+ err_code);
+ }
+ return (err_code);
+}
+
+int
+_AscWaitQDone(
+ PortAddr iop_base,
+ ASC_SCSI_Q dosfar * scsiq
+)
+{
+ ushort q_addr;
+ uchar q_status;
+ int count = 0;
+
+ while (scsiq->q1.q_no == 0) ;
+ q_addr = ASC_QNO_TO_QADDR(scsiq->q1.q_no);
+
+ do {
+ q_status = AscReadLramByte(iop_base, q_addr + ASC_SCSIQ_B_STATUS);
+ DvcSleepMilliSecond(100L);
+ if (count++ > 30) {
+ return (0);
+ }
+ } while ((q_status & QS_READY) != 0);
+ return (1);
+}
+
+uchar _sdtr_period_tbl_[ASC_SYN_XFER_NO] =
+{
+ SYN_XFER_NS_0,
+ SYN_XFER_NS_1,
+ SYN_XFER_NS_2,
+ SYN_XFER_NS_3,
+ SYN_XFER_NS_4,
+ SYN_XFER_NS_5,
+ SYN_XFER_NS_6,
+ SYN_XFER_NS_7};
+
+uchar
+AscMsgOutSDTR(
+ PortAddr iop_base,
+ uchar sdtr_period,
+ uchar sdtr_offset
+)
+{
+ SDTR_XMSG sdtr_buf;
+ uchar sdtr_period_index;
+
+ sdtr_buf.msg_type = MS_EXTEND;
+ sdtr_buf.msg_len = MS_SDTR_LEN;
+ sdtr_buf.msg_req = MS_SDTR_CODE;
+ sdtr_buf.xfer_period = sdtr_period;
+ sdtr_offset &= ASC_SYN_MAX_OFFSET;
+ sdtr_buf.req_ack_offset = sdtr_offset;
+ AscMemWordCopyToLram(iop_base, ASCV_MSGOUT_BEG,
+ (ushort dosfar *) & sdtr_buf, SYN_XMSG_WLEN);
+ if ((sdtr_period_index = AscGetSynPeriodIndex(sdtr_period)) <=
+ ASC_MAX_SDTR_PERIOD_INDEX) {
+ return ((sdtr_period_index << 4) | sdtr_offset);
+ } else {
+
+ return (0);
+ }
+}
+
+uchar
+AscCalSDTRData(
+ uchar sdtr_period,
+ uchar syn_offset
+)
+{
+ uchar byte;
+ uchar sdtr_period_ix;
+
+ sdtr_period_ix = AscGetSynPeriodIndex(sdtr_period);
+ if ((sdtr_period_ix > ASC_MAX_SDTR_PERIOD_INDEX) ||
+ (sdtr_period_ix > ASC_SDTR_PERIOD_IX_MIN)) {
+ return (0xFF);
+ }
+ byte = (sdtr_period_ix << 4) | (syn_offset & ASC_SYN_MAX_OFFSET);
+ return (byte);
+}
+
+void
+AscSetChipSDTR(
+ PortAddr iop_base,
+ uchar sdtr_data,
+ uchar tid_no
+)
+{
+
+ AscWriteChipSyn(iop_base, sdtr_data);
+ AscWriteLramByte(iop_base,
+ (ushort) ((ushort) ASCV_SDTR_DONE_BEG + (ushort) tid_no),
+ sdtr_data);
+ return;
+}
+
+uchar
+AscGetSynPeriodIndex(
+ uchar syn_time
+)
+{
+ if ((syn_time >= SYN_XFER_NS_0) && (syn_time <= SYN_XFER_NS_7)) {
+ if (syn_time <= SYN_XFER_NS_6) {
+ if (syn_time <= SYN_XFER_NS_5) {
+ if (syn_time <= SYN_XFER_NS_4) {
+ if (syn_time <= SYN_XFER_NS_3) {
+ if (syn_time <= SYN_XFER_NS_2) {
+ if (syn_time <= SYN_XFER_NS_1) {
+ if (syn_time <= SYN_XFER_NS_0) {
+ return (0);
+ } else
+ return (1);
+ } else {
+ return (2);
+ }
+ } else {
+ return (3);
+ }
+ } else {
+ return (4);
+ }
+ } else {
+ return (5);
+ }
+ } else {
+ return (6);
+ }
+ } else {
+ return (7);
+ }
+ } else {
+
+ return (8);
+ }
+}
+
+uchar
+AscAllocFreeQueue(
+ PortAddr iop_base,
+ uchar free_q_head
+)
+{
+ ushort q_addr;
+ uchar next_qp;
+ uchar q_status;
+
+ q_addr = ASC_QNO_TO_QADDR(free_q_head);
+ q_status = (uchar) AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_STATUS));
+ next_qp = AscReadLramByte(iop_base,
+ (ushort) (q_addr + ASC_SCSIQ_B_FWD));
+ if (((q_status & QS_READY) == 0) &&
+ (next_qp != ASC_QLINK_END)) {
+ return (next_qp);
+ }
+ return (ASC_QLINK_END);
+}
+
+uchar
+AscAllocMultipleFreeQueue(
+ PortAddr iop_base,
+ uchar free_q_head,
+ uchar n_free_q
+)
+{
+ uchar i;
+
+ for (i = 0; i < n_free_q; i++) {
+ if ((free_q_head = AscAllocFreeQueue(iop_base, free_q_head))
+ == ASC_QLINK_END) {
+ return (ASC_QLINK_END);
+ }
+ }
+ return (free_q_head);
+}
+
+int
+AscRiscHaltedAbortSRB(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ulong srb_ptr
+)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ uchar q_no;
+ ASC_QDONE_INFO scsiq_buf;
+ ASC_QDONE_INFO dosfar *scsiq;
+ ASC_ISR_CALLBACK asc_isr_callback;
+ int last_int_level;
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+ last_int_level = DvcEnterCritical();
+ scsiq = (ASC_QDONE_INFO dosfar *) & scsiq_buf;
+
+#if CC_LINK_BUSY_Q
+ _AscAbortSrbBusyQueue(asc_dvc, scsiq, srb_ptr);
+#endif
+
+ for (q_no = ASC_MIN_ACTIVE_QNO; q_no <= asc_dvc->max_total_qng;
+ q_no++) {
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ scsiq->d2.srb_ptr = AscReadLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR));
+ if (scsiq->d2.srb_ptr == srb_ptr) {
+ _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq, asc_dvc->max_dma_count);
+ if (((scsiq->q_status & QS_READY) != 0) &&
+ ((scsiq->q_status & QS_ABORTED) == 0) &&
+ ((scsiq->cntl & QCSG_SG_XFER_LIST) == 0)) {
+
+ scsiq->q_status |= QS_ABORTED;
+ scsiq->d3.done_stat = QD_ABORTED_BY_HOST;
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR),
+ 0L);
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ scsiq->q_status);
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ return (1);
+ }
+ }
+ }
+ DvcLeaveCritical(last_int_level);
+ return (0);
+}
+
+int
+AscRiscHaltedAbortTIX(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix
+)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ uchar q_no;
+ ASC_QDONE_INFO scsiq_buf;
+ ASC_QDONE_INFO dosfar *scsiq;
+ ASC_ISR_CALLBACK asc_isr_callback;
+ int last_int_level;
+
+#if CC_LINK_BUSY_Q
+ uchar tid_no;
+
+#endif
+
+ iop_base = asc_dvc->iop_base;
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+ last_int_level = DvcEnterCritical();
+ scsiq = (ASC_QDONE_INFO dosfar *) & scsiq_buf;
+
+#if CC_LINK_BUSY_Q
+
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ _AscAbortTidBusyQueue(asc_dvc, scsiq, tid_no);
+
+#endif
+
+ for (q_no = ASC_MIN_ACTIVE_QNO; q_no <= asc_dvc->max_total_qng;
+ q_no++) {
+ q_addr = ASC_QNO_TO_QADDR(q_no);
+ _AscCopyLramScsiDoneQ(iop_base, q_addr, scsiq, asc_dvc->max_dma_count);
+ if (((scsiq->q_status & QS_READY) != 0) &&
+ ((scsiq->q_status & QS_ABORTED) == 0) &&
+ ((scsiq->cntl & QCSG_SG_XFER_LIST) == 0)) {
+ if (scsiq->d2.target_ix == target_ix) {
+ scsiq->q_status |= QS_ABORTED;
+ scsiq->d3.done_stat = QD_ABORTED_BY_HOST;
+
+ AscWriteLramDWord(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_D_SRBPTR),
+ 0L);
+
+ AscWriteLramByte(iop_base,
+ (ushort) (q_addr + (ushort) ASC_SCSIQ_B_STATUS),
+ scsiq->q_status);
+ (*asc_isr_callback) (asc_dvc, scsiq);
+ }
+ }
+ }
+ DvcLeaveCritical(last_int_level);
+ return (1);
+}
+
+#if CC_LINK_BUSY_Q
+
+#endif
+
+int
+AscHostReqRiscHalt(
+ PortAddr iop_base
+)
+{
+ int count = 0;
+ int sta = 0;
+ uchar saved_stop_code;
+
+ if (AscIsChipHalted(iop_base))
+ return (1);
+ saved_stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_HOST_REQ_RISC_HALT | ASC_STOP_REQ_RISC_STOP
+ );
+ do {
+ if (AscIsChipHalted(iop_base)) {
+ sta = 1;
+ break;
+ }
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, saved_stop_code);
+ return (sta);
+}
+
+int
+AscStopQueueExe(
+ PortAddr iop_base
+)
+{
+ int count;
+
+ count = 0;
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) == 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_REQ_RISC_STOP);
+ do {
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) &
+ ASC_STOP_ACK_RISC_STOP) {
+ return (1);
+ }
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (0);
+}
+
+int
+AscStartQueueExe(
+ PortAddr iop_base
+)
+{
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) != 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B, 0);
+ }
+ return (1);
+}
+
+int
+AscCleanUpBusyQueue(
+ PortAddr iop_base
+)
+{
+ int count;
+ uchar stop_code;
+
+ count = 0;
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) != 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_CLEAN_UP_BUSY_Q);
+ do {
+ stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+ if ((stop_code & ASC_STOP_CLEAN_UP_BUSY_Q) == 0)
+ break;
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (1);
+}
+
+int
+AscCleanUpDiscQueue(
+ PortAddr iop_base
+)
+{
+ int count;
+ uchar stop_code;
+
+ count = 0;
+ if (AscReadLramByte(iop_base, ASCV_STOP_CODE_B) != 0) {
+ AscWriteLramByte(iop_base, ASCV_STOP_CODE_B,
+ ASC_STOP_CLEAN_UP_DISC_Q);
+ do {
+ stop_code = AscReadLramByte(iop_base, ASCV_STOP_CODE_B);
+ if ((stop_code & ASC_STOP_CLEAN_UP_DISC_Q) == 0)
+ break;
+ DvcSleepMilliSecond(100);
+ } while (count++ < 20);
+ }
+ return (1);
+}
+
+int
+AscWaitTixISRDone(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar target_ix
+)
+{
+ uchar cur_req;
+ uchar tid_no;
+
+ tid_no = ASC_TIX_TO_TID(target_ix);
+ while (TRUE) {
+ if ((cur_req = asc_dvc->cur_dvc_qng[tid_no]) == 0) {
+ break;
+ }
+ DvcSleepMilliSecond(1000L);
+ if (asc_dvc->cur_dvc_qng[tid_no] == cur_req) {
+ break;
+ }
+ }
+ return (1);
+}
+
+int
+AscWaitISRDone(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ int tid;
+
+ for (tid = 0; tid <= ASC_MAX_TID; tid++) {
+ AscWaitTixISRDone(asc_dvc, ASC_TID_TO_TIX(tid));
+ }
+ return (1);
+}
+
+ulong
+AscGetOnePhyAddr(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ uchar dosfar * buf_addr,
+ ulong buf_size
+)
+{
+ ASC_MIN_SG_HEAD sg_head;
+
+ sg_head.entry_cnt = ASC_MIN_SG_LIST;
+ if (DvcGetSGList(asc_dvc, (uchar dosfar *) buf_addr,
+ buf_size, (ASC_SG_HEAD dosfar *) & sg_head) != buf_size) {
+ return (0L);
+ }
+ if (sg_head.entry_cnt > 1) {
+ return (0L);
+ }
+ return (sg_head.sg_list[0].addr);
+}
+
+ulong
+AscGetEisaProductID(
+ PortAddr iop_base
+)
+{
+ PortAddr eisa_iop;
+ ushort product_id_high, product_id_low;
+ ulong product_id;
+
+ eisa_iop = ASC_GET_EISA_SLOT(iop_base) | ASC_EISA_PID_IOP_MASK;
+ product_id_low = inpw(eisa_iop);
+ product_id_high = inpw(eisa_iop + 2);
+ product_id = ((ulong) product_id_high << 16) | (ulong) product_id_low;
+ return (product_id);
+}
+
+PortAddr
+AscSearchIOPortAddrEISA(
+ PortAddr iop_base
+)
+{
+ ulong eisa_product_id;
+
+ if (iop_base == 0) {
+ iop_base = ASC_EISA_MIN_IOP_ADDR;
+ } else {
+ if (iop_base == ASC_EISA_MAX_IOP_ADDR)
+ return (0);
+ if ((iop_base & 0x0050) == 0x0050) {
+ iop_base += ASC_EISA_BIG_IOP_GAP;
+ } else {
+ iop_base += ASC_EISA_SMALL_IOP_GAP;
+ }
+ }
+ while (iop_base <= ASC_EISA_MAX_IOP_ADDR) {
+
+ eisa_product_id = AscGetEisaProductID(iop_base);
+ if ((eisa_product_id == ASC_EISA_ID_740) ||
+ (eisa_product_id == ASC_EISA_ID_750)) {
+ if (AscFindSignature(iop_base)) {
+
+ inpw(iop_base + 4);
+ return (iop_base);
+ }
+ }
+ if (iop_base == ASC_EISA_MAX_IOP_ADDR)
+ return (0);
+ if ((iop_base & 0x0050) == 0x0050) {
+ iop_base += ASC_EISA_BIG_IOP_GAP;
+ } else {
+ iop_base += ASC_EISA_SMALL_IOP_GAP;
+ }
+ }
+ return (0);
+}
+
+int
+AscStartChip(
+ PortAddr iop_base
+)
+{
+ AscSetChipControl(iop_base, 0);
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
+ return (0);
+ }
+ return (1);
+}
+
+int
+AscStopChip(
+ PortAddr iop_base
+)
+{
+ uchar cc_val;
+
+ cc_val = AscGetChipControl(iop_base) & (~(CC_SINGLE_STEP | CC_TEST | CC_DIAG));
+ AscSetChipControl(iop_base, (uchar) (cc_val | CC_HALT));
+ AscSetChipIH(iop_base, INS_HALT);
+ AscSetChipIH(iop_base, INS_RFLAG_WTM);
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) == 0) {
+ return (0);
+ }
+ return (1);
+}
+
+int
+AscIsChipHalted(
+ PortAddr iop_base
+)
+{
+
+ if ((AscGetChipStatus(iop_base) & CSW_HALTED) != 0) {
+ if ((AscGetChipControl(iop_base) & CC_HALT) != 0) {
+ return (1);
+ }
+ }
+ return (0);
+}
+
+void
+AscSetChipIH(
+ PortAddr iop_base,
+ ushort ins_code
+)
+{
+ AscSetBank(iop_base, 1);
+ AscWriteChipIH(iop_base, ins_code);
+ AscSetBank(iop_base, 0);
+ return;
+}
+
+void
+AscAckInterrupt(
+ PortAddr iop_base
+)
+{
+
+ uchar host_flag;
+ uchar risc_flag;
+ ushort loop;
+
+ loop = 0;
+ do {
+ risc_flag = AscReadLramByte(iop_base, ASCV_RISC_FLAG_B);
+ if (loop++ > 0x7FFF) {
+ break;
+ }
+ } while ((risc_flag & ASC_RISC_FLAG_GEN_INT) != 0);
+
+ host_flag = AscReadLramByte(iop_base, ASCV_HOST_FLAG_B);
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B,
+ (uchar) (host_flag | ASC_HOST_FLAG_ACK_INT));
+
+ AscSetChipStatus(iop_base, CIW_INT_ACK);
+ loop = 0;
+ while (AscGetChipStatus(iop_base) & CSW_INT_PENDING) {
+
+ AscSetChipStatus(iop_base, CIW_INT_ACK);
+ if (loop++ > 3) {
+ break;
+ }
+ }
+
+ AscWriteLramByte(iop_base, ASCV_HOST_FLAG_B, host_flag);
+ return;
+}
+
+void
+AscDisableInterrupt(
+ PortAddr iop_base
+)
+{
+ ushort cfg;
+
+ cfg = AscGetChipCfgLsw(iop_base);
+ AscSetChipCfgLsw(iop_base, cfg & (~ASC_CFG0_HOST_INT_ON));
+ return;
+}
+
+void
+AscEnableInterrupt(
+ PortAddr iop_base
+)
+{
+ ushort cfg;
+
+ cfg = AscGetChipCfgLsw(iop_base);
+ AscSetChipCfgLsw(iop_base, cfg | ASC_CFG0_HOST_INT_ON);
+ return;
+}
+
+void
+AscSetBank(
+ PortAddr iop_base,
+ uchar bank
+)
+{
+ uchar val;
+
+ val = AscGetChipControl(iop_base) &
+ (~(CC_SINGLE_STEP | CC_TEST | CC_DIAG | CC_SCSI_RESET | CC_CHIP_RESET));
+ if (bank == 1) {
+ val |= CC_BANK_ONE;
+ } else if (bank == 2) {
+ val |= CC_DIAG | CC_BANK_ONE;
+ } else {
+ val &= ~CC_BANK_ONE;
+ }
+ AscSetChipControl(iop_base, val);
+ return;
+}
+
+int
+AscResetChipAndScsiBus(
+ PortAddr iop_base
+)
+{
+ AscStopChip(iop_base);
+ AscSetChipControl(iop_base, CC_CHIP_RESET | CC_SCSI_RESET | CC_HALT);
+ DvcSleepMilliSecond(200);
+
+ AscSetChipIH(iop_base, INS_RFLAG_WTM);
+ AscSetChipIH(iop_base, INS_HALT);
+
+ AscSetChipControl(iop_base, CC_CHIP_RESET | CC_HALT);
+ AscSetChipControl(iop_base, CC_HALT);
+ DvcSleepMilliSecond(200);
+ return (AscIsChipHalted(iop_base));
+}
+
+ushort
+AscGetIsaDmaChannel(
+ PortAddr iop_base
+)
+{
+ ushort channel;
+
+ channel = AscGetChipCfgLsw(iop_base) & 0x0003;
+ if (channel == 0x03)
+ return (0);
+ else if (channel == 0x00)
+ return (7);
+ return (channel + 4);
+}
+
+ushort
+AscSetIsaDmaChannel(
+ PortAddr iop_base,
+ ushort dma_channel
+)
+{
+ ushort cfg_lsw;
+ uchar value;
+
+ if ((dma_channel >= 5) && (dma_channel <= 7)) {
+
+ if (dma_channel == 7)
+ value = 0x00;
+ else
+ value = dma_channel - 4;
+ cfg_lsw = AscGetChipCfgLsw(iop_base) & 0xFFFC;
+ cfg_lsw |= value;
+ AscSetChipCfgLsw(iop_base, cfg_lsw);
+ return (AscGetIsaDmaChannel(iop_base));
+ }
+ return (0);
+}
+
+uchar
+AscSetIsaDmaSpeed(
+ PortAddr iop_base,
+ uchar speed_value
+)
+{
+ speed_value &= 0x07;
+ AscSetBank(iop_base, 1);
+ AscSetChipDmaSpeed(iop_base, speed_value);
+ AscSetBank(iop_base, 0);
+ return (AscGetIsaDmaSpeed(iop_base));
+}
+
+uchar
+AscGetIsaDmaSpeed(
+ PortAddr iop_base
+)
+{
+ uchar speed_value;
+
+ AscSetBank(iop_base, 1);
+ speed_value = AscGetChipDmaSpeed(iop_base);
+ speed_value &= 0x07;
+ AscSetBank(iop_base, 0);
+ return (speed_value);
+}
+
+ulong
+AscGetMaxDmaCount(
+ ushort bus_type
+)
+{
+ if (bus_type & ASC_IS_ISA)
+ return (ASC_MAX_ISA_DMA_COUNT);
+ else if (bus_type & (ASC_IS_EISA | ASC_IS_VL))
+ return (ASC_MAX_VL_DMA_COUNT);
+ return (ASC_MAX_PCI_DMA_COUNT);
+}
+
+ushort
+AscInitGetConfig(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ ushort warn_code;
+
+ warn_code = 0;
+ asc_dvc->init_state = ASC_INIT_STATE_BEG_GET_CFG;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (AscFindSignature(asc_dvc->iop_base)) {
+ warn_code |= AscInitAscDvcVar(asc_dvc);
+ warn_code |= AscInitFromEEP(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_GET_CFG;
+
+ if (asc_dvc->scsi_reset_wait > 10)
+ asc_dvc->scsi_reset_wait = 10;
+
+ } else {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ }
+ return (warn_code);
+}
+
+ushort
+AscInitSetConfig(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ ushort warn_code;
+
+ warn_code = 0;
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_SET_CFG;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ if (AscFindSignature(asc_dvc->iop_base)) {
+ warn_code |= AscInitFromAscDvcVar(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_SET_CFG;
+ } else {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ }
+ return (warn_code);
+}
+
+ushort
+AscInitAsc1000Driver(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ ushort warn_code;
+ PortAddr iop_base;
+
+ extern ushort _mcode_size;
+ extern ulong _mcode_chksum;
+ extern uchar _mcode_buf[];
+
+ ASC_DBG(3, "AscInitAsc1000Driver: begin\n");
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+
+ if ((asc_dvc->dvc_cntl & ASC_CNTL_RESET_SCSI) &&
+ !(asc_dvc->init_state & ASC_INIT_RESET_SCSI_DONE)) {
+
+ ASC_DBG(3, "AscInitAsc1000Driver: AscResetChipAndScsiBus()\n");
+ AscResetChipAndScsiBus(iop_base);
+ DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ }
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_LOAD_MC;
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ ASC_DBG(3, "AscInitAsc1000Driver: AscFindSignature()\n");
+ if (!AscFindSignature(asc_dvc->iop_base)) {
+ asc_dvc->err_code = ASC_IERR_BAD_SIGNATURE;
+ return (warn_code);
+ }
+ ASC_DBG(3, "AscInitAsc1000Driver: AscDisableInterrupt()\n");
+ AscDisableInterrupt(iop_base);
+
+ ASC_DBG(3, "AscInitAsc1000Driver: AscInitLram()\n");
+ warn_code |= AscInitLram(asc_dvc);
+ if (asc_dvc->err_code != 0)
+ return (UW_ERR);
+ ASC_DBG(3, "AscInitAsc1000Driver: AscLoadMicroCode()\n");
+ if (AscLoadMicroCode(iop_base, 0, (ushort dosfar *) _mcode_buf,
+ _mcode_size) != _mcode_chksum) {
+ asc_dvc->err_code |= ASC_IERR_MCODE_CHKSUM;
+ return (warn_code);
+ }
+ ASC_DBG(3, "AscInitAsc1000Driver: AscInitMicroCodeVar()\n");
+ warn_code |= AscInitMicroCodeVar(asc_dvc);
+ asc_dvc->init_state |= ASC_INIT_STATE_END_LOAD_MC;
+ ASC_DBG(3, "AscInitAsc1000Driver: AscEnableInterrupt()\n");
+ AscEnableInterrupt(iop_base);
+ return (warn_code);
+}
+
+ushort
+AscInitAscDvcVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ int i;
+ PortAddr iop_base;
+ ushort warn_code;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ asc_dvc->err_code = 0;
+
+ if ((asc_dvc->bus_type &
+ (ASC_IS_ISA | ASC_IS_PCI | ASC_IS_EISA | ASC_IS_VL)) == 0) {
+ asc_dvc->err_code |= ASC_IERR_NO_BUS_TYPE;
+ }
+#if CC_LINK_BUSY_Q
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->scsiq_busy_head[i] = (ASC_SCSI_Q dosfar *) 0L;
+ asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q dosfar *) 0L;
+ }
+#endif
+
+ asc_dvc->dvc_cntl = ASC_DEF_DVC_CNTL;
+ asc_dvc->bug_fix_cntl = 0;
+ asc_dvc->pci_fix_asyn_xfer = 0;
+ asc_dvc->init_sdtr = 0;
+ asc_dvc->sdtr_done = 0;
+ asc_dvc->max_total_qng = ASC_DEF_MAX_TOTAL_QNG;
+ asc_dvc->cur_total_qng = 0;
+ asc_dvc->is_in_int = 0;
+ asc_dvc->scsi_reset_wait = 3;
+ asc_dvc->in_critical_cnt = 0;
+
+ asc_dvc->last_q_shortage = 0;
+ asc_dvc->use_tagged_qng = 0;
+ asc_dvc->cfg->can_tagged_qng = 0;
+ asc_dvc->no_scam = 0;
+ asc_dvc->irq_no = 10;
+ asc_dvc->start_motor = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->disc_enable = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->cmd_qng_enabled = ASC_SCSI_WIDTH_BIT_SET;
+ asc_dvc->cfg->chip_scsi_id = ASC_DEF_CHIP_SCSI_ID;
+ asc_dvc->cfg->chip_version = AscGetChipVersion(iop_base,
+ asc_dvc->bus_type);
+ if (AscGetChipBusType(iop_base) == ASC_IS_ISAPNP) {
+
+ AscPutChipIFC(iop_base, IFC_INIT_DEFAULT);
+ asc_dvc->bus_type = ASC_IS_ISAPNP;
+ }
+ asc_dvc->unit_not_ready = 0;
+ asc_dvc->queue_full_or_busy = 0;
+
+ if ((asc_dvc->bus_type & ASC_IS_ISA) != 0) {
+ asc_dvc->cfg->isa_dma_channel = (uchar) AscGetIsaDmaChannel(iop_base);
+ asc_dvc->cfg->isa_dma_speed = ASC_DEF_ISA_DMA_SPEED;
+ }
+ asc_dvc->cfg->lib_serial_no = ASC_LIB_SERIAL_NUMBER;
+ asc_dvc->cfg->lib_version = (ASC_LIB_VERSION_MAJOR << 8) |
+ ASC_LIB_VERSION_MINOR;
+ asc_dvc->int_count = 0L;
+ asc_dvc->req_count = 0L;
+ asc_dvc->busy_count = 0L;
+ asc_dvc->max_dma_count = AscGetMaxDmaCount(asc_dvc->bus_type);
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->cfg->sdtr_data[i] =
+ (uchar) (ASC_DEF_SDTR_OFFSET | (ASC_DEF_SDTR_INDEX << 4));
+ asc_dvc->cur_dvc_qng[i] = 0;
+ asc_dvc->max_dvc_qng[i] = ASC_MAX_SCSI1_QNG;
+ asc_dvc->scsiq_busy_head[i] = (ASC_SCSI_Q dosfar *) 0L;
+ asc_dvc->scsiq_busy_tail[i] = (ASC_SCSI_Q dosfar *) 0L;
+ }
+ return (warn_code);
+}
+
+ushort
+AscInitFromAscDvcVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ PortAddr iop_base;
+ ushort cfg_msw;
+ ushort warn_code;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+
+ cfg_msw = AscGetChipCfgMsw(iop_base);
+
+ if ((cfg_msw & ASC_CFG_MSW_CLR_MASK) != 0) {
+ cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+ warn_code |= ASC_WARN_CFG_MSW_RECOVER;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ }
+ if (AscGetChipStatus(iop_base) & CSW_AUTO_CONFIG) {
+ warn_code |= ASC_WARN_AUTO_CONFIG;
+
+ }
+ if ((asc_dvc->cfg->cmd_qng_enabled & asc_dvc->cfg->disc_enable) !=
+ asc_dvc->cfg->cmd_qng_enabled) {
+ asc_dvc->cfg->disc_enable = asc_dvc->cfg->cmd_qng_enabled;
+ warn_code |= ASC_WARN_CMD_QNG_CONFLICT;
+ }
+ if ((asc_dvc->bus_type & (ASC_IS_ISA | ASC_IS_VL)) != 0) {
+
+ if (AscSetChipIRQ(iop_base, asc_dvc->irq_no, asc_dvc->bus_type)
+ != asc_dvc->irq_no) {
+ asc_dvc->err_code |= ASC_IERR_SET_IRQ_NO;
+ }
+ }
+ if (AscSetChipScsiID(iop_base, asc_dvc->cfg->chip_scsi_id) !=
+ asc_dvc->cfg->chip_scsi_id) {
+ asc_dvc->err_code |= ASC_IERR_SET_SCSI_ID;
+ }
+ if ((asc_dvc->bus_type & ASC_IS_ISA) != 0) {
+ AscSetIsaDmaChannel(iop_base, asc_dvc->cfg->isa_dma_channel);
+ AscSetIsaDmaSpeed(iop_base, asc_dvc->cfg->isa_dma_speed);
+ }
+ return (warn_code);
+}
+
+ushort
+AscInitFromEEP(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ ASCEEP_CONFIG eep_config_buf;
+ ASCEEP_CONFIG dosfar *eep_config;
+ PortAddr iop_base;
+ ushort chksum;
+ ushort warn_code;
+ ushort cfg_msw, cfg_lsw;
+ uchar i;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+
+ AscWriteLramWord(iop_base, ASCV_HALTCODE_W, 0x00FE);
+
+ AscStopQueueExe(iop_base);
+ if ((AscStopChip(iop_base) == FALSE) ||
+ (AscGetChipScsiCtrl(iop_base) != 0)) {
+ asc_dvc->init_state |= ASC_INIT_RESET_SCSI_DONE;
+ AscResetChipAndScsiBus(iop_base);
+ DvcSleepMilliSecond((ulong) ((ushort) asc_dvc->scsi_reset_wait * 1000));
+ }
+ if (AscIsChipHalted(iop_base) == FALSE) {
+ asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
+ return (warn_code);
+ }
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
+ return (warn_code);
+ }
+ eep_config = (ASCEEP_CONFIG dosfar *) & eep_config_buf;
+
+ cfg_msw = AscGetChipCfgMsw(iop_base);
+ cfg_lsw = AscGetChipCfgLsw(iop_base);
+
+ if (asc_dvc->bus_type & ASC_IS_PCI) {
+#if CC_DISABLE_PCI_PARITY_INT
+ cfg_msw &= 0xFFC0;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+#endif
+ if (asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_A) {
+ asc_dvc->bug_fix_cntl |= ASC_BUG_FIX_ADD_ONE_BYTE;
+ }
+ }
+ if ((cfg_msw & ASC_CFG_MSW_CLR_MASK) != 0) {
+ cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+ warn_code |= ASC_WARN_CFG_MSW_RECOVER;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ }
+ chksum = AscGetEEPConfig(iop_base, eep_config, asc_dvc->bus_type);
+
+ eep_config->cfg_msw &= (~(ASC_CFG_MSW_CLR_MASK));
+
+ if (AscGetChipStatus(iop_base) & CSW_AUTO_CONFIG) {
+ warn_code |= ASC_WARN_AUTO_CONFIG;
+
+ if (asc_dvc->cfg->chip_version == 3) {
+
+ if (eep_config->cfg_lsw != cfg_lsw) {
+ warn_code |= ASC_WARN_EEPROM_RECOVER;
+ eep_config->cfg_lsw = AscGetChipCfgLsw(iop_base);
+ }
+ if (eep_config->cfg_msw != cfg_msw) {
+ warn_code |= ASC_WARN_EEPROM_RECOVER;
+ eep_config->cfg_msw = AscGetChipCfgMsw(iop_base);
+ }
+ }
+ }
+ eep_config->cfg_lsw |= ASC_CFG0_HOST_INT_ON;
+ if (chksum != eep_config->chksum) {
+ warn_code |= ASC_WARN_EEPROM_CHKSUM;
+ }
+ asc_dvc->init_sdtr = eep_config->init_sdtr;
+ asc_dvc->cfg->disc_enable = eep_config->disc_enable;
+
+ asc_dvc->cfg->cmd_qng_enabled = eep_config->use_cmd_qng;
+ asc_dvc->cfg->isa_dma_speed = eep_config->isa_dma_speed;
+ asc_dvc->start_motor = eep_config->start_motor;
+ asc_dvc->dvc_cntl = eep_config->cntl;
+ asc_dvc->no_scam = eep_config->no_scam;
+
+ if ((asc_dvc->bus_type & ASC_IS_PCI) &&
+ !(asc_dvc->dvc_cntl & ASC_CNTL_NO_PCI_FIX_ASYN_XFER)) {
+ if ((asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_A) ||
+ (asc_dvc->cfg->pci_device_id == ASC_PCI_DEVICE_ID_REV_B)) {
+ asc_dvc->pci_fix_asyn_xfer = ASC_ALL_DEVICE_BIT_SET;
+ }
+ } else if (asc_dvc->bus_type & ASC_IS_ISAPNP) {
+
+ if (AscGetChipVersion(iop_base, asc_dvc->bus_type)
+ == ASC_CHIP_VER_ASYN_BUG) {
+ asc_dvc->pci_fix_asyn_xfer = ASC_ALL_DEVICE_BIT_SET;
+ }
+ }
+ if (!AscTestExternalLram(asc_dvc)) {
+ if (asc_dvc->bus_type & ASC_IS_PCI) {
+ eep_config->cfg_msw |= 0x0800;
+ cfg_msw |= 0x0800;
+ AscSetChipCfgMsw(iop_base, cfg_msw);
+ eep_config->max_total_qng = ASC_MAX_PCI_INRAM_TOTAL_QNG;
+ eep_config->max_tag_qng = ASC_MAX_INRAM_TAG_QNG;
+ }
+ } else {
+#if CC_TEST_RW_LRAM
+ asc_dvc->err_code |= AscTestLramEndian(iop_base);
+#endif
+ }
+ if (eep_config->max_total_qng < ASC_MIN_TOTAL_QNG) {
+ eep_config->max_total_qng = ASC_MIN_TOTAL_QNG;
+ }
+ if (eep_config->max_total_qng > ASC_MAX_TOTAL_QNG) {
+ eep_config->max_total_qng = ASC_MAX_TOTAL_QNG;
+ }
+ if (eep_config->max_tag_qng > eep_config->max_total_qng) {
+ eep_config->max_tag_qng = eep_config->max_total_qng;
+ }
+ if (eep_config->max_tag_qng < ASC_MIN_TAG_Q_PER_DVC) {
+ eep_config->max_tag_qng = ASC_MIN_TAG_Q_PER_DVC;
+ }
+ asc_dvc->max_total_qng = eep_config->max_total_qng;
+
+ if ((eep_config->use_cmd_qng & eep_config->disc_enable) !=
+ eep_config->use_cmd_qng) {
+ eep_config->disc_enable = eep_config->use_cmd_qng;
+ warn_code |= ASC_WARN_CMD_QNG_CONFLICT;
+ }
+ asc_dvc->irq_no = AscGetChipIRQ(iop_base, asc_dvc->bus_type);
+ eep_config->chip_scsi_id &= ASC_MAX_TID;
+ asc_dvc->cfg->chip_scsi_id = eep_config->chip_scsi_id;
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ asc_dvc->cfg->sdtr_data[i] = eep_config->sdtr_data[i];
+ asc_dvc->cfg->max_tag_qng[i] = eep_config->max_tag_qng;
+ }
+
+ eep_config->cfg_msw = AscGetChipCfgMsw(iop_base);
+ if (AscSetEEPConfig(iop_base, eep_config, asc_dvc->bus_type) != 0) {
+ asc_dvc->err_code |= ASC_IERR_WRITE_EEPROM;
+ }
+ return (warn_code);
+}
+
+ushort
+AscInitMicroCodeVar(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ int i;
+ ushort warn_code;
+ PortAddr iop_base;
+ ulong phy_addr;
+
+ iop_base = asc_dvc->iop_base;
+ warn_code = 0;
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ AscWriteLramByte(iop_base, (ushort) (ASCV_SDTR_DATA_BEG + i),
+ asc_dvc->cfg->sdtr_data[i]);
+ }
+
+ AscInitQLinkVar(asc_dvc);
+
+ AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B,
+ asc_dvc->cfg->disc_enable);
+ AscWriteLramByte(iop_base, ASCV_HOSTSCSI_ID_B,
+ ASC_TID_TO_TARGET_ID(asc_dvc->cfg->chip_scsi_id));
+ if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
+ (uchar dosfar *) asc_dvc->cfg->overrun_buf,
+ ASC_OVERRUN_BSIZE)) == 0L) {
+ asc_dvc->err_code |= ASC_IERR_GET_PHY_ADDR;
+ } else {
+
+ phy_addr = (phy_addr & 0xFFFFFFF8UL) + 8;
+ AscWriteLramDWord(iop_base, ASCV_OVERRUN_PADDR_D, phy_addr);
+ AscWriteLramDWord(iop_base, ASCV_OVERRUN_BSIZE_D,
+ ASC_OVERRUN_BSIZE - 8);
+ }
+
+ asc_dvc->cfg->mcode_date = AscReadLramWord(iop_base,
+ (ushort) ASCV_MC_DATE_W);
+ asc_dvc->cfg->mcode_version = AscReadLramWord(iop_base,
+ (ushort) ASCV_MC_VER_W);
+ AscSetPCAddr(iop_base, ASC_MCODE_START_ADDR);
+ if (AscGetPCAddr(iop_base) != ASC_MCODE_START_ADDR) {
+ asc_dvc->err_code |= ASC_IERR_SET_PC_ADDR;
+ return (warn_code);
+ }
+ if (AscStartChip(iop_base) != 1) {
+ asc_dvc->err_code |= ASC_IERR_START_STOP_CHIP;
+ return (warn_code);
+ }
+ return (warn_code);
+}
+
+void dosfar
+AscInitPollIsrCallBack(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_QDONE_INFO dosfar * scsi_done_q
+)
+{
+ ASC_SCSI_REQ_Q dosfar *scsiq_req;
+ ASC_ISR_CALLBACK asc_isr_callback;
+ uchar cp_sen_len;
+ uchar i;
+
+ if ((scsi_done_q->d2.flag & ASC_FLAG_SCSIQ_REQ) != 0) {
+ scsiq_req = (ASC_SCSI_REQ_Q dosfar *) scsi_done_q->d2.srb_ptr;
+ ASC_DBG2(3, "AscInitPollIsrCallBack: done_stat %x, host_stat %x\n",
+ scsiq_req->r3.done_stat, scsiq_req->r3.host_stat);
+ scsiq_req->r3.done_stat = scsi_done_q->d3.done_stat;
+ scsiq_req->r3.host_stat = scsi_done_q->d3.host_stat;
+ scsiq_req->r3.scsi_stat = scsi_done_q->d3.scsi_stat;
+ scsiq_req->r3.scsi_msg = scsi_done_q->d3.scsi_msg;
+ if ((scsi_done_q->d3.scsi_stat == SS_CHK_CONDITION) &&
+ (scsi_done_q->d3.host_stat == 0)) {
+ cp_sen_len = (uchar) ASC_MIN_SENSE_LEN;
+ if (scsiq_req->r1.sense_len < ASC_MIN_SENSE_LEN) {
+ cp_sen_len = (uchar) scsiq_req->r1.sense_len;
+ }
+ for (i = 0; i < cp_sen_len; i++) {
+ scsiq_req->sense[i] = scsiq_req->sense_ptr[i];
+ }
+ }
+ } else {
+ ASC_DBG1(3, "AscInitPollIsrCallBack: isr_callback %x\n",
+ (unsigned) asc_dvc->isr_callback);
+ if (asc_dvc->isr_callback != 0) {
+ asc_isr_callback = (ASC_ISR_CALLBACK) asc_dvc->isr_callback;
+ (*asc_isr_callback) (asc_dvc, scsi_done_q);
+ }
+ }
+ return;
+}
+
+int
+AscTestExternalLram(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ PortAddr iop_base;
+ ushort q_addr;
+ ushort saved_word;
+ int sta;
+
+ iop_base = asc_dvc->iop_base;
+ sta = 0;
+
+ q_addr = ASC_QNO_TO_QADDR(241);
+ saved_word = AscReadLramWord(iop_base, q_addr);
+ if (AscVerWriteLramWord(iop_base, q_addr, 0x55AA) == 0) {
+ sta = 1;
+ AscWriteLramWord(iop_base, q_addr, saved_word);
+ }
+ return (sta);
+}
+
+#if CC_TEST_LRAM_ENDIAN
+
+#endif
+
+int
+AscWriteEEPCmdReg(
+ PortAddr iop_base,
+ uchar cmd_reg
+)
+{
+ uchar read_back;
+ int retry;
+
+ retry = 0;
+ while (TRUE) {
+ AscSetChipEEPCmd(iop_base, cmd_reg);
+ DvcSleepMilliSecond(1);
+ read_back = AscGetChipEEPCmd(iop_base);
+ if (read_back == cmd_reg) {
+ return (1);
+ }
+ if (retry++ > ASC_EEP_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+int
+AscWriteEEPDataReg(
+ PortAddr iop_base,
+ ushort data_reg
+)
+{
+ ushort read_back;
+ int retry;
+
+ retry = 0;
+ while (TRUE) {
+ AscSetChipEEPData(iop_base, data_reg);
+ DvcSleepMilliSecond(1);
+ read_back = AscGetChipEEPData(iop_base);
+ if (read_back == data_reg) {
+ return (1);
+ }
+ if (retry++ > ASC_EEP_MAX_RETRY) {
+ return (0);
+ }
+ }
+}
+
+void
+AscWaitEEPRead(
+ void
+)
+{
+ DvcSleepMilliSecond(1);
+ return;
+}
+
+void
+AscWaitEEPWrite(
+ void
+)
+{
+ DvcSleepMilliSecond(20);
+ return;
+}
+
+ushort
+AscReadEEPWord(
+ PortAddr iop_base,
+ uchar addr
+)
+{
+ ushort read_wval;
+ uchar cmd_reg;
+
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_DISABLE);
+ AscWaitEEPRead();
+ cmd_reg = addr | ASC_EEP_CMD_READ;
+ AscWriteEEPCmdReg(iop_base, cmd_reg);
+ AscWaitEEPRead();
+ read_wval = AscGetChipEEPData(iop_base);
+ AscWaitEEPRead();
+ return (read_wval);
+}
+
+ushort
+AscWriteEEPWord(
+ PortAddr iop_base,
+ uchar addr,
+ ushort word_val
+)
+{
+ ushort read_wval;
+
+ read_wval = AscReadEEPWord(iop_base, addr);
+ if (read_wval != word_val) {
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_ABLE);
+ AscWaitEEPRead();
+
+ AscWriteEEPDataReg(iop_base, word_val);
+ AscWaitEEPRead();
+
+ AscWriteEEPCmdReg(iop_base,
+ (uchar) ((uchar) ASC_EEP_CMD_WRITE | addr));
+ AscWaitEEPWrite();
+
+ AscWriteEEPCmdReg(iop_base, ASC_EEP_CMD_WRITE_DISABLE);
+ AscWaitEEPRead();
+ return (AscReadEEPWord(iop_base, addr));
+ }
+ return (read_wval);
+}
+
+ushort
+AscGetEEPConfig(
+ PortAddr iop_base,
+ ASCEEP_CONFIG dosfar * cfg_buf, ushort bus_type
+)
+{
+ ushort wval;
+ ushort sum;
+ ushort dosfar *wbuf;
+ int cfg_beg;
+ int cfg_end;
+ int s_addr;
+ int isa_pnp_wsize;
+
+ wbuf = (ushort dosfar *) cfg_buf;
+ sum = 0;
+
+ isa_pnp_wsize = 0;
+ for (s_addr = 0; s_addr < (2 + isa_pnp_wsize); s_addr++, wbuf++) {
+ wval = AscReadEEPWord(iop_base, (uchar) s_addr);
+ sum += wval;
+ *wbuf = wval;
+ }
+
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1);
+ s_addr++, wbuf++) {
+ wval = AscReadEEPWord(iop_base, (uchar) s_addr);
+ sum += wval;
+ *wbuf = wval;
+ }
+ *wbuf = AscReadEEPWord(iop_base, (uchar) s_addr);
+ return (sum);
+}
+
+int
+AscSetEEPConfigOnce(
+ PortAddr iop_base,
+ ASCEEP_CONFIG dosfar * cfg_buf, ushort bus_type
+)
+{
+ int n_error;
+ ushort dosfar *wbuf;
+ ushort sum;
+ int s_addr;
+ int cfg_beg;
+ int cfg_end;
+
+ wbuf = (ushort dosfar *) cfg_buf;
+ n_error = 0;
+ sum = 0;
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != AscWriteEEPWord(iop_base, (uchar) s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ if (bus_type & ASC_IS_VL) {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG_VL;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR_VL;
+ } else {
+ cfg_beg = ASC_EEP_DVC_CFG_BEG;
+ cfg_end = ASC_EEP_MAX_DVC_ADDR;
+ }
+ for (s_addr = cfg_beg; s_addr <= (cfg_end - 1);
+ s_addr++, wbuf++) {
+ sum += *wbuf;
+ if (*wbuf != AscWriteEEPWord(iop_base, (uchar) s_addr, *wbuf)) {
+ n_error++;
+ }
+ }
+ *wbuf = sum;
+ if (sum != AscWriteEEPWord(iop_base, (uchar) s_addr, sum)) {
+ n_error++;
+ }
+ wbuf = (ushort dosfar *) cfg_buf;
+ for (s_addr = 0; s_addr < 2; s_addr++, wbuf++) {
+ if (*wbuf != AscReadEEPWord(iop_base, (uchar) s_addr)) {
+ n_error++;
+ }
+ }
+ for (s_addr = cfg_beg; s_addr <= cfg_end;
+ s_addr++, wbuf++) {
+ if (*wbuf != AscReadEEPWord(iop_base, (uchar) s_addr)) {
+ n_error++;
+ }
+ }
+ return (n_error);
+}
+
+int
+AscSetEEPConfig(
+ PortAddr iop_base,
+ ASCEEP_CONFIG dosfar * cfg_buf, ushort bus_type
+)
+{
+ int retry;
+ int n_error;
+
+ retry = 0;
+ while (TRUE) {
+ if ((n_error = AscSetEEPConfigOnce(iop_base, cfg_buf,
+ bus_type)) == 0) {
+ break;
+ }
+ if (++retry > ASC_EEP_MAX_RETRY) {
+ break;
+ }
+ }
+ return (n_error);
+}
+
+int
+AscInitPollBegin(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+
+#if CC_INIT_INQ_DISPLAY
+ DvcDisplayString((uchar dosfar *) "\r\n");
+#endif
+
+ AscDisableInterrupt(iop_base);
+
+ asc_dvc->init_state |= ASC_INIT_STATE_BEG_INQUIRY;
+
+ AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B, 0x00);
+ asc_dvc->use_tagged_qng = 0;
+ asc_dvc->cfg->can_tagged_qng = 0;
+ asc_dvc->saved_ptr2func = (ulong) asc_dvc->isr_callback;
+ asc_dvc->isr_callback = ASC_GET_PTR2FUNC(AscInitPollIsrCallBack);
+ return (0);
+}
+
+int
+AscInitPollEnd(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc
+)
+{
+ PortAddr iop_base;
+ int i;
+
+ iop_base = asc_dvc->iop_base;
+ asc_dvc->isr_callback = (Ptr2Func) asc_dvc->saved_ptr2func;
+ AscWriteLramByte(iop_base, ASCV_DISC_ENABLE_B,
+ asc_dvc->cfg->disc_enable);
+ AscWriteLramByte(iop_base, ASCV_USE_TAGGED_QNG_B,
+ asc_dvc->use_tagged_qng);
+ AscWriteLramByte(iop_base, ASCV_CAN_TAGGED_QNG_B,
+ asc_dvc->cfg->can_tagged_qng);
+
+ for (i = 0; i <= ASC_MAX_TID; i++) {
+ AscWriteLramByte(iop_base,
+ (ushort) ((ushort) ASCV_MAX_DVC_QNG_BEG + (ushort) i),
+ asc_dvc->max_dvc_qng[i]);
+ }
+
+ AscEnableInterrupt(iop_base);
+
+#if CC_INIT_INQ_DISPLAY
+ DvcDisplayString((uchar dosfar *) "\r\n");
+#endif
+ asc_dvc->init_state |= ASC_INIT_STATE_END_INQUIRY;
+
+ return (0);
+}
+
+int _asc_wait_slow_device_ = FALSE;
+
+int
+AscInitPollTarget(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ ASC_SCSI_INQUIRY dosfar * inq,
+ ASC_CAP_INFO dosfar * cap_info
+)
+{
+ uchar tid_no, lun;
+ uchar dvc_type;
+ ASC_SCSI_BIT_ID_TYPE tid_bits;
+ int dvc_found;
+ int support_read_cap;
+ int tmp_disable_init_sdtr;
+ ulong phy_addr;
+
+ dvc_found = 0;
+ tmp_disable_init_sdtr = FALSE;
+ tid_bits = scsiq->r1.target_id;
+ lun = scsiq->r1.target_lun;
+ tid_no = ASC_TIX_TO_TID(scsiq->r2.target_ix);
+ if ((phy_addr = AscGetOnePhyAddr(asc_dvc,
+ (uchar dosfar *) scsiq->sense_ptr,
+ (ulong) scsiq->r1.sense_len)) == 0L) {
+ return (ERR);
+ }
+ scsiq->r1.sense_addr = phy_addr;
+ if (((asc_dvc->init_sdtr & tid_bits) != 0) &&
+ ((asc_dvc->sdtr_done & tid_bits) == 0)) {
+
+ asc_dvc->init_sdtr &= ~tid_bits;
+ tmp_disable_init_sdtr = TRUE;
+ }
+ ASC_DBG(3, "AscInitPollTarget: PollScsiInquiry()\n");
+ if (PollScsiInquiry(asc_dvc, scsiq, (uchar dosfar *) inq,
+ sizeof (ASC_SCSI_INQUIRY)) == 1) {
+ dvc_found = 1;
+ support_read_cap = TRUE;
+ dvc_type = inq->byte0.peri_dvc_type;
+ if (dvc_type != SCSI_TYPE_UNKNOWN) {
+ if ((dvc_type != SCSI_TYPE_DASD) &&
+ (dvc_type != SCSI_TYPE_WORM) &&
+ (dvc_type != SCSI_TYPE_CDROM) &&
+ (dvc_type != SCSI_TYPE_OPTMEM)) {
+ asc_dvc->start_motor &= ~tid_bits;
+ support_read_cap = FALSE;
+ }
+ if ((dvc_type != SCSI_TYPE_DASD) ||
+ inq->byte1.rmb) {
+
+ if (!_asc_wait_slow_device_) {
+ DvcSleepMilliSecond(3000 - ((int) tid_no * 250));
+ _asc_wait_slow_device_ = TRUE;
+ }
+ }
+#if CC_INIT_INQ_DISPLAY
+ AscDispInquiry(tid_no, lun, inq);
+#endif
+
+ if (lun == 0) {
+
+ if ((inq->byte3.rsp_data_fmt >= 2) ||
+ (inq->byte2.ansi_apr_ver >= 2)) {
+
+ if (inq->byte7.CmdQue) {
+ asc_dvc->cfg->can_tagged_qng |= tid_bits;
+ if (asc_dvc->cfg->cmd_qng_enabled & tid_bits) {
+ asc_dvc->use_tagged_qng |= tid_bits;
+ asc_dvc->max_dvc_qng[tid_no] =
+ asc_dvc->cfg->max_tag_qng[tid_no];
+ }
+ }
+ if (!inq->byte7.Sync) {
+
+ asc_dvc->init_sdtr &= ~tid_bits;
+ asc_dvc->sdtr_done &= ~tid_bits;
+ } else if (tmp_disable_init_sdtr) {
+
+ asc_dvc->init_sdtr |= tid_bits;
+ }
+ } else {
+
+ asc_dvc->init_sdtr &= ~tid_bits;
+ asc_dvc->sdtr_done &= ~tid_bits;
+ asc_dvc->use_tagged_qng &= ~tid_bits;
+ }
+ }
+ if (asc_dvc->pci_fix_asyn_xfer & tid_bits) {
+ if (!(asc_dvc->init_sdtr & tid_bits)) {
+
+ AscSetRunChipSynRegAtID(asc_dvc->iop_base, tid_no,
+ ASYN_SDTR_DATA_FIX_PCI_REV_AB);
+ }
+ }
+ ASC_DBG(3, "AscInitPollTarget: InitTestUnitReady()\n");
+ if (InitTestUnitReady(asc_dvc, scsiq) != 1) {
+
+ } else {
+ if ((cap_info != 0L) && support_read_cap) {
+ ASC_DBG(3, "AscInitPollTarget: PollScsiReadCapacity()\n");
+ if (PollScsiReadCapacity(asc_dvc, scsiq,
+ cap_info) != 1) {
+ cap_info->lba = 0L;
+ cap_info->blk_size = 0x0000;
+ } else {
+
+ }
+ }
+ }
+ } else {
+ asc_dvc->start_motor &= ~tid_bits;
+ }
+ } else {
+
+ }
+ return (dvc_found);
+}
+
+int
+PollQueueDone(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ int timeout_sec
+)
+{
+ int status;
+ int retry;
+
+ retry = 0;
+ do {
+ ASC_DBG(3, "PollQueueDone: AscExeScsiQueue()\n");
+ if ((status = AscExeScsiQueue(asc_dvc,
+ (ASC_SCSI_Q dosfar *) scsiq)) == 1) {
+ ASC_DBG(3, "PollQueueDone: AscPollQDone()\n");
+ if ((status = AscPollQDone(asc_dvc, scsiq,
+ timeout_sec)) != 1) {
+ ASC_DBG1(3, "PollQueueDone: AscPollQDone() status %x\n", status);
+ if (status == 0x80) {
+ if (retry++ > ASC_MAX_INIT_BUSY_RETRY) {
+ break;
+ }
+ scsiq->r3.done_stat = 0;
+ scsiq->r3.host_stat = 0;
+ scsiq->r3.scsi_stat = 0;
+ scsiq->r3.scsi_msg = 0;
+ DvcSleepMilliSecond(100);
+ continue;
+ }
+ scsiq->r3.done_stat = 0;
+ scsiq->r3.host_stat = 0;
+ scsiq->r3.scsi_stat = 0;
+ scsiq->r3.scsi_msg = 0;
+ ASC_DBG1(3, "PollQueueDone: AscAbortSRB() scsiq %x\n",
+ (unsigned) scsiq);
+
+ AscAbortSRB(asc_dvc, (ulong) scsiq);
+ }
+ ASC_DBG1(3, "PollQueueDone: done_stat %x\n", scsiq->r3.done_stat);
+ return (scsiq->r3.done_stat);
+ }
+ } while ((status == 0) || (status == 0x80));
+ ASC_DBG(3, "PollQueueDone: done_stat QD_WITH_ERROR\n");
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+}
+
+int
+PollScsiInquiry(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ uchar dosfar * buf,
+ int buf_len
+)
+{
+ if (AscScsiInquiry(asc_dvc, scsiq, buf, buf_len) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q dosfar *) scsiq, 4));
+}
+
+int
+PollScsiReadCapacity(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ ASC_CAP_INFO dosfar * cap_info
+)
+{
+ ASC_CAP_INFO scsi_cap_info;
+ int status;
+
+ if (AscScsiReadCapacity(asc_dvc, scsiq,
+ (uchar dosfar *) & scsi_cap_info) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ status = PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q dosfar *) scsiq, 8);
+ if (status == 1) {
+#if CC_LITTLE_ENDIAN_HOST
+ cap_info->lba = (ulong) * swapfarbuf4((uchar dosfar *) & scsi_cap_info.lba);
+ cap_info->blk_size = (ulong) * swapfarbuf4((uchar dosfar *) & scsi_cap_info.blk_size);
+#else
+ cap_info->lba = scsi_cap_info.lba;
+ cap_info->blk_size = scsi_cap_info.blk_size;
+#endif
+ return (scsiq->r3.done_stat);
+ }
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+}
+
+ulong dosfar *
+swapfarbuf4(
+ uchar dosfar * buf
+)
+{
+ uchar tmp;
+
+ tmp = buf[3];
+ buf[3] = buf[0];
+ buf[0] = tmp;
+
+ tmp = buf[1];
+ buf[1] = buf[2];
+ buf[2] = tmp;
+
+ return ((ulong dosfar *) buf);
+}
+
+int
+PollScsiTestUnitReady(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq
+)
+{
+ if (AscScsiTestUnitReady(asc_dvc, scsiq) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q dosfar *) scsiq, 12));
+}
+
+int
+PollScsiStartUnit(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq
+)
+{
+ if (AscScsiStartStopUnit(asc_dvc, scsiq, 1) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ return (PollQueueDone(asc_dvc, (ASC_SCSI_REQ_Q dosfar *) scsiq, 40));
+}
+
+int
+InitTestUnitReady(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq
+)
+{
+ ASC_SCSI_BIT_ID_TYPE tid_bits;
+ int retry;
+ ASC_REQ_SENSE dosfar *sen;
+
+ retry = 0;
+ tid_bits = scsiq->r1.target_id;
+ while (retry++ < 2) {
+ ASC_DBG(3, "InitTestUnitReady: PollScsiTestUnitReady()\n");
+ PollScsiTestUnitReady(asc_dvc, scsiq);
+ ASC_DBG1(3, "InitTestUnitReady: done_stat %x\n", scsiq->r3.done_stat);
+ if (scsiq->r3.done_stat == 0x01) {
+ return (1);
+ } else if (scsiq->r3.done_stat == QD_WITH_ERROR) {
+ DvcSleepMilliSecond(100);
+
+ sen = (ASC_REQ_SENSE dosfar *) scsiq->sense_ptr;
+
+ if ((scsiq->r3.scsi_stat == SS_CHK_CONDITION) &&
+ ((sen->err_code & 0x70) != 0)) {
+
+ if (sen->sense_key == SCSI_SENKEY_NOT_READY) {
+
+ if (asc_dvc->start_motor & tid_bits) {
+ if (PollScsiStartUnit(asc_dvc, scsiq) == 1) {
+ retry = 0;
+ continue;
+ } else {
+ asc_dvc->start_motor &= ~tid_bits;
+ break;
+ }
+ } else {
+ DvcSleepMilliSecond(100);
+ }
+ } else if (sen->sense_key == SCSI_SENKEY_ATTENSION) {
+ DvcSleepMilliSecond(100);
+ } else {
+ break;
+ }
+ } else {
+ break;
+ }
+ } else if (scsiq->r3.done_stat == QD_ABORTED_BY_HOST) {
+ break;
+ } else {
+ break;
+ }
+ }
+ return (0);
+}
+
+#if CC_INIT_INQ_DISPLAY
+
+#endif
+
+int
+AscPollQDone(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ int timeout_sec
+)
+{
+ int loop, loop_end;
+ int sta;
+ PortAddr iop_base;
+
+ iop_base = asc_dvc->iop_base;
+ loop = 0;
+ loop_end = timeout_sec * 100;
+ sta = 1;
+
+ while (TRUE) {
+ ASC_DBG4(3,
+ "AscPollQDone: loop %d, err_code %x, done_stat %x, scsi_stat %x\n",
+ loop, asc_dvc->err_code, scsiq->r3.done_stat, scsiq->r3.scsi_stat);
+ if (asc_dvc->err_code != 0) {
+ scsiq->r3.done_stat = QD_WITH_ERROR;
+ sta = ERR;
+ break;
+ }
+ if (scsiq->r3.done_stat != QD_IN_PROGRESS) {
+ if ((scsiq->r3.done_stat == QD_WITH_ERROR) &&
+ (scsiq->r3.scsi_stat == SS_TARGET_BUSY)) {
+ sta = 0x80;
+ break;
+ }
+ break;
+ }
+ DvcSleepMilliSecond(10);
+ if (loop++ > loop_end) {
+ sta = 0;
+ break;
+ }
+ if (AscIsChipHalted(iop_base)) {
+ AscISR(asc_dvc);
+ loop = 0;
+ } else {
+ ASC_DBG(3, "AscPollQDone: AscIsIntPending()\n");
+ if (AscIsIntPending(iop_base)) {
+ ASC_DBG(3, "AscPollQDone: AscISR()\n");
+ AscISR(asc_dvc);
+ }
+ }
+ }
+ ASC_DBG1(3, "AscPollQDone: sta %x\n", sta);
+ return (sta);
+}
+
+uchar
+AscReadLramByte(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ uchar byte_data;
+ ushort word_data;
+
+ if (isodd_word(addr)) {
+ AscSetChipLramAddr(iop_base, addr - 1);
+ word_data = AscGetChipLramData(iop_base);
+
+#if CC_LITTLE_ENDIAN_HOST
+ byte_data = (uchar) ((word_data >> 8) & 0xFF);
+#else
+ byte_data = (uchar) (word_data & 0xFF);
+#endif
+
+ } else {
+ AscSetChipLramAddr(iop_base, addr);
+ word_data = AscGetChipLramData(iop_base);
+
+#if CC_LITTLE_ENDIAN_HOST
+ byte_data = (uchar) (word_data & 0xFF);
+#else
+ byte_data = (uchar) ((word_data >> 8) & 0xFF);
+#endif
+
+ }
+ return (byte_data);
+}
+
+ushort
+AscReadLramWord(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ ushort word_data;
+
+ AscSetChipLramAddr(iop_base, addr);
+ word_data = AscGetChipLramData(iop_base);
+ return (word_data);
+}
+
+ulong
+AscReadLramDWord(
+ PortAddr iop_base,
+ ushort addr
+)
+{
+ ushort val_low, val_high;
+ ulong dword_data;
+
+ AscSetChipLramAddr(iop_base, addr);
+
+#if CC_LITTLE_ENDIAN_HOST
+ val_low = AscGetChipLramData(iop_base);
+
+ val_high = AscGetChipLramData(iop_base);
+#else
+ val_high = AscGetChipLramData(iop_base);
+ val_low = AscGetChipLramData(iop_base);
+#endif
+
+ dword_data = ((ulong) val_high << 16) | (ulong) val_low;
+ return (dword_data);
+}
+
+void
+AscWriteLramWord(
+ PortAddr iop_base,
+ ushort addr,
+ ushort word_val
+)
+{
+ AscSetChipLramAddr(iop_base, addr);
+ AscPutChipLramData(iop_base, word_val);
+ return;
+}
+
+void
+AscWriteLramDWord(
+ PortAddr iop_base,
+ ushort addr,
+ ulong dword_val
+)
+{
+ ushort word_val;
+
+ AscSetChipLramAddr(iop_base, addr);
+
+#if CC_LITTLE_ENDIAN_HOST
+ word_val = (ushort) dword_val;
+ AscPutChipLramData(iop_base, word_val);
+ word_val = (ushort) (dword_val >> 16);
+ AscPutChipLramData(iop_base, word_val);
+#else
+ word_val = (ushort) (dword_val >> 16);
+ AscPutChipLramData(iop_base, word_val);
+ word_val = (ushort) dword_val;
+ AscPutChipLramData(iop_base, word_val);
+#endif
+ return;
+}
+
+void
+AscWriteLramByte(
+ PortAddr iop_base,
+ ushort addr,
+ uchar byte_val
+)
+{
+ ushort word_data;
+
+ if (isodd_word(addr)) {
+ addr--;
+ word_data = AscReadLramWord(iop_base, addr);
+ word_data &= 0x00FF;
+ word_data |= (((ushort) byte_val << 8) & 0xFF00);
+ } else {
+ word_data = AscReadLramWord(iop_base, addr);
+ word_data &= 0xFF00;
+ word_data |= ((ushort) byte_val & 0x00FF);
+ }
+ AscWriteLramWord(iop_base, addr, word_data);
+ return;
+}
+
+int
+AscVerWriteLramWord(
+ PortAddr iop_base,
+ ushort addr,
+ ushort word_val
+)
+{
+ int sta;
+
+ sta = 0;
+ AscSetChipLramAddr(iop_base, addr);
+ AscPutChipLramData(iop_base, word_val);
+ AscSetChipLramAddr(iop_base, addr);
+ if (word_val != AscGetChipLramData(iop_base)) {
+ sta = ERR;
+ }
+ return (sta);
+}
+
+void
+AscMemWordCopyToLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort dosfar * s_buffer,
+ int words
+)
+{
+ AscSetChipLramAddr(iop_base, s_addr);
+ DvcOutPortWords(iop_base + IOP_RAM_DATA, s_buffer, words);
+ return;
+}
+
+void
+AscMemDWordCopyToLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ulong dosfar * s_buffer,
+ int dwords
+)
+{
+ AscSetChipLramAddr(iop_base, s_addr);
+ DvcOutPortDWords(iop_base + IOP_RAM_DATA, s_buffer, dwords);
+ return;
+}
+
+void
+AscMemWordCopyFromLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort dosfar * d_buffer,
+ int words
+)
+{
+ AscSetChipLramAddr(iop_base, s_addr);
+ DvcInPortWords(iop_base + IOP_RAM_DATA, d_buffer, words);
+ return;
+}
+
+ulong
+AscMemSumLramWord(
+ PortAddr iop_base,
+ ushort s_addr,
+ rint words
+)
+{
+ ulong sum;
+ int i;
+
+ sum = 0L;
+ for (i = 0; i < words; i++, s_addr += 2) {
+ sum += AscReadLramWord(iop_base, s_addr);
+ }
+ return (sum);
+}
+
+void
+AscMemWordSetLram(
+ PortAddr iop_base,
+ ushort s_addr,
+ ushort set_wval,
+ rint words
+)
+{
+ rint i;
+
+ AscSetChipLramAddr(iop_base, s_addr);
+ for (i = 0; i < words; i++) {
+ AscPutChipLramData(iop_base, set_wval);
+ }
+ return;
+}
+
+int
+AscScsiInquiry(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ uchar dosfar * buf, int buf_len
+)
+{
+ if (AscScsiSetupCmdQ(asc_dvc, scsiq, buf,
+ (ulong) buf_len) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ scsiq->cdb[0] = (uchar) SCSICMD_Inquiry;
+ scsiq->cdb[1] = scsiq->r1.target_lun << 5;
+ scsiq->cdb[2] = 0;
+ scsiq->cdb[3] = 0;
+ scsiq->cdb[4] = buf_len;
+ scsiq->cdb[5] = 0;
+ scsiq->r2.cdb_len = 6;
+ return (0);
+}
+
+int
+AscScsiReadCapacity(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ uchar dosfar * info
+)
+{
+ if (AscScsiSetupCmdQ(asc_dvc, scsiq, info, 8L) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ scsiq->cdb[0] = (uchar) SCSICMD_ReadCapacity;
+ scsiq->cdb[1] = scsiq->r1.target_lun << 5;
+ scsiq->cdb[2] = 0;
+ scsiq->cdb[3] = 0;
+ scsiq->cdb[4] = 0;
+ scsiq->cdb[5] = 0;
+ scsiq->cdb[6] = 0;
+ scsiq->cdb[7] = 0;
+ scsiq->cdb[8] = 0;
+ scsiq->cdb[9] = 0;
+ scsiq->r2.cdb_len = 10;
+ return (0);
+}
+
+int
+AscScsiTestUnitReady(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq
+)
+{
+ if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR,
+ (ulong) 0L) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA;
+ scsiq->cdb[0] = (uchar) SCSICMD_TestUnitReady;
+ scsiq->cdb[1] = scsiq->r1.target_lun << 5;
+ scsiq->cdb[2] = 0;
+ scsiq->cdb[3] = 0;
+ scsiq->cdb[4] = 0;
+ scsiq->cdb[5] = 0;
+ scsiq->r2.cdb_len = 6;
+ return (0);
+}
+
+int
+AscScsiStartStopUnit(
+ ASC_DVC_VAR asc_ptr_type * asc_dvc,
+ ASC_SCSI_REQ_Q dosfar * scsiq,
+ uchar op_mode
+)
+{
+ if (AscScsiSetupCmdQ(asc_dvc, scsiq, FNULLPTR, (ulong) 0L) == ERR) {
+ return (scsiq->r3.done_stat = QD_WITH_ERROR);
+ }
+ scsiq->r1.cntl = (uchar) ASC_SCSIDIR_NODATA;
+ scsiq->cdb[0] = (uchar) SCSICMD_StartStopUnit;
+ scsiq->cdb[1] = scsiq->r1.target_lun << 5;
+ scsiq->cdb[2] = 0;
+ scsiq->cdb[3] = 0;
+ scsiq->cdb[4] = op_mode;
+
+ scsiq->cdb[5] = 0;
+ scsiq->r2.cdb_len = 6;
+ return (0);
+}
diff --git a/i386/i386at/gpl/linux/scsi/advansys.h b/i386/i386at/gpl/linux/scsi/advansys.h
new file mode 100644
index 00000000..255279ca
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/advansys.h
@@ -0,0 +1,131 @@
+/* $Id: advansys.h,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $ */
+/*
+ * advansys.h - Linux Host Driver for AdvanSys SCSI Adapters
+ *
+ * Copyright (c) 1995-1996 Advanced System Products, Inc.
+ *
+ * This driver may be modified and freely distributed provided that
+ * the above copyright message and this comment are included in the
+ * distribution. The latest version of this driver is available at
+ * the AdvanSys FTP and BBS sites listed below.
+ *
+ * Please send questions, comments, and bug reports to:
+ * bobf@advansys.com (Bob Frey)
+ */
+
+#ifndef _ADVANSYS_H
+#define _ADVANSYS_H
+
+/* The driver can be used in Linux 1.2.X or 1.3.X. */
+#if !defined(LINUX_1_2) && !defined(LINUX_1_3)
+#ifndef LINUX_VERSION_CODE
+#include <linux/version.h>
+#endif /* LINUX_VERSION_CODE */
+#if LINUX_VERSION_CODE > 65536 + 3 * 256
+#define LINUX_1_3
+#else /* LINUX_VERSION_CODE */
+#define LINUX_1_2
+#endif /* LINUX_VERSION_CODE */
+#endif /* !defined(LINUX_1_2) && !defined(LINUX_1_3) */
+
+/*
+ * Scsi_Host_Template function prototypes.
+ */
+int advansys_detect(Scsi_Host_Template *);
+int advansys_release(struct Scsi_Host *);
+const char *advansys_info(struct Scsi_Host *);
+int advansys_command(Scsi_Cmnd *);
+int advansys_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int advansys_abort(Scsi_Cmnd *);
+int advansys_reset(Scsi_Cmnd *);
+#ifdef LINUX_1_2
+int advansys_biosparam(Disk *, int, int[]);
+#else /* LINUX_1_3 */
+int advansys_biosparam(Disk *, kdev_t, int[]);
+extern struct proc_dir_entry proc_scsi_advansys;
+int advansys_proc_info(char *, char **, off_t, int, int, int);
+#endif /* LINUX_1_3 */
+
+/* init/main.c setup function */
+void advansys_setup(char *, int *);
+
+/*
+ * AdvanSys Host Driver Scsi_Host_Template (struct SHT) from hosts.h.
+ */
+#ifdef LINUX_1_2
+#define ADVANSYS { \
+ NULL, /* struct SHT *next */ \
+ NULL, /* int *usage_count */ \
+ "advansys", /* char *name */ \
+ advansys_detect, /* int (*detect)(struct SHT *) */ \
+ advansys_release, /* int (*release)(struct Scsi_Host *) */ \
+ advansys_info, /* const char *(*info)(struct Scsi_Host *) */ \
+ advansys_command, /* int (*command)(Scsi_Cmnd *) */ \
+ advansys_queuecommand, \
+ /* int (*queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ \
+ advansys_abort, /* int (*abort)(Scsi_Cmnd *) */ \
+ advansys_reset, /* int (*reset)(Scsi_Cmnd *) */ \
+ NULL, /* int (*slave_attach)(int, int) */ \
+ advansys_biosparam, /* int (* bios_param)(Disk *, int, int []) */ \
+ /* \
+ * The following fields are set per adapter in advansys_detect(). \
+ */ \
+ 0, /* int can_queue */ \
+ 0, /* int this_id */ \
+ 0, /* short unsigned int sg_tablesize */ \
+ 0, /* short cmd_per_lun */ \
+ 0, /* unsigned char present */ \
+ /* \
+ * Because the driver may control an ISA adapter 'unchecked_isa_dma' \
+ * must be set. The flag will be cleared in advansys_detect for non-ISA \
+ * adapters. Refer to the comment in scsi_module.c for more information. \
+ */ \
+ 1, /* unsigned unchecked_isa_dma:1 */ \
+ /* \
+ * All adapters controlled by this driver are capable of large \
+ * scatter-gather lists. This apparently obviates any performance
+ * gain provided by setting 'use_clustering'. \
+ */ \
+ DISABLE_CLUSTERING, /* unsigned use_clustering:1 */ \
+}
+#else /* LINUX_1_3 */
+#define ADVANSYS { \
+ NULL, /* struct SHT *next */ \
+ NULL, /* long *usage_count */ \
+ &proc_scsi_advansys, /* struct proc_dir_entry *proc_dir */ \
+ advansys_proc_info, \
+ /* int (*proc_info)(char *, char **, off_t, int, int, int) */ \
+ "advansys", /* const char *name */ \
+ advansys_detect, /* int (*detect)(struct SHT *) */ \
+ advansys_release, /* int (*release)(struct Scsi_Host *) */ \
+ advansys_info, /* const char *(*info)(struct Scsi_Host *) */ \
+ advansys_command, /* int (*command)(Scsi_Cmnd *) */ \
+ advansys_queuecommand, \
+ /* int (*queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *)) */ \
+ advansys_abort, /* int (*abort)(Scsi_Cmnd *) */ \
+ advansys_reset, /* int (*reset)(Scsi_Cmnd *) */ \
+ NULL, /* int (*slave_attach)(int, int) */ \
+ advansys_biosparam, /* int (* bios_param)(Disk *, kdev_t, int []) */ \
+ /* \
+ * The following fields are set per adapter in advansys_detect(). \
+ */ \
+ 0, /* int can_queue */ \
+ 0, /* int this_id */ \
+ 0, /* short unsigned int sg_tablesize */ \
+ 0, /* short cmd_per_lun */ \
+ 0, /* unsigned char present */ \
+ /* \
+ * Because the driver may control an ISA adapter 'unchecked_isa_dma' \
+ * must be set. The flag will be cleared in advansys_detect for non-ISA \
+ * adapters. Refer to the comment in scsi_module.c for more information. \
+ */ \
+ 1, /* unsigned unchecked_isa_dma:1 */ \
+ /* \
+ * All adapters controlled by this driver are capable of large \
+ * scatter-gather lists. This apparently obviates any performance
+ * gain provided by setting 'use_clustering'. \
+ */ \
+ DISABLE_CLUSTERING, /* unsigned use_clustering:1 */ \
+}
+#endif /* LINUX_1_3 */
+#endif /* _ADVANSYS_H */
diff --git a/i386/i386at/gpl/linux/scsi/aha152x.c b/i386/i386at/gpl/linux/scsi/aha152x.c
new file mode 100644
index 00000000..67dcd102
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aha152x.c
@@ -0,0 +1,2985 @@
+/* aha152x.c -- Adaptec AHA-152x driver
+ * Author: Juergen E. Fischer, fischer@et-inf.fho-emden.de
+ * Copyright 1993, 1994, 1995 Juergen E. Fischer
+ *
+ *
+ * This driver is based on
+ * fdomain.c -- Future Domain TMC-16x0 driver
+ * which is
+ * Copyright 1992, 1993 Rickard E. Faith (faith@cs.unc.edu)
+ *
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ *
+ * $Id: aha152x.c,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ *
+ * $Log: aha152x.c,v $
+ * Revision 1.1.1.1 1996/10/30 01:40:01 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:17 goel
+ * Linux driver merge.
+ *
+ * Revision 1.14 1996/01/17 15:11:20 fischer
+ * - fixed lockup in MESSAGE IN phase after reconnection
+ *
+ * Revision 1.13 1996/01/09 02:15:53 fischer
+ * - some cleanups
+ * - moved request_irq behind controller initialization
+ * (to avoid spurious interrupts)
+ *
+ * Revision 1.12 1995/12/16 12:26:07 fischer
+ * - barrier()'s added
+ * - configurable RESET delay added
+ *
+ * Revision 1.11 1995/12/06 21:18:35 fischer
+ * - some minor updates
+ *
+ * Revision 1.10 1995/07/22 19:18:45 fischer
+ * - support for 2 controllers
+ * - started synchronous data transfers (not working yet)
+ *
+ * Revision 1.9 1995/03/18 09:20:24 root
+ * - patches for PCMCIA and modules
+ *
+ * Revision 1.8 1995/01/21 22:07:19 root
+ * - snarf_region => request_region
+ * - aha152x_intr interface change
+ *
+ * Revision 1.7 1995/01/02 23:19:36 root
+ * - updated COMMAND_SIZE to cmd_len
+ * - changed sti() to restore_flags()
+ * - fixed some #ifdef which generated warnings
+ *
+ * Revision 1.6 1994/11/24 20:35:27 root
+ * - problem with odd number of bytes in fifo fixed
+ *
+ * Revision 1.5 1994/10/30 14:39:56 root
+ * - abort code fixed
+ * - debugging improved
+ *
+ * Revision 1.4 1994/09/12 11:33:01 root
+ * - irqaction to request_irq
+ * - abortion updated
+ *
+ * Revision 1.3 1994/08/04 13:53:05 root
+ * - updates for mid-level-driver changes
+ * - accept unexpected BUSFREE phase as error condition
+ * - parity check now configurable
+ *
+ * Revision 1.2 1994/07/03 12:56:36 root
+ * - cleaned up debugging code
+ * - more tweaking on reset delays
+ * - updated abort/reset code (pretty untested...)
+ *
+ * Revision 1.1 1994/05/28 21:18:49 root
+ * - update for mid-level interface change (abort-reset)
+ * - delays after resets adjusted for some slow devices
+ *
+ * Revision 1.0 1994/03/25 12:52:00 root
+ * - Fixed "more data than expected" problem
+ * - added new BIOS signatures
+ *
+ * Revision 0.102 1994/01/31 20:44:12 root
+ * - minor changes in insw/outsw handling
+ *
+ * Revision 0.101 1993/12/13 01:16:27 root
+ * - fixed STATUS phase (non-GOOD stati were dropped sometimes;
+ * fixes problems with CD-ROM sector size detection & media change)
+ *
+ * Revision 0.100 1993/12/10 16:58:47 root
+ * - fix for unsuccessful selections in case of non-continuous id assignments
+ * on the scsi bus.
+ *
+ * Revision 0.99 1993/10/24 16:19:59 root
+ * - fixed DATA IN (rare read errors gone)
+ *
+ * Revision 0.98 1993/10/17 12:54:44 root
+ * - fixed some recent fixes (shame on me)
+ * - moved initialization of scratch area to aha152x_queue
+ *
+ * Revision 0.97 1993/10/09 18:53:53 root
+ * - DATA IN fixed. Rarely left data in the fifo.
+ *
+ * Revision 0.96 1993/10/03 00:53:59 root
+ * - minor changes on DATA IN
+ *
+ * Revision 0.95 1993/09/24 10:36:01 root
+ * - change handling of MSGI after reselection
+ * - fixed sti/cli
+ * - minor changes
+ *
+ * Revision 0.94 1993/09/18 14:08:22 root
+ * - fixed bug in multiple outstanding command code
+ * - changed detection
+ * - support for kernel command line configuration
+ * - reset corrected
+ * - changed message handling
+ *
+ * Revision 0.93 1993/09/15 20:41:19 root
+ * - fixed bugs with multiple outstanding commands
+ *
+ * Revision 0.92 1993/09/13 02:46:33 root
+ * - multiple outstanding commands work (no problems with IBM drive)
+ *
+ * Revision 0.91 1993/09/12 20:51:46 root
+ * added multiple outstanding commands
+ * (some problem with this $%&? IBM device remain)
+ *
+ * Revision 0.9 1993/09/12 11:11:22 root
+ * - corrected auto-configuration
+ * - changed the auto-configuration (added some '#define's)
+ * - added support for dis-/reconnection
+ *
+ * Revision 0.8 1993/09/06 23:09:39 root
+ * - added support for the drive activity light
+ * - minor changes
+ *
+ * Revision 0.7 1993/09/05 14:30:15 root
+ * - improved phase detection
+ * - now using the new snarf_region code of 0.99pl13
+ *
+ * Revision 0.6 1993/09/02 11:01:38 root
+ * first public release; added some signatures and biosparam()
+ *
+ * Revision 0.5 1993/08/30 10:23:30 root
+ * fixed timing problems with my IBM drive
+ *
+ * Revision 0.4 1993/08/29 14:06:52 root
+ * fixed some problems with timeouts due incomplete commands
+ *
+ * Revision 0.3 1993/08/28 15:55:03 root
+ * writing data works too. mounted and worked on a dos partition
+ *
+ * Revision 0.2 1993/08/27 22:42:07 root
+ * reading data works. Mounted a msdos partition.
+ *
+ * Revision 0.1 1993/08/25 13:38:30 root
+ * first "damn thing doesn't work" version
+ *
+ * Revision 0.0 1993/08/14 19:54:25 root
+ * empty function bodies; detect() works.
+ *
+ *
+ **************************************************************************
+
+
+
+ DESCRIPTION:
+
+ This is the Linux low-level SCSI driver for Adaptec AHA-1520/1522
+ SCSI host adapters.
+
+
+ PER-DEFINE CONFIGURABLE OPTIONS:
+
+ AUTOCONF:
+ use configuration the controller reports (only 152x)
+
+ SKIP_BIOSTEST:
+ Don't test for BIOS signature (AHA-1510 or disabled BIOS)
+
+ SETUP0 { IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY }:
+ override for the first controller
+
+ SETUP1 { IOPORT, IRQ, SCSI_ID, RECONNECT, PARITY, SYNCHRONOUS, DELAY }:
+ override for the second controller
+
+
+ LILO COMMAND LINE OPTIONS:
+
+ aha152x=<IOPORT>[,<IRQ>[,<SCSI-ID>[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>]]]]]]
+
+ The normal configuration can be overridden by specifying a command line.
+ When you do this, the BIOS test is skipped. Entered values have to be
+ valid (known). Don't use values that aren't supported under normal operation.
+ If you think that you need other values: contact me. For two controllers
+ use the aha152x statement twice.
+
+
+ REFERENCES USED:
+
+ "AIC-6260 SCSI Chip Specification", Adaptec Corporation.
+
+ "SCSI COMPUTER SYSTEM INTERFACE - 2 (SCSI-2)", X3T9.2/86-109 rev. 10h
+
+ "Writing a SCSI device driver for Linux", Rik Faith (faith@cs.unc.edu)
+
+ "Kernel Hacker's Guide", Michael K. Johnson (johnsonm@sunsite.unc.edu)
+
+ "Adaptec 1520/1522 User's Guide", Adaptec Corporation.
+
+ Michael K. Johnson (johnsonm@sunsite.unc.edu)
+
+ Drew Eckhardt (drew@cs.colorado.edu)
+
+ Eric Youngdale (ericy@cais.com)
+
+ special thanks to Eric Youngdale for the free(!) supplying the
+ documentation on the chip.
+
+ **************************************************************************/
+
+#ifdef PCMCIA
+#define MODULE
+#endif
+
+#include <linux/module.h>
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include "constants.h"
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/wait.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+
+#include "aha152x.h"
+#include <linux/stat.h>
+
+struct proc_dir_entry proc_scsi_aha152x = {
+ PROC_SCSI_AHA152X, 7, "aha152x",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* DEFINES */
+
+#ifdef MACH
+#define AUTOCONF
+#endif
+
+/* For PCMCIA cards, always use AUTOCONF */
+#if defined(PCMCIA) || defined(MODULE)
+#if !defined(AUTOCONF)
+#define AUTOCONF
+#endif
+#endif
+
+#if !defined(AUTOCONF) && !defined(SETUP0)
+#error define AUTOCONF or SETUP0
+#endif
+
+#if defined(DEBUG_AHA152X)
+
+#undef SKIP_PORTS /* don't display ports */
+
+#undef DEBUG_QUEUE /* debug queue() */
+#undef DEBUG_RESET /* debug reset() */
+#undef DEBUG_INTR /* debug intr() */
+#undef DEBUG_SELECTION /* debug selection part in intr() */
+#undef DEBUG_MSGO /* debug message out phase in intr() */
+#undef DEBUG_MSGI /* debug message in phase in intr() */
+#undef DEBUG_STATUS /* debug status phase in intr() */
+#undef DEBUG_CMD /* debug command phase in intr() */
+#undef DEBUG_DATAI /* debug data in phase in intr() */
+#undef DEBUG_DATAO /* debug data out phase in intr() */
+#undef DEBUG_ABORT /* debug abort() */
+#undef DEBUG_DONE /* debug done() */
+#undef DEBUG_BIOSPARAM /* debug biosparam() */
+
+#undef DEBUG_RACE /* debug race conditions */
+#undef DEBUG_PHASES /* debug phases (useful to trace) */
+#undef DEBUG_QUEUES /* debug reselection */
+
+/* recently used for debugging */
+#if 0
+#endif
+
+#define DEBUG_SELECTION
+#define DEBUG_PHASES
+#define DEBUG_RESET
+#define DEBUG_ABORT
+
+#define DEBUG_DEFAULT (debug_reset|debug_abort)
+
+#endif
+
+/* END OF DEFINES */
+
+extern long loops_per_sec;
+
+#define DELAY_DEFAULT 100
+
+/* some additional "phases" for getphase() */
+#define P_BUSFREE 1
+#define P_PARITY 2
+
+/* possible irq range */
+#define IRQ_MIN 9
+#define IRQ_MAX 12
+#define IRQS IRQ_MAX-IRQ_MIN+1
+
+enum {
+ not_issued = 0x0001,
+ in_selection = 0x0002,
+ disconnected = 0x0004,
+ aborted = 0x0008,
+ sent_ident = 0x0010,
+ in_other = 0x0020,
+ in_sync = 0x0040,
+ sync_ok = 0x0080,
+};
+
+/* set by aha152x_setup according to the command line */
+static int setup_count=0;
+static struct aha152x_setup {
+ int io_port;
+ int irq;
+ int scsiid;
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+#ifdef DEBUG_AHA152X
+ int debug;
+#endif
+ char *conf;
+} setup[2];
+
+static struct Scsi_Host *aha152x_host[IRQS];
+
+#define HOSTDATA(shpnt) ((struct aha152x_hostdata *) &shpnt->hostdata)
+#define CURRENT_SC (HOSTDATA(shpnt)->current_SC)
+#define ISSUE_SC (HOSTDATA(shpnt)->issue_SC)
+#define DISCONNECTED_SC (HOSTDATA(shpnt)->disconnected_SC)
+#define DELAY (HOSTDATA(shpnt)->delay)
+#define SYNCRATE (HOSTDATA(shpnt)->syncrate[CURRENT_SC->target])
+#define MSG(i) (HOSTDATA(shpnt)->message[i])
+#define MSGLEN (HOSTDATA(shpnt)->message_len)
+#define ADDMSG(x) (MSG(MSGLEN++)=x)
+
+struct aha152x_hostdata {
+ Scsi_Cmnd *issue_SC;
+ Scsi_Cmnd *current_SC;
+ Scsi_Cmnd *disconnected_SC;
+ int aborting;
+ int abortion_complete;
+ int abort_result;
+ int commands;
+
+ int reconnect;
+ int parity;
+ int synchronous;
+ int delay;
+
+ unsigned char syncrate[8];
+
+ unsigned char message[256];
+ int message_len;
+
+#ifdef DEBUG_AHA152X
+ int debug;
+#endif
+};
+
+void aha152x_intr(int irq, struct pt_regs *);
+void aha152x_done(struct Scsi_Host *shpnt, int error);
+void aha152x_setup(char *str, int *ints);
+int aha152x_checksetup(struct aha152x_setup *setup);
+
+static void aha152x_reset_ports(struct Scsi_Host *shpnt);
+static void aha152x_panic(struct Scsi_Host *shpnt, char *msg);
+
+static void disp_ports(struct Scsi_Host *shpnt);
+static void show_command(Scsi_Cmnd *ptr);
+static void show_queues(struct Scsi_Host *shpnt);
+static void disp_enintr(struct Scsi_Host *shpnt);
+
+#if defined(DEBUG_RACE)
+static void enter_driver(const char *);
+static void leave_driver(const char *);
+#endif
+
+/* possible i/o addresses for the AIC-6260 */
+static unsigned short ports[] =
+{
+ 0x340, /* default first */
+ 0x140
+};
+#define PORT_COUNT (sizeof(ports) / sizeof(unsigned short))
+
+#if !defined(SKIP_BIOSTEST)
+/* possible locations for the Adaptec BIOS */
+static void *addresses[] =
+{
+ (void *) 0xdc000, /* default first */
+ (void *) 0xc8000,
+ (void *) 0xcc000,
+ (void *) 0xd0000,
+ (void *) 0xd4000,
+ (void *) 0xd8000,
+ (void *) 0xe0000,
+ (void *) 0xeb800, /* VTech Platinum SMP */
+ (void *) 0xf0000,
+};
+#define ADDRESS_COUNT (sizeof(addresses) / sizeof(void *))
+
+/* signatures for various AIC-6[23]60 based controllers.
+ The point in detecting signatures is to avoid useless
+ and maybe harmful probes on ports. I'm not sure that
+ all listed boards pass auto-configuration. For those
+ which fail the BIOS signature is obsolete, because
+ user intervention to supply the configuration is
+ needed anyway. */
+static struct signature {
+ char *signature;
+ int sig_offset;
+ int sig_length;
+} signatures[] =
+{
+ { "Adaptec AHA-1520 BIOS", 0x102e, 21 }, /* Adaptec 152x */
+ { "Adaptec ASW-B626 BIOS", 0x1029, 21 }, /* on-board controller */
+ { "Adaptec BIOS: ASW-B626", 0x0f, 22 }, /* on-board controller */
+ { "Adaptec ASW-B626 S2", 0x2e6c, 19 }, /* on-board controller */
+ { "Adaptec BIOS:AIC-6360", 0xc, 21 }, /* on-board controller */
+ { "ScsiPro SP-360 BIOS", 0x2873, 19 }, /* ScsiPro-Controller */
+ { "GA-400 LOCAL BUS SCSI BIOS", 0x102e, 26 }, /* Gigabyte Local-Bus-SCSI */
+ { "Adaptec BIOS:AVA-282X", 0xc, 21 }, /* Adaptec 282x */
+ { "Adaptec IBM Dock II SCSI", 0x2edd, 24 }, /* IBM Thinkpad Dock II */
+ { "Adaptec BIOS:AHA-1532P", 0x1c, 22 }, /* IBM Thinkpad Dock II SCSI */
+};
+#define SIGNATURE_COUNT (sizeof(signatures) / sizeof(struct signature))
+#endif
+
+
+static void do_pause(unsigned amount) /* Pause for amount*10 milliseconds */
+{
+ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
+
+ while (jiffies < the_time)
+ barrier();
+}
+
+/*
+ * queue services:
+ */
+static inline void append_SC(Scsi_Cmnd **SC, Scsi_Cmnd *new_SC)
+{
+ Scsi_Cmnd *end;
+
+ new_SC->host_scribble = (unsigned char *) NULL;
+ if(!*SC)
+ *SC=new_SC;
+ else
+ {
+ for(end=*SC; end->host_scribble; end = (Scsi_Cmnd *) end->host_scribble)
+ ;
+ end->host_scribble = (unsigned char *) new_SC;
+ }
+}
+
+static inline Scsi_Cmnd *remove_first_SC(Scsi_Cmnd **SC)
+{
+ Scsi_Cmnd *ptr;
+
+ ptr=*SC;
+ if(ptr)
+ *SC= (Scsi_Cmnd *) (*SC)->host_scribble;
+ return ptr;
+}
+
+static inline Scsi_Cmnd *remove_SC(Scsi_Cmnd **SC, int target, int lun)
+{
+ Scsi_Cmnd *ptr, *prev;
+
+ for(ptr=*SC, prev=NULL;
+ ptr && ((ptr->target!=target) || (ptr->lun!=lun));
+ prev = ptr, ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr)
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ *SC= (Scsi_Cmnd *) ptr->host_scribble;
+ return ptr;
+}
+
+/*
+ * read inbound byte and wait for ACK to get low
+ */
+static void make_acklow(struct Scsi_Host *shpnt)
+{
+ SETPORT(SXFRCTL0, CH1|SPIOEN);
+ GETPORT(SCSIDAT);
+ SETPORT(SXFRCTL0, CH1);
+
+ while(TESTHI(SCSISIG, ACKI))
+ barrier();
+}
+
+/*
+ * detect current phase more reliable:
+ * phase is valid, when the target asserts REQ after we've deasserted ACK.
+ *
+ * return value is a valid phase or an error code.
+ *
+ * errorcodes:
+ * P_BUSFREE BUS FREE phase detected
+ * P_PARITY parity error in DATA phase
+ */
+static int getphase(struct Scsi_Host *shpnt)
+{
+ int phase, sstat1;
+
+ while(1)
+ {
+ do
+ {
+ while(!((sstat1 = GETPORT(SSTAT1)) & (BUSFREE|SCSIRSTI|REQINIT)))
+ barrier();
+ if(sstat1 & BUSFREE)
+ return P_BUSFREE;
+ if(sstat1 & SCSIRSTI)
+ {
+ printk("aha152x: RESET IN\n");
+ SETPORT(SSTAT1, SCSIRSTI);
+ }
+ }
+ while(TESTHI(SCSISIG, ACKI) || TESTLO(SSTAT1, REQINIT));
+
+ SETPORT(SSTAT1, CLRSCSIPERR);
+
+ phase = GETPORT(SCSISIG) & P_MASK ;
+
+ if(TESTHI(SSTAT1, SCSIPERR))
+ {
+ if((phase & (CDO|MSGO))==0) /* DATA phase */
+ return P_PARITY;
+
+ make_acklow(shpnt);
+ }
+ else
+ return phase;
+ }
+}
+
+/* called from init/main.c */
+void aha152x_setup(char *str, int *ints)
+{
+ if(setup_count>2)
+ panic("aha152x: you can only configure up to two controllers\n");
+
+ setup[setup_count].conf = str;
+ setup[setup_count].io_port = ints[0] >= 1 ? ints[1] : 0x340;
+ setup[setup_count].irq = ints[0] >= 2 ? ints[2] : 11;
+ setup[setup_count].scsiid = ints[0] >= 3 ? ints[3] : 7;
+ setup[setup_count].reconnect = ints[0] >= 4 ? ints[4] : 1;
+ setup[setup_count].parity = ints[0] >= 5 ? ints[5] : 1;
+ setup[setup_count].synchronous = ints[0] >= 6 ? ints[6] : 0 /* FIXME: 1 */;
+ setup[setup_count].delay = ints[0] >= 7 ? ints[7] : DELAY_DEFAULT;
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = ints[0] >= 8 ? ints[8] : DEBUG_DEFAULT;
+ if(ints[0]>8)
+ {
+ printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>[,<DEBUG>]]]]]]]\n");
+#else
+ if(ints[0]>7)
+ {
+ printk("aha152x: usage: aha152x=<IOBASE>[,<IRQ>[,<SCSI ID>"
+ "[,<RECONNECT>[,<PARITY>[,<SYNCHRONOUS>[,<DELAY>]]]]]]\n");
+#endif
+ }
+ else
+ setup_count++;
+}
+
+/*
+ Test, if port_base is valid.
+ */
+static int aha152x_porttest(int io_port)
+{
+ int i;
+
+ if(check_region(io_port, IO_RANGE))
+ return 0;
+
+ SETPORT(io_port+O_DMACNTRL1, 0); /* reset stack pointer */
+ for(i=0; i<16; i++)
+ SETPORT(io_port+O_STACK, i);
+
+ SETPORT(io_port+O_DMACNTRL1, 0); /* reset stack pointer */
+ for(i=0; i<16 && GETPORT(io_port+O_STACK)==i; i++)
+ ;
+
+ return(i==16);
+}
+
+int aha152x_checksetup(struct aha152x_setup *setup)
+{
+ int i;
+
+#ifndef PCMCIA
+ for(i=0; i<PORT_COUNT && (setup->io_port != ports[i]); i++)
+ ;
+
+ if(i==PORT_COUNT)
+ return 0;
+#endif
+
+ if(!aha152x_porttest(setup->io_port))
+ return 0;
+
+ if((setup->irq < IRQ_MIN) && (setup->irq > IRQ_MAX))
+ return 0;
+
+ if((setup->scsiid < 0) || (setup->scsiid > 7))
+ return 0;
+
+ if((setup->reconnect < 0) || (setup->reconnect > 1))
+ return 0;
+
+ if((setup->parity < 0) || (setup->parity > 1))
+ return 0;
+
+ if((setup->synchronous < 0) || (setup->synchronous > 1))
+ return 0;
+
+ return 1;
+}
+
+
+int aha152x_detect(Scsi_Host_Template * tpnt)
+{
+ int i, j, ok;
+#if defined(AUTOCONF)
+ aha152x_config conf;
+#endif
+
+ tpnt->proc_dir = &proc_scsi_aha152x;
+
+ for(i=0; i<IRQS; i++)
+ aha152x_host[i] = (struct Scsi_Host *) NULL;
+
+ if(setup_count)
+ {
+ printk("aha152x: processing commandline: ");
+
+ for(i=0; i<setup_count; i++)
+ if(!aha152x_checksetup(&setup[i]))
+ {
+ printk("\naha152x: %s\n", setup[i].conf);
+ printk("aha152x: invalid line (controller=%d)\n", i+1);
+ }
+
+ printk("ok\n");
+ }
+
+#ifdef SETUP0
+ if(setup_count<2)
+ {
+ struct aha152x_setup override = SETUP0;
+
+ if(setup_count==0 || (override.io_port != setup[0].io_port))
+ if(!aha152x_checksetup(&override))
+ {
+ printk("\naha152x: SETUP0 (0x%x, %d, %d, %d, %d, %d, %d) invalid\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay);
+ }
+ else
+ setup[setup_count++] = override;
+ }
+#endif
+
+#ifdef SETUP1
+ if(setup_count<2)
+ {
+ struct aha152x_setup override = SETUP1;
+
+ if(setup_count==0 || (override.io_port != setup[0].io_port))
+ if(!aha152x_checksetup(&override))
+ {
+ printk("\naha152x: SETUP1 (0x%x, %d, %d, %d, %d, %d, %d) invalid\n",
+ override.io_port,
+ override.irq,
+ override.scsiid,
+ override.reconnect,
+ override.parity,
+ override.synchronous,
+ override.delay);
+ }
+ else
+ setup[setup_count++] = override;
+ }
+#endif
+
+#if defined(AUTOCONF)
+ if(setup_count<2)
+ {
+#if !defined(SKIP_BIOSTEST)
+ ok=0;
+ for(i=0; i < ADDRESS_COUNT && !ok; i++)
+ for(j=0; (j < SIGNATURE_COUNT) && !ok; j++)
+ ok=!memcmp((void *) addresses[i]+signatures[j].sig_offset,
+ (void *) signatures[j].signature,
+ (int) signatures[j].sig_length);
+
+ if(!ok && setup_count==0)
+ return 0;
+
+ printk("aha152x: BIOS test: passed, ");
+#else
+ printk("aha152x: ");
+#endif /* !SKIP_BIOSTEST */
+
+ for(i=0; i<PORT_COUNT && setup_count<2; i++)
+ {
+ if((setup_count==1) && (setup[0].io_port == ports[i]))
+ continue;
+
+ if(aha152x_porttest(ports[i]))
+ {
+ setup[setup_count].io_port = ports[i];
+
+ conf.cf_port =
+ (GETPORT(ports[i]+O_PORTA)<<8) + GETPORT(ports[i]+O_PORTB);
+
+ setup[setup_count].irq = IRQ_MIN + conf.cf_irq;
+ setup[setup_count].scsiid = conf.cf_id;
+ setup[setup_count].reconnect = conf.cf_tardisc;
+ setup[setup_count].parity = !conf.cf_parity;
+ setup[setup_count].synchronous = 0 /* FIXME: conf.cf_syncneg */;
+ setup[setup_count].delay = DELAY_DEFAULT;
+#ifdef DEBUG_AHA152X
+ setup[setup_count].debug = DEBUG_DEFAULT;
+#endif
+ setup_count++;
+ }
+ }
+
+ printk("auto configuration: ok, ");
+ }
+#endif
+
+ printk("detection complete\n");
+
+ for(i=0; i<setup_count; i++)
+ {
+ struct Scsi_Host *shpnt;
+
+ shpnt = aha152x_host[setup[i].irq-IRQ_MIN] =
+ scsi_register(tpnt, sizeof(struct aha152x_hostdata));
+
+ shpnt->io_port = setup[i].io_port;
+ shpnt->n_io_port = IO_RANGE;
+ shpnt->irq = setup[i].irq;
+
+ ISSUE_SC = (Scsi_Cmnd *) NULL;
+ CURRENT_SC = (Scsi_Cmnd *) NULL;
+ DISCONNECTED_SC = (Scsi_Cmnd *) NULL;
+
+ HOSTDATA(shpnt)->reconnect = setup[i].reconnect;
+ HOSTDATA(shpnt)->parity = setup[i].parity;
+ HOSTDATA(shpnt)->synchronous = setup[i].synchronous;
+ HOSTDATA(shpnt)->delay = setup[i].delay;
+#ifdef DEBUG_AHA152X
+ HOSTDATA(shpnt)->debug = setup[i].debug;
+#endif
+
+ HOSTDATA(shpnt)->aborting = 0;
+ HOSTDATA(shpnt)->abortion_complete = 0;
+ HOSTDATA(shpnt)->abort_result = 0;
+ HOSTDATA(shpnt)->commands = 0;
+
+ HOSTDATA(shpnt)->message_len = 0;
+
+ for(j=0; j<8; j++)
+ HOSTDATA(shpnt)->syncrate[j] = 0;
+
+ SETPORT(SCSIID, setup[i].scsiid << 4);
+ shpnt->this_id=setup[i].scsiid;
+
+ if(setup[i].reconnect)
+ shpnt->hostt->can_queue=AHA152X_MAXQUEUE;
+
+ /* RESET OUT */
+ SETBITS(SCSISEQ, SCSIRSTO);
+ do_pause(30);
+ CLRBITS(SCSISEQ, SCSIRSTO);
+ do_pause(setup[i].delay);
+
+ aha152x_reset_ports(shpnt);
+
+ printk("aha152x%d: vital data: PORTBASE=0x%03x, IRQ=%d, SCSI ID=%d,"
+ " reconnect=%s, parity=%s, synchronous=%s, delay=%d\n",
+ i,
+ shpnt->io_port,
+ shpnt->irq,
+ shpnt->this_id,
+ HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->parity ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled",
+ HOSTDATA(shpnt)->delay);
+
+ request_region(shpnt->io_port, IO_RANGE, "aha152x"); /* Register */
+
+ /* not expecting any interrupts */
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ ok = request_irq(setup[i].irq, aha152x_intr, SA_INTERRUPT, "aha152x");
+
+ if(ok<0)
+ {
+ if(ok == -EINVAL)
+ {
+ printk("aha152x%d: bad IRQ %d.\n", i, setup[i].irq);
+ printk(" Contact author.\n");
+ }
+ else
+ if(ok == -EBUSY)
+ printk("aha152x%d: IRQ %d already in use. Configure another.\n",
+ i, setup[i].irq);
+ else
+ {
+ printk("\naha152x%d: Unexpected error code on"
+ " requesting IRQ %d.\n", i, setup[i].irq);
+ printk(" Contact author.\n");
+ }
+ printk("aha152x: driver needs an IRQ.\n");
+ continue;
+ }
+ }
+
+ return (setup_count>0);
+}
+
+/*
+ * Queue a command and setup interrupts for a free bus.
+ */
+int aha152x_queue(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+
+#if defined(DEBUG_RACE)
+ enter_driver("queue");
+#else
+#if defined(DEBUG_QUEUE)
+ if(HOSTDATA(shpnt)->debug & debug_queue)
+ printk("aha152x: queue(), ");
+#endif
+#endif
+
+#if defined(DEBUG_QUEUE)
+ if(HOSTDATA(shpnt)->debug & debug_queue)
+ {
+ printk("SCpnt (target = %d lun = %d cmnd = ",
+ SCpnt->target, SCpnt->lun);
+ print_command(SCpnt->cmnd);
+ printk(", cmd_len=%d, pieces = %d size = %u), ",
+ SCpnt->cmd_len, SCpnt->use_sg, SCpnt->request_bufflen);
+ disp_ports(shpnt);
+ }
+#endif
+
+ SCpnt->scsi_done = done;
+
+ /* setup scratch area
+ SCp.ptr : buffer pointer
+ SCp.this_residual : buffer length
+ SCp.buffer : next buffer
+ SCp.buffers_residual : left buffers in list
+ SCp.phase : current state of the command */
+ SCpnt->SCp.phase = not_issued;
+ if (SCpnt->use_sg)
+ {
+ SCpnt->SCp.buffer =
+ (struct scatterlist *) SCpnt->request_buffer;
+ SCpnt->SCp.ptr = SCpnt->SCp.buffer->address;
+ SCpnt->SCp.this_residual = SCpnt->SCp.buffer->length;
+ SCpnt->SCp.buffers_residual = SCpnt->use_sg - 1;
+ }
+ else
+ {
+ SCpnt->SCp.ptr = (char *)SCpnt->request_buffer;
+ SCpnt->SCp.this_residual = SCpnt->request_bufflen;
+ SCpnt->SCp.buffer = NULL;
+ SCpnt->SCp.buffers_residual = 0;
+ }
+
+ SCpnt->SCp.Status = CHECK_CONDITION;
+ SCpnt->SCp.Message = 0;
+ SCpnt->SCp.have_data_in = 0;
+ SCpnt->SCp.sent_command = 0;
+
+ /* Turn led on, when this is the first command. */
+ save_flags(flags);
+ cli();
+ HOSTDATA(shpnt)->commands++;
+ if(HOSTDATA(shpnt)->commands==1)
+ SETPORT(PORTA, 1);
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i+ (%d), ", HOSTDATA(shpnt)->commands);
+#endif
+ append_SC(&ISSUE_SC, SCpnt);
+
+ /* Enable bus free interrupt, when we aren't currently on the bus */
+ if(!CURRENT_SC)
+ {
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ }
+ restore_flags(flags);
+
+#if defined(DEBUG_RACE)
+ leave_driver("queue");
+#endif
+
+ return 0;
+}
+
+/*
+ * We only support commands in interrupt-driven fashion
+ */
+int aha152x_command(Scsi_Cmnd *SCpnt)
+{
+ printk("aha152x: interrupt driven driver; use aha152x_queue()\n");
+ return -1;
+}
+
+/*
+ * Abort a queued command
+ * (commands that are on the bus can't be aborted easily)
+ */
+int aha152x_abort(Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+ Scsi_Cmnd *ptr, *prev;
+
+ save_flags(flags);
+ cli();
+
+#if defined(DEBUG_ABORT)
+ if(HOSTDATA(shpnt)->debug & debug_abort)
+ {
+ printk("aha152x: abort(), SCpnt=0x%08x, ", (unsigned int) SCpnt);
+ show_queues(shpnt);
+ }
+#endif
+
+ /* look for command in issue queue */
+ for(ptr=ISSUE_SC, prev=NULL;
+ ptr && ptr!=SCpnt;
+ prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr)
+ {
+ /* dequeue */
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ ISSUE_SC = (Scsi_Cmnd *) ptr->host_scribble;
+ restore_flags(flags);
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_ABORT << 16;
+ ptr->scsi_done(ptr);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ /* if the bus is busy or a command is currently processed,
+ we can't do anything more */
+ if (TESTLO(SSTAT1, BUSFREE) || (CURRENT_SC && CURRENT_SC!=SCpnt))
+ {
+ /* fail abortion, if bus is busy */
+
+ if(!CURRENT_SC)
+ printk("bus busy w/o current command, ");
+
+ restore_flags(flags);
+ return SCSI_ABORT_BUSY;
+ }
+
+ /* bus is free */
+
+ if(CURRENT_SC)
+ {
+ /* target entered bus free before COMMAND COMPLETE, nothing to abort */
+ restore_flags(flags);
+ CURRENT_SC->result = DID_ERROR << 16;
+ CURRENT_SC->scsi_done(CURRENT_SC);
+ CURRENT_SC = (Scsi_Cmnd *) NULL;
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ /* look for command in disconnected queue */
+ for(ptr=DISCONNECTED_SC, prev=NULL;
+ ptr && ptr!=SCpnt;
+ prev=ptr, ptr=(Scsi_Cmnd *) ptr->host_scribble)
+ ;
+
+ if(ptr)
+ if(!HOSTDATA(shpnt)->aborting)
+ {
+ /* dequeue */
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ /* set command current and initiate selection,
+ let the interrupt routine take care of the abortion */
+ CURRENT_SC = ptr;
+ ptr->SCp.phase = in_selection|aborted;
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);
+
+ ADDMSG(ABORT);
+
+ /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+
+ /* Enable SELECTION OUT sequence */
+ SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
+
+ SETBITS(DMACNTRL0, INTEN);
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_SUCCESS;
+ HOSTDATA(shpnt)->aborting++;
+ HOSTDATA(shpnt)->abortion_complete=0;
+
+ sti(); /* Hi Eric, guess what ;-) */
+
+ /* sleep until the abortion is complete */
+ while(!HOSTDATA(shpnt)->abortion_complete)
+ barrier();
+ HOSTDATA(shpnt)->aborting=0;
+ return HOSTDATA(shpnt)->abort_result;
+ }
+ else
+ {
+ /* we're already aborting a command */
+ restore_flags(flags);
+ return SCSI_ABORT_BUSY;
+ }
+
+ /* command wasn't found */
+ printk("command not found\n");
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+}
+
+/*
+ * Restore default values to the AIC-6260 registers and reset the fifos
+ */
+static void aha152x_reset_ports(struct Scsi_Host *shpnt)
+{
+ /* disable interrupts */
+ SETPORT(DMACNTRL0, RSTFIFO);
+
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL1, 0);
+ SETPORT(SCSISIG, 0);
+ SETPORT(SCSIRATE, 0);
+
+ /* clear all interrupt conditions */
+ SETPORT(SSTAT0, 0x7f);
+ SETPORT(SSTAT1, 0xef);
+
+ SETPORT(SSTAT4, SYNCERR|FWERR|FRERR);
+
+ SETPORT(DMACNTRL0, 0);
+ SETPORT(DMACNTRL1, 0);
+
+ SETPORT(BRSTCNTRL, 0xf1);
+
+ /* clear SCSI fifo and transfer count */
+ SETPORT(SXFRCTL0, CH1|CLRCH1|CLRSTCNT);
+ SETPORT(SXFRCTL0, CH1);
+
+ /* enable interrupts */
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+}
+
+/*
+ * Reset registers, reset a hanging bus and
+ * kill active and disconnected commands for target w/o soft reset
+ */
+int aha152x_reset(Scsi_Cmnd *SCpnt)
+{
+ struct Scsi_Host *shpnt = SCpnt->host;
+ unsigned long flags;
+ Scsi_Cmnd *ptr, *prev, *next;
+
+ aha152x_reset_ports(shpnt);
+
+ /* Reset, if bus hangs */
+ if(TESTLO(SSTAT1, BUSFREE))
+ {
+ CLRBITS(DMACNTRL0, INTEN);
+
+#if defined(DEBUG_RESET)
+ if(HOSTDATA(shpnt)->debug & debug_reset)
+ {
+ printk("aha152x: reset(), bus not free: SCSI RESET OUT\n");
+ show_queues(shpnt);
+ }
+#endif
+
+ ptr=CURRENT_SC;
+ if(ptr && !ptr->device->soft_reset)
+ {
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(CURRENT_SC);
+ CURRENT_SC=NULL;
+ }
+
+ save_flags(flags);
+ cli();
+ prev=NULL; ptr=DISCONNECTED_SC;
+ while(ptr)
+ {
+ if(!ptr->device->soft_reset)
+ {
+ if(prev)
+ prev->host_scribble = ptr->host_scribble;
+ else
+ DISCONNECTED_SC = (Scsi_Cmnd *) ptr->host_scribble;
+
+ next = (Scsi_Cmnd *) ptr->host_scribble;
+
+ ptr->host_scribble = NULL;
+ ptr->result = DID_RESET << 16;
+ ptr->scsi_done(ptr);
+
+ ptr = next;
+ }
+ else
+ {
+ prev=ptr;
+ ptr = (Scsi_Cmnd *) ptr->host_scribble;
+ }
+ }
+ restore_flags(flags);
+
+#if defined(DEBUG_RESET)
+ if(HOSTDATA(shpnt)->debug & debug_reset)
+ {
+ printk("commands on targets w/ soft-resets:\n");
+ show_queues(shpnt);
+ }
+#endif
+
+ /* RESET OUT */
+ SETPORT(SCSISEQ, SCSIRSTO);
+ do_pause(30);
+ SETPORT(SCSISEQ, 0);
+ do_pause(DELAY);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+ SETPORT(DMACNTRL0, INTEN);
+ }
+
+ return SCSI_RESET_SUCCESS;
+}
+
+/*
+ * Return the "logical geometry"
+ */
+int aha152x_biosparam(Scsi_Disk * disk, kdev_t dev, int *info_array)
+{
+ int size = disk->capacity;
+
+#if defined(DEBUG_BIOSPARAM)
+ if(HOSTDATA(shpnt)->debug & debug_biosparam)
+ printk("aha152x_biosparam: dev=%s, size=%d, ", kdevname(dev), size);
+#endif
+
+/* I took this from other SCSI drivers, since it provides
+ the correct data for my devices. */
+ info_array[0]=64;
+ info_array[1]=32;
+ info_array[2]=size>>11;
+
+#if defined(DEBUG_BIOSPARAM)
+ if(HOSTDATA(shpnt)->debug & debug_biosparam)
+ {
+ printk("bios geometry: head=%d, sec=%d, cyl=%d\n",
+ info_array[0], info_array[1], info_array[2]);
+ printk("WARNING: check, if the bios geometry is correct.\n");
+ }
+#endif
+
+ return 0;
+}
+
+/*
+ * Internal done function
+ */
+void aha152x_done(struct Scsi_Host *shpnt, int error)
+{
+ unsigned long flags;
+ Scsi_Cmnd *done_SC;
+
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ {
+ printk("\naha152x: done(), ");
+ disp_ports(shpnt);
+ }
+#endif
+
+ if (CURRENT_SC)
+ {
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("done(%x), ", error);
+#endif
+
+ save_flags(flags);
+ cli();
+
+ done_SC = CURRENT_SC;
+ CURRENT_SC = NULL;
+
+ /* turn led off, when no commands are in the driver */
+ HOSTDATA(shpnt)->commands--;
+ if(!HOSTDATA(shpnt)->commands)
+ SETPORT(PORTA, 0); /* turn led off */
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("ok (%d), ", HOSTDATA(shpnt)->commands);
+#endif
+ restore_flags(flags);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("BUS FREE loop, ");
+#endif
+ while(TESTLO(SSTAT1, BUSFREE))
+ barrier();
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("BUS FREE\n");
+#endif
+
+ done_SC->result = error;
+ if(done_SC->scsi_done)
+ {
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("calling scsi_done, ");
+#endif
+ done_SC->scsi_done(done_SC);
+#if defined(DEBUG_DONE)
+ if(HOSTDATA(shpnt)->debug & debug_done)
+ printk("done returned, ");
+#endif
+ }
+ else
+ panic("aha152x: current_SC->scsi_done() == NULL");
+ }
+ else
+ aha152x_panic(shpnt, "done() called outside of command");
+}
+
+/*
+ * Interrupts handler (main routine of the driver)
+ */
+void aha152x_intr(int irqno, struct pt_regs * regs)
+{
+ struct Scsi_Host *shpnt = aha152x_host[irqno-IRQ_MIN];
+ unsigned int flags;
+ int done=0, phase;
+
+#if defined(DEBUG_RACE)
+ enter_driver("intr");
+#else
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ printk("\naha152x: intr(), ");
+#endif
+#endif
+
+ /* no more interrupts from the controller, while we busy.
+ INTEN has to be restored, when we're ready to leave
+ intr(). To avoid race conditions we have to return
+ immediately afterwards. */
+ CLRBITS(DMACNTRL0, INTEN);
+ sti(); /* Yes, sti() really needs to be here */
+
+ /* disconnected target is trying to reconnect.
+ Only possible, if we have disconnected nexuses and
+ nothing is occupying the bus.
+ */
+ if(TESTHI(SSTAT0, SELDI) &&
+ DISCONNECTED_SC &&
+ (!CURRENT_SC || (CURRENT_SC->SCp.phase & in_selection)) )
+ {
+ int identify_msg, target, i;
+
+ /* Avoid conflicts when a target reconnects
+ while we are trying to connect to another. */
+ if(CURRENT_SC)
+ {
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i+, ");
+#endif
+ save_flags(flags);
+ cli();
+ append_SC(&ISSUE_SC, CURRENT_SC);
+ CURRENT_SC=NULL;
+ restore_flags(flags);
+ }
+
+ /* disable sequences */
+ SETPORT(SCSISEQ, 0);
+ SETPORT(SSTAT0, CLRSELDI);
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+#if defined(DEBUG_QUEUES) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_queues|debug_phases))
+ printk("reselected, ");
+#endif
+
+ i = GETPORT(SELID) & ~(1 << shpnt->this_id);
+ target=0;
+ if(i)
+ for(; (i & 1)==0; target++, i>>=1)
+ ;
+ else
+ aha152x_panic(shpnt, "reconnecting target unknown");
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("SELID=%02x, target=%d, ", GETPORT(SELID), target);
+#endif
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | target);
+ SETPORT(SCSISEQ, ENRESELI);
+
+ if(TESTLO(SSTAT0, SELDI))
+ aha152x_panic(shpnt, "RESELI failed");
+
+ SETPORT(SCSIRATE, HOSTDATA(shpnt)->syncrate[target]&0x7f);
+
+ SETPORT(SCSISIG, P_MSGI);
+
+ /* Get identify message */
+ if((i=getphase(shpnt))!=P_MSGI)
+ {
+ printk("target doesn't enter MSGI to identify (phase=%02x)\n", i);
+ aha152x_panic(shpnt, "unknown lun");
+ }
+ SETPORT(SCSISEQ, 0);
+
+ SETPORT(SXFRCTL0, CH1);
+
+ identify_msg = GETPORT(SCSIBUS);
+
+ if(!(identify_msg & IDENTIFY_BASE))
+ {
+ printk("target=%d, inbound message (%02x) != IDENTIFY\n",
+ target, identify_msg);
+ aha152x_panic(shpnt, "unknown lun");
+ }
+
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("identify=%02x, lun=%d, ", identify_msg, identify_msg & 0x3f);
+#endif
+
+ save_flags(flags);
+ cli();
+
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("d-, ");
+#endif
+ CURRENT_SC = remove_SC(&DISCONNECTED_SC,
+ target,
+ identify_msg & 0x3f);
+
+ if(!CURRENT_SC)
+ {
+ printk("lun=%d, ", identify_msg & 0x3f);
+ aha152x_panic(shpnt, "no disconnected command for that lun");
+ }
+
+ CURRENT_SC->SCp.phase &= ~disconnected;
+ restore_flags(flags);
+
+ make_acklow(shpnt);
+ if(getphase(shpnt)!=P_MSGI) {
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+#if defined(DEBUG_RACE)
+ leave_driver("(reselected) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+ }
+
+ /* Check, if we aren't busy with a command */
+ if(!CURRENT_SC)
+ {
+ /* bus is free to issue a queued command */
+ if(TESTHI(SSTAT1, BUSFREE) && ISSUE_SC)
+ {
+ save_flags(flags);
+ cli();
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("i-, ");
+#endif
+ CURRENT_SC = remove_first_SC(&ISSUE_SC);
+ restore_flags(flags);
+
+#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_selection|debug_phases))
+ printk("issuing command, ");
+#endif
+ CURRENT_SC->SCp.phase = in_selection;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_selection|debug_phases))
+ printk("selecting %d, ", CURRENT_SC->target);
+#endif
+ SETPORT(SCSIID, (shpnt->this_id << OID_) | CURRENT_SC->target);
+
+ /* Enable interrupts for SELECTION OUT DONE and SELECTION OUT INITIATED */
+ SETPORT(SXFRCTL1, HOSTDATA(shpnt)->parity ? (ENSPCHK|ENSTIMER) : ENSTIMER);
+
+ /* enable interrupts for SELECTION OUT DONE and SELECTION TIME OUT */
+ SETPORT(SIMODE0, ENSELDO | (DISCONNECTED_SC ? ENSELDI : 0));
+ SETPORT(SIMODE1, ENSELTIMO);
+
+ /* Enable SELECTION OUT sequence */
+ SETBITS(SCSISEQ, ENSELO | ENAUTOATNO);
+
+ }
+ else
+ {
+ /* No command we are busy with and no new to issue */
+ printk("aha152x: ignoring spurious interrupt, nothing to do\n");
+ if(TESTHI(DMACNTRL0, SWINT)) {
+ printk("aha152x: SWINT is set! Why?\n");
+ CLRBITS(DMACNTRL0, SWINT);
+ }
+ show_queues(shpnt);
+ }
+
+#if defined(DEBUG_RACE)
+ leave_driver("(selecting) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+
+ /* the bus is busy with something */
+
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ disp_ports(shpnt);
+#endif
+
+ /* we are waiting for the result of a selection attempt */
+ if(CURRENT_SC->SCp.phase & in_selection)
+ {
+ if(TESTLO(SSTAT1, SELTO))
+ /* no timeout */
+ if(TESTHI(SSTAT0, SELDO))
+ {
+ /* clear BUS FREE interrupt */
+ SETPORT(SSTAT1, CLRBUSFREE);
+
+ /* Disable SELECTION OUT sequence */
+ CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO);
+
+ /* Disable SELECTION OUT DONE interrupt */
+ CLRBITS(SIMODE0, ENSELDO);
+ CLRBITS(SIMODE1, ENSELTIMO);
+
+ if(TESTLO(SSTAT0, SELDO))
+ {
+ printk("aha152x: passing bus free condition\n");
+
+#if defined(DEBUG_RACE)
+ leave_driver("(passing bus free) intr");
+#endif
+ SETBITS(DMACNTRL0, INTEN);
+
+ if(CURRENT_SC->SCp.phase & aborted)
+ {
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_ERROR;
+ HOSTDATA(shpnt)->abortion_complete++;
+ }
+
+ aha152x_done(shpnt, DID_NO_CONNECT << 16);
+ return;
+ }
+#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
+ printk("SELDO (SELID=%x), ", GETPORT(SELID));
+#endif
+
+ /* selection was done */
+ SETPORT(SSTAT0, CLRSELDO);
+
+#if defined(DEBUG_ABORT)
+ if((HOSTDATA(shpnt)->debug & debug_abort) && (CURRENT_SC->SCp.phase & aborted))
+ printk("(ABORT) target selected, ");
+#endif
+
+ CURRENT_SC->SCp.phase &= ~in_selection;
+ CURRENT_SC->SCp.phase |= in_other;
+
+ ADDMSG(IDENTIFY(HOSTDATA(shpnt)->reconnect,CURRENT_SC->lun));
+
+ if(!(SYNCRATE&0x80) && HOSTDATA(shpnt)->synchronous)
+ {
+ ADDMSG(EXTENDED_MESSAGE);
+ ADDMSG(3);
+ ADDMSG(EXTENDED_SDTR);
+ ADDMSG(50);
+ ADDMSG(8);
+
+ printk("outbound SDTR: ");
+ print_msg(&MSG(MSGLEN-5));
+
+ SYNCRATE=0x80;
+ CURRENT_SC->SCp.phase |= in_sync;
+ }
+
+#if defined(DEBUG_RACE)
+ leave_driver("(SELDO) intr");
+#endif
+ SETPORT(SCSIRATE, SYNCRATE&0x7f);
+
+ SETPORT(SCSISIG, P_MSGO);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENREQINIT|ENBUSFREE);
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+ else
+ aha152x_panic(shpnt, "neither timeout nor selection\007");
+ else
+ {
+#if defined(DEBUG_SELECTION) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_selection|debug_phases))
+ printk("SELTO, ");
+#endif
+ /* end selection attempt */
+ CLRBITS(SCSISEQ, ENSELO|ENAUTOATNO);
+
+ /* timeout */
+ SETPORT(SSTAT1, CLRSELTIMO);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ SETBITS(DMACNTRL0, INTEN);
+#if defined(DEBUG_RACE)
+ leave_driver("(SELTO) intr");
+#endif
+
+ if(CURRENT_SC->SCp.phase & aborted)
+ {
+#if defined(DEBUG_ABORT)
+ if(HOSTDATA(shpnt)->debug & debug_abort)
+ printk("(ABORT) selection timeout, ");
+#endif
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_ERROR;
+ HOSTDATA(shpnt)->abortion_complete++;
+ }
+
+ if(TESTLO(SSTAT0, SELINGO))
+ /* ARBITRATION not won */
+ aha152x_done(shpnt, DID_BUS_BUSY << 16);
+ else
+ /* ARBITRATION won, but SELECTION failed */
+ aha152x_done(shpnt, DID_NO_CONNECT << 16);
+
+ return;
+ }
+ }
+
+ /* enable interrupt, when target leaves current phase */
+ phase = getphase(shpnt);
+ if(!(phase & ~P_MASK)) /* "real" phase */
+ SETPORT(SCSISIG, phase);
+ SETPORT(SSTAT1, CLRPHASECHG);
+ CURRENT_SC->SCp.phase =
+ (CURRENT_SC->SCp.phase & ~((P_MASK|1)<<16)) | (phase << 16);
+
+ /* information transfer phase */
+ switch(phase)
+ {
+ case P_MSGO: /* MESSAGE OUT */
+ {
+ int i, identify=0, abort=0;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_MSGO) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_msgo|debug_phases))
+ printk("MESSAGE OUT, ");
+#endif
+ if(MSGLEN==0)
+ {
+ ADDMSG(MESSAGE_REJECT);
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ printk("unexpected MSGO; rejecting, ");
+#endif
+ }
+
+
+ CLRBITS(SXFRCTL0, ENDMA);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE);
+
+ /* wait for data latch to become ready or a phase change */
+ while(TESTLO(DMASTAT, INTSTAT))
+ barrier();
+
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ {
+ int i;
+
+ printk("messages (");
+ for(i=0; i<MSGLEN; i+=print_msg(&MSG(i)), printk(" "))
+ ;
+ printk("), ");
+ }
+#endif
+
+ for(i=0; i<MSGLEN && TESTLO(SSTAT1, PHASEMIS); i++)
+ {
+#if defined(DEBUG_MSGO)
+ if(HOSTDATA(shpnt)->debug & debug_msgo)
+ printk("%x ", MSG(i));
+#endif
+ if(i==MSGLEN-1)
+ {
+ /* Leave MESSAGE OUT after transfer */
+ SETPORT(SSTAT1, CLRATNO);
+ }
+
+ SETPORT(SCSIDAT, MSG(i));
+
+ make_acklow(shpnt);
+ getphase(shpnt);
+
+ if(MSG(i)==IDENTIFY(HOSTDATA(shpnt)->reconnect,CURRENT_SC->lun))
+ identify++;
+
+ if(MSG(i)==ABORT)
+ abort++;
+
+ }
+
+ MSGLEN=0;
+
+ if(identify)
+ CURRENT_SC->SCp.phase |= sent_ident;
+
+ if(abort)
+ {
+ /* revive abort(); abort() enables interrupts */
+ HOSTDATA(shpnt)->abort_result=SCSI_ABORT_SUCCESS;
+ HOSTDATA(shpnt)->abortion_complete++;
+
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ /* exit */
+ SETBITS(DMACNTRL0, INTEN);
+#if defined(DEBUG_RACE)
+ leave_driver("(ABORT) intr");
+#endif
+ aha152x_done(shpnt, DID_ABORT<<16);
+ return;
+ }
+ }
+ break;
+
+ case P_CMD: /* COMMAND phase */
+#if defined(DEBUG_INTR) || defined(DEBUG_CMD) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_cmd|debug_phases))
+ printk("COMMAND, ");
+#endif
+ if(!(CURRENT_SC->SCp.sent_command))
+ {
+ int i;
+
+ CLRBITS(SXFRCTL0, ENDMA);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENREQINIT|ENBUSFREE);
+
+ /* wait for data latch to become ready or a phase change */
+ while(TESTLO(DMASTAT, INTSTAT))
+ barrier();
+
+ for(i=0; i<CURRENT_SC->cmd_len && TESTLO(SSTAT1, PHASEMIS); i++)
+ {
+ SETPORT(SCSIDAT, CURRENT_SC->cmnd[i]);
+
+ make_acklow(shpnt);
+ getphase(shpnt);
+ }
+
+ if(i<CURRENT_SC->cmd_len && TESTHI(SSTAT1, PHASEMIS))
+ aha152x_panic(shpnt, "target left COMMAND");
+
+ CURRENT_SC->SCp.sent_command++;
+ }
+ else
+ aha152x_panic(shpnt, "Nothing to send while in COMMAND");
+ break;
+
+ case P_MSGI: /* MESSAGE IN phase */
+ {
+ int start_sync=0;
+
+#if defined(DEBUG_INTR) || defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_intr|debug_msgi|debug_phases))
+ printk("MESSAGE IN, ");
+#endif
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENBUSFREE);
+
+ while(phase == P_MSGI)
+ {
+ CURRENT_SC->SCp.Message = GETPORT(SCSIDAT);
+ switch(CURRENT_SC->SCp.Message)
+ {
+ case DISCONNECT:
+#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_msgi|debug_phases))
+ printk("target disconnected, ");
+#endif
+ CURRENT_SC->SCp.Message = 0;
+ CURRENT_SC->SCp.phase |= disconnected;
+ if(!HOSTDATA(shpnt)->reconnect)
+ aha152x_panic(shpnt, "target was not allowed to disconnect");
+ break;
+
+ case COMMAND_COMPLETE:
+#if defined(DEBUG_MSGI) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_msgi|debug_phases))
+ printk("inbound message (COMMAND COMPLETE), ");
+#endif
+ done++;
+ break;
+
+ case MESSAGE_REJECT:
+ if(CURRENT_SC->SCp.phase & in_sync)
+ {
+ CURRENT_SC->SCp.phase &= ~in_sync;
+ SYNCRATE=0x80;
+ printk("synchronous rejected, ");
+ }
+ else
+ printk("inbound message (MESSAGE REJECT), ");
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (MESSAGE REJECT), ");
+#endif
+ break;
+
+ case SAVE_POINTERS:
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (SAVE DATA POINTERS), ");
+#endif
+ break;
+
+ case EXTENDED_MESSAGE:
+ {
+ char buffer[16];
+ int i;
+
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ printk("inbound message (EXTENDED MESSAGE), ");
+#endif
+ make_acklow(shpnt);
+ if(getphase(shpnt)!=P_MSGI)
+ break;
+
+ buffer[0]=EXTENDED_MESSAGE;
+ buffer[1]=GETPORT(SCSIDAT);
+
+ for(i=0; i<buffer[1] &&
+ (make_acklow(shpnt), getphase(shpnt)==P_MSGI); i++)
+ buffer[2+i]=GETPORT(SCSIDAT);
+
+#if defined(DEBUG_MSGI)
+ if(HOSTDATA(shpnt)->debug & debug_msgi)
+ print_msg(buffer);
+#endif
+
+ switch(buffer [2])
+ {
+ case EXTENDED_SDTR:
+ {
+ long ticks;
+
+ if(buffer[1]!=3)
+ aha152x_panic(shpnt, "SDTR message length != 3");
+
+ if(!HOSTDATA(shpnt)->synchronous)
+ break;
+
+ printk("inbound SDTR: "); print_msg(buffer);
+
+ ticks=(buffer[3]*4+49)/50;
+
+ if(CURRENT_SC->SCp.phase & in_sync)
+ {
+ /* we initiated SDTR */
+ if(ticks>9 || buffer[4]<1 || buffer[4]>8)
+ aha152x_panic(shpnt, "received SDTR invalid");
+
+ SYNCRATE |= ((ticks-2)<<4) + buffer[4];
+ }
+ else if(ticks<=9 && buffer[4]>=1)
+ {
+ if(buffer[4]>8)
+ buffer[4]=8;
+
+ ADDMSG(EXTENDED_MESSAGE);
+ ADDMSG(3);
+ ADDMSG(EXTENDED_SDTR);
+ if(ticks<4)
+ {
+ ticks=4;
+ ADDMSG(50);
+ }
+ else
+ ADDMSG(buffer[3]);
+
+ ADDMSG(buffer[4]);
+
+ printk("outbound SDTR: ");
+ print_msg(&MSG(MSGLEN-5));
+
+ CURRENT_SC->SCp.phase |= in_sync;
+
+ SYNCRATE |= ((ticks-2)<<4) + buffer[4];
+
+ start_sync++;
+ }
+ else
+ {
+ /* requested SDTR is too slow, do it asynchronously */
+ ADDMSG(MESSAGE_REJECT);
+ SYNCRATE = 0;
+ }
+
+ SETPORT(SCSIRATE, SYNCRATE&0x7f);
+ }
+ break;
+
+ case EXTENDED_MODIFY_DATA_POINTER:
+ case EXTENDED_EXTENDED_IDENTIFY:
+ case EXTENDED_WDTR:
+ default:
+ ADDMSG(MESSAGE_REJECT);
+ break;
+ }
+ }
+ break;
+
+ default:
+ printk("unsupported inbound message %x, ",
+ CURRENT_SC->SCp.Message);
+ break;
+
+ }
+
+ make_acklow(shpnt);
+ phase=getphase(shpnt);
+ }
+
+ if(start_sync)
+ CURRENT_SC->SCp.phase |= in_sync;
+ else
+ CURRENT_SC->SCp.phase &= ~in_sync;
+
+ if(MSGLEN>0)
+ SETPORT(SCSISIG, P_MSGI|ATNO);
+
+ /* clear SCSI fifo on BUSFREE */
+ if(phase==P_BUSFREE)
+ SETPORT(SXFRCTL0, CH1|CLRCH1);
+
+ if(CURRENT_SC->SCp.phase & disconnected)
+ {
+ save_flags(flags);
+ cli();
+#if defined(DEBUG_QUEUES)
+ if(HOSTDATA(shpnt)->debug & debug_queues)
+ printk("d+, ");
+#endif
+ append_SC(&DISCONNECTED_SC, CURRENT_SC);
+ CURRENT_SC->SCp.phase |= 1<<16;
+ CURRENT_SC = NULL;
+ restore_flags(flags);
+
+ SETBITS(SCSISEQ, ENRESELI);
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+ }
+ }
+ break;
+
+ case P_STATUS: /* STATUS IN phase */
+#if defined(DEBUG_STATUS) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_status|debug_intr|debug_phases))
+ printk("STATUS, ");
+#endif
+ SETPORT(SXFRCTL0, CH1);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENREQINIT|ENBUSFREE);
+
+ if(TESTHI(SSTAT1, PHASEMIS))
+ printk("aha152x: passing STATUS phase");
+
+ CURRENT_SC->SCp.Status = GETPORT(SCSIBUS);
+ make_acklow(shpnt);
+ getphase(shpnt);
+
+#if defined(DEBUG_STATUS)
+ if(HOSTDATA(shpnt)->debug & debug_status)
+ {
+ printk("inbound status ");
+ print_status(CURRENT_SC->SCp.Status);
+ printk(", ");
+ }
+#endif
+ break;
+
+ case P_DATAI: /* DATA IN phase */
+ {
+ int fifodata, data_count, done;
+
+#if defined(DEBUG_DATAI) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_datai|debug_intr|debug_phases))
+ printk("DATA IN, ");
+#endif
+
+#if 0
+ if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
+ printk("aha152x: P_DATAI: %d(%d) bytes left in FIFO, resetting\n",
+ GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+#endif
+
+ /* reset host fifo */
+ SETPORT(DMACNTRL0, RSTFIFO);
+ SETPORT(DMACNTRL0, RSTFIFO|ENDMA);
+
+ SETPORT(SXFRCTL0, CH1|SCSIEN|DMAEN);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+
+ /* done is set when the FIFO is empty after the target left DATA IN */
+ done=0;
+
+ /* while the target stays in DATA to transfer data */
+ while (!done)
+ {
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("expecting data, ");
+#endif
+ /* wait for PHASEMIS or full FIFO */
+ while(TESTLO (DMASTAT, DFIFOFULL|INTSTAT))
+ barrier();
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("ok, ");
+#endif
+
+ if(TESTHI(DMASTAT, DFIFOFULL))
+ fifodata=GETPORT(FIFOSTAT);
+ else
+ {
+ /* wait for SCSI fifo to get empty */
+ while(TESTLO(SSTAT2, SEMPTY))
+ barrier();
+
+ /* rest of data in FIFO */
+ fifodata=GETPORT(FIFOSTAT);
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("last transfer, ");
+#endif
+ done=1;
+ }
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("fifodata=%d, ", fifodata);
+#endif
+
+ while(fifodata && CURRENT_SC->SCp.this_residual)
+ {
+ data_count=fifodata;
+
+ /* limit data transfer to size of first sg buffer */
+ if (data_count > CURRENT_SC->SCp.this_residual)
+ data_count = CURRENT_SC->SCp.this_residual;
+
+ fifodata -= data_count;
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ printk("data_count=%d, ", data_count);
+#endif
+
+ if(data_count&1)
+ {
+ /* get a single byte in byte mode */
+ SETBITS(DMACNTRL0, _8BIT);
+ *CURRENT_SC->SCp.ptr++ = GETPORT(DATAPORT);
+ CURRENT_SC->SCp.this_residual--;
+ }
+ if(data_count>1)
+ {
+ CLRBITS(DMACNTRL0, _8BIT);
+ data_count >>= 1; /* Number of words */
+ insw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ /* show what comes with the last transfer */
+ if(done)
+ {
+#ifdef 0
+ int i;
+ unsigned char *data;
+#endif
+
+ printk("data on last transfer (%d bytes) ",
+ 2*data_count);
+#ifdef 0
+ printk("data on last transfer (%d bytes: ",
+ 2*data_count);
+ data = (unsigned char *) CURRENT_SC->SCp.ptr;
+ for(i=0; i<2*data_count; i++)
+ printk("%2x ", *data++);
+ printk("), ");
+#endif
+ }
+#endif
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ }
+
+ /* if this buffer is full and there are more buffers left */
+ if (!CURRENT_SC->SCp.this_residual &&
+ CURRENT_SC->SCp.buffers_residual)
+ {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr =
+ CURRENT_SC->SCp.buffer->address;
+ CURRENT_SC->SCp.this_residual =
+ CURRENT_SC->SCp.buffer->length;
+ }
+ }
+
+ /*
+ * Fifo should be empty
+ */
+ if(fifodata>0)
+ {
+ printk("aha152x: more data than expected (%d bytes)\n",
+ GETPORT(FIFOSTAT));
+ SETBITS(DMACNTRL0, _8BIT);
+ printk("aha152x: data (");
+ while(fifodata--)
+ printk("%2x ", GETPORT(DATAPORT));
+ printk(")\n");
+ }
+
+#if defined(DEBUG_DATAI)
+ if(HOSTDATA(shpnt)->debug & debug_datai)
+ if(!fifodata)
+ printk("fifo empty, ");
+ else
+ printk("something left in fifo, ");
+#endif
+ }
+
+#if defined(DEBUG_DATAI)
+ if((HOSTDATA(shpnt)->debug & debug_datai) &&
+ (CURRENT_SC->SCp.buffers_residual ||
+ CURRENT_SC->SCp.this_residual))
+ printk("left buffers (buffers=%d, bytes=%d), ",
+ CURRENT_SC->SCp.buffers_residual,
+ CURRENT_SC->SCp.this_residual);
+#endif
+ /* transfer can be considered ended, when SCSIEN reads back zero */
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+ while(TESTHI(SXFRCTL0, SCSIEN))
+ barrier();
+ CLRBITS(DMACNTRL0, ENDMA);
+
+#if defined(DEBUG_DATAI) || defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & (debug_datai|debug_intr))
+ printk("got %d bytes, ", GETSTCNT());
+#endif
+
+ CURRENT_SC->SCp.have_data_in++;
+ }
+ break;
+
+ case P_DATAO: /* DATA OUT phase */
+ {
+ int data_count;
+
+#if defined(DEBUG_DATAO) || defined(DEBUG_INTR) || defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & (debug_datao|debug_intr|debug_phases))
+ printk("DATA OUT, ");
+#endif
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("got data to send (bytes=%d, buffers=%d), ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+
+ if(GETPORT(FIFOSTAT) || GETPORT(SSTAT2) & (SFULL|SFCNT))
+ {
+ printk("%d(%d) left in FIFO, ",
+ GETPORT(FIFOSTAT), GETPORT(SSTAT2) & (SFULL|SFCNT));
+ aha152x_panic(shpnt, "FIFO should be empty");
+ }
+
+ SETPORT(SXFRCTL0, CH1|CLRSTCNT|CLRCH1);
+ SETPORT(SXFRCTL0, SCSIEN|DMAEN|CH1);
+
+ SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
+ SETPORT(DMACNTRL0, ENDMA|WRITE_READ);
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+
+ /* while current buffer is not empty or
+ there are more buffers to transfer */
+ while(TESTLO(SSTAT1, PHASEMIS) &&
+ (CURRENT_SC->SCp.this_residual ||
+ CURRENT_SC->SCp.buffers_residual))
+ {
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("sending data (left: bytes=%d, buffers=%d), waiting, ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+ /* transfer rest of buffer, but max. 128 byte */
+ data_count =
+ CURRENT_SC->SCp.this_residual > 128 ?
+ 128 : CURRENT_SC->SCp.this_residual ;
+
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("data_count=%d, ", data_count);
+#endif
+
+ if(data_count&1)
+ {
+ /* put a single byte in byte mode */
+ SETBITS(DMACNTRL0, _8BIT);
+ SETPORT(DATAPORT, *CURRENT_SC->SCp.ptr++);
+ CURRENT_SC->SCp.this_residual--;
+ }
+ if(data_count>1)
+ {
+ CLRBITS(DMACNTRL0, _8BIT);
+ data_count >>= 1; /* number of words */
+ outsw(DATAPORT, CURRENT_SC->SCp.ptr, data_count);
+ CURRENT_SC->SCp.ptr += 2 * data_count;
+ CURRENT_SC->SCp.this_residual -= 2 * data_count;
+ }
+
+ /* wait for FIFO to get empty */
+ while(TESTLO(DMASTAT, DFIFOEMP|INTSTAT))
+ barrier();
+
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("fifo (%d bytes), transfered (%d bytes), ",
+ GETPORT(FIFOSTAT), GETSTCNT());
+#endif
+
+ /* if this buffer is empty and there are more buffers left */
+ if (TESTLO(SSTAT1, PHASEMIS) &&
+ !CURRENT_SC->SCp.this_residual &&
+ CURRENT_SC->SCp.buffers_residual)
+ {
+ /* advance to next buffer */
+ CURRENT_SC->SCp.buffers_residual--;
+ CURRENT_SC->SCp.buffer++;
+ CURRENT_SC->SCp.ptr =
+ CURRENT_SC->SCp.buffer->address;
+ CURRENT_SC->SCp.this_residual =
+ CURRENT_SC->SCp.buffer->length;
+ }
+ }
+
+ if (CURRENT_SC->SCp.this_residual || CURRENT_SC->SCp.buffers_residual)
+ {
+ /* target leaves DATA OUT for an other phase
+ (perhaps disconnect) */
+
+ /* data in fifos has to be resend */
+ data_count = GETPORT(SSTAT2) & (SFULL|SFCNT);
+
+ data_count += GETPORT(FIFOSTAT) ;
+ CURRENT_SC->SCp.ptr -= data_count;
+ CURRENT_SC->SCp.this_residual += data_count;
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("left data (bytes=%d, buffers=%d), fifos (bytes=%d), "
+ "transfer incomplete, resetting fifo, ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual,
+ data_count);
+#endif
+ SETPORT(DMACNTRL0, WRITE_READ|RSTFIFO);
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+ CLRBITS(DMACNTRL0, ENDMA);
+ }
+ else
+ {
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("waiting for SCSI fifo to get empty, ");
+#endif
+ /* wait for SCSI fifo to get empty */
+ while(TESTLO(SSTAT2, SEMPTY))
+ barrier();
+#if defined(DEBUG_DATAO)
+ if(HOSTDATA(shpnt)->debug & debug_datao)
+ printk("ok, left data (bytes=%d, buffers=%d) ",
+ CURRENT_SC->SCp.this_residual,
+ CURRENT_SC->SCp.buffers_residual);
+#endif
+ CLRBITS(SXFRCTL0, SCSIEN|DMAEN);
+
+ /* transfer can be considered ended, when SCSIEN reads back zero */
+ while(TESTHI(SXFRCTL0, SCSIEN))
+ barrier();
+
+ CLRBITS(DMACNTRL0, ENDMA);
+ }
+
+#if defined(DEBUG_DATAO) || defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & (debug_datao|debug_intr))
+ printk("sent %d data bytes, ", GETSTCNT());
+#endif
+ }
+ break;
+
+ case P_BUSFREE: /* BUSFREE */
+#if defined(DEBUG_RACE)
+ leave_driver("(BUSFREE) intr");
+#endif
+#if defined(DEBUG_PHASES)
+ if(HOSTDATA(shpnt)->debug & debug_phases)
+ printk("unexpected BUS FREE, ");
+#endif
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ aha152x_done(shpnt, DID_ERROR << 16); /* Don't know any better */
+ return;
+ break;
+
+ case P_PARITY: /* parity error in DATA phase */
+#if defined(DEBUG_RACE)
+ leave_driver("(DID_PARITY) intr");
+#endif
+ printk("PARITY error in DATA phase, ");
+
+ CURRENT_SC->SCp.phase &= ~(P_MASK<<16);
+
+ SETBITS(DMACNTRL0, INTEN);
+ aha152x_done(shpnt, DID_PARITY << 16);
+ return;
+ break;
+
+ default:
+ printk("aha152x: unexpected phase\n");
+ break;
+ }
+
+ if(done)
+ {
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ printk("command done.\n");
+#endif
+#if defined(DEBUG_RACE)
+ leave_driver("(done) intr");
+#endif
+
+ SETPORT(SIMODE0, DISCONNECTED_SC ? ENSELDI : 0);
+ SETPORT(SIMODE1, ISSUE_SC ? ENBUSFREE : 0);
+ SETPORT(SCSISEQ, DISCONNECTED_SC ? ENRESELI : 0);
+
+ SETBITS(DMACNTRL0, INTEN);
+
+ aha152x_done(shpnt,
+ (CURRENT_SC->SCp.Status & 0xff)
+ | ((CURRENT_SC->SCp.Message & 0xff) << 8)
+ | (DID_OK << 16));
+
+#if defined(DEBUG_RACE)
+ printk("done returned (DID_OK: Status=%x; Message=%x).\n",
+ CURRENT_SC->SCp.Status, CURRENT_SC->SCp.Message);
+#endif
+ return;
+ }
+
+ if(CURRENT_SC)
+ CURRENT_SC->SCp.phase |= 1<<16 ;
+
+ SETPORT(SIMODE0, 0);
+ SETPORT(SIMODE1, ENPHASEMIS|ENBUSFREE);
+#if defined(DEBUG_INTR)
+ if(HOSTDATA(shpnt)->debug & debug_intr)
+ disp_enintr(shpnt);
+#endif
+#if defined(DEBUG_RACE)
+ leave_driver("(PHASEEND) intr");
+#endif
+
+ SETBITS(DMACNTRL0, INTEN);
+ return;
+}
+
+/*
+ * Dump the current driver status and panic...
+ */
+static void aha152x_panic(struct Scsi_Host *shpnt, char *msg)
+{
+ printk("\naha152x: %s\n", msg);
+ show_queues(shpnt);
+ panic("aha152x panic");
+}
+
+/*
+ * Display registers of AIC-6260
+ */
+static void disp_ports(struct Scsi_Host *shpnt)
+{
+#ifdef DEBUG_AHA152X
+ int s;
+
+#ifdef SKIP_PORTS
+ if(HOSTDATA(shpnt)->debug & debug_skipports)
+ return;
+#endif
+
+ printk("\n%s: ", CURRENT_SC ? "on bus" : "waiting");
+
+ s=GETPORT(SCSISEQ);
+ printk("SCSISEQ (");
+ if(s & TEMODEO) printk("TARGET MODE ");
+ if(s & ENSELO) printk("SELO ");
+ if(s & ENSELI) printk("SELI ");
+ if(s & ENRESELI) printk("RESELI ");
+ if(s & ENAUTOATNO) printk("AUTOATNO ");
+ if(s & ENAUTOATNI) printk("AUTOATNI ");
+ if(s & ENAUTOATNP) printk("AUTOATNP ");
+ if(s & SCSIRSTO) printk("SCSIRSTO ");
+ printk(");");
+
+ printk(" SCSISIG (");
+ s=GETPORT(SCSISIG);
+ switch(s & P_MASK)
+ {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*illegal*");
+ break;
+ }
+
+ printk("); ");
+
+ printk("INTSTAT (%s); ", TESTHI(DMASTAT, INTSTAT) ? "hi" : "lo");
+
+ printk("SSTAT (");
+ s=GETPORT(SSTAT0);
+ if(s & TARGET) printk("TARGET ");
+ if(s & SELDO) printk("SELDO ");
+ if(s & SELDI) printk("SELDI ");
+ if(s & SELINGO) printk("SELINGO ");
+ if(s & SWRAP) printk("SWRAP ");
+ if(s & SDONE) printk("SDONE ");
+ if(s & SPIORDY) printk("SPIORDY ");
+ if(s & DMADONE) printk("DMADONE ");
+
+ s=GETPORT(SSTAT1);
+ if(s & SELTO) printk("SELTO ");
+ if(s & ATNTARG) printk("ATNTARG ");
+ if(s & SCSIRSTI) printk("SCSIRSTI ");
+ if(s & PHASEMIS) printk("PHASEMIS ");
+ if(s & BUSFREE) printk("BUSFREE ");
+ if(s & SCSIPERR) printk("SCSIPERR ");
+ if(s & PHASECHG) printk("PHASECHG ");
+ if(s & REQINIT) printk("REQINIT ");
+ printk("); ");
+
+
+ printk("SSTAT (");
+
+ s=GETPORT(SSTAT0) & GETPORT(SIMODE0);
+
+ if(s & TARGET) printk("TARGET ");
+ if(s & SELDO) printk("SELDO ");
+ if(s & SELDI) printk("SELDI ");
+ if(s & SELINGO) printk("SELINGO ");
+ if(s & SWRAP) printk("SWRAP ");
+ if(s & SDONE) printk("SDONE ");
+ if(s & SPIORDY) printk("SPIORDY ");
+ if(s & DMADONE) printk("DMADONE ");
+
+ s=GETPORT(SSTAT1) & GETPORT(SIMODE1);
+
+ if(s & SELTO) printk("SELTO ");
+ if(s & ATNTARG) printk("ATNTARG ");
+ if(s & SCSIRSTI) printk("SCSIRSTI ");
+ if(s & PHASEMIS) printk("PHASEMIS ");
+ if(s & BUSFREE) printk("BUSFREE ");
+ if(s & SCSIPERR) printk("SCSIPERR ");
+ if(s & PHASECHG) printk("PHASECHG ");
+ if(s & REQINIT) printk("REQINIT ");
+ printk("); ");
+
+ printk("SXFRCTL0 (");
+
+ s=GETPORT(SXFRCTL0);
+ if(s & SCSIEN) printk("SCSIEN ");
+ if(s & DMAEN) printk("DMAEN ");
+ if(s & CH1) printk("CH1 ");
+ if(s & CLRSTCNT) printk("CLRSTCNT ");
+ if(s & SPIOEN) printk("SPIOEN ");
+ if(s & CLRCH1) printk("CLRCH1 ");
+ printk("); ");
+
+ printk("SIGNAL (");
+
+ s=GETPORT(SCSISIG);
+ if(s & ATNI) printk("ATNI ");
+ if(s & SELI) printk("SELI ");
+ if(s & BSYI) printk("BSYI ");
+ if(s & REQI) printk("REQI ");
+ if(s & ACKI) printk("ACKI ");
+ printk("); ");
+
+ printk("SELID (%02x), ", GETPORT(SELID));
+
+ printk("SSTAT2 (");
+
+ s=GETPORT(SSTAT2);
+ if(s & SOFFSET) printk("SOFFSET ");
+ if(s & SEMPTY) printk("SEMPTY ");
+ if(s & SFULL) printk("SFULL ");
+ printk("); SFCNT (%d); ", s & (SFULL|SFCNT));
+
+ s=GETPORT(SSTAT3);
+ printk("SCSICNT (%d), OFFCNT(%d), ", (s&0xf0)>>4, s&0x0f);
+
+ printk("SSTAT4 (");
+ s=GETPORT(SSTAT4);
+ if(s & SYNCERR) printk("SYNCERR ");
+ if(s & FWERR) printk("FWERR ");
+ if(s & FRERR) printk("FRERR ");
+ printk("); ");
+
+ printk("DMACNTRL0 (");
+ s=GETPORT(DMACNTRL0);
+ printk("%s ", s & _8BIT ? "8BIT" : "16BIT");
+ printk("%s ", s & DMA ? "DMA" : "PIO" );
+ printk("%s ", s & WRITE_READ ? "WRITE" : "READ" );
+ if(s & ENDMA) printk("ENDMA ");
+ if(s & INTEN) printk("INTEN ");
+ if(s & RSTFIFO) printk("RSTFIFO ");
+ if(s & SWINT) printk("SWINT ");
+ printk("); ");
+
+
+#if 0
+ printk("DMACNTRL1 (");
+
+ s=GETPORT(DMACNTRL1);
+ if(s & PWRDWN) printk("PWRDN ");
+ printk("); ");
+
+
+ printk("STK (%d); ", s & 0xf);
+
+#endif
+
+ printk("DMASTAT (");
+ s=GETPORT(DMASTAT);
+ if(s & ATDONE) printk("ATDONE ");
+ if(s & WORDRDY) printk("WORDRDY ");
+ if(s & DFIFOFULL) printk("DFIFOFULL ");
+ if(s & DFIFOEMP) printk("DFIFOEMP ");
+ printk(")");
+
+ printk("\n");
+#endif
+}
+
+/*
+ * display enabled interrupts
+ */
+static void disp_enintr(struct Scsi_Host *shpnt)
+{
+ int s;
+
+ printk("enabled interrupts (");
+
+ s=GETPORT(SIMODE0);
+ if(s & ENSELDO) printk("ENSELDO ");
+ if(s & ENSELDI) printk("ENSELDI ");
+ if(s & ENSELINGO) printk("ENSELINGO ");
+ if(s & ENSWRAP) printk("ENSWRAP ");
+ if(s & ENSDONE) printk("ENSDONE ");
+ if(s & ENSPIORDY) printk("ENSPIORDY ");
+ if(s & ENDMADONE) printk("ENDMADONE ");
+
+ s=GETPORT(SIMODE1);
+ if(s & ENSELTIMO) printk("ENSELTIMO ");
+ if(s & ENATNTARG) printk("ENATNTARG ");
+ if(s & ENPHASEMIS) printk("ENPHASEMIS ");
+ if(s & ENBUSFREE) printk("ENBUSFREE ");
+ if(s & ENSCSIPERR) printk("ENSCSIPERR ");
+ if(s & ENPHASECHG) printk("ENPHASECHG ");
+ if(s & ENREQINIT) printk("ENREQINIT ");
+ printk(")\n");
+}
+
+#if defined(DEBUG_RACE)
+
+static const char *should_leave;
+static int in_driver=0;
+
+/*
+ * Only one routine can be in the driver at once.
+ */
+static void enter_driver(const char *func)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ printk("aha152x: entering %s() (%x)\n", func, jiffies);
+ if(in_driver)
+ {
+ printk("%s should leave first.\n", should_leave);
+ panic("aha152x: already in driver\n");
+ }
+
+ in_driver++;
+ should_leave=func;
+ restore_flags(flags);
+}
+
+static void leave_driver(const char *func)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ printk("\naha152x: leaving %s() (%x)\n", func, jiffies);
+ if(!in_driver)
+ {
+ printk("aha152x: %s already left.\n", should_leave);
+ panic("aha152x: %s already left driver.\n");
+ }
+
+ in_driver--;
+ should_leave=func;
+ restore_flags(flags);
+}
+#endif
+
+/*
+ * Show the command data of a command
+ */
+static void show_command(Scsi_Cmnd *ptr)
+{
+ printk("0x%08x: target=%d; lun=%d; cmnd=(",
+ (unsigned int) ptr, ptr->target, ptr->lun);
+
+ print_command(ptr->cmnd);
+
+ printk("); residual=%d; buffers=%d; phase |",
+ ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if(ptr->SCp.phase & not_issued ) printk("not issued|");
+ if(ptr->SCp.phase & in_selection) printk("in selection|");
+ if(ptr->SCp.phase & disconnected) printk("disconnected|");
+ if(ptr->SCp.phase & aborted ) printk("aborted|");
+ if(ptr->SCp.phase & sent_ident ) printk("send_ident|");
+ if(ptr->SCp.phase & in_other)
+ {
+ printk("; in other(");
+ switch((ptr->SCp.phase >> 16) & P_MASK)
+ {
+ case P_DATAO:
+ printk("DATA OUT");
+ break;
+ case P_DATAI:
+ printk("DATA IN");
+ break;
+ case P_CMD:
+ printk("COMMAND");
+ break;
+ case P_STATUS:
+ printk("STATUS");
+ break;
+ case P_MSGO:
+ printk("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ printk("MESSAGE IN");
+ break;
+ default:
+ printk("*illegal*");
+ break;
+ }
+ printk(")");
+ if(ptr->SCp.phase & (1<<16))
+ printk("; phaseend");
+ }
+ printk("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
+}
+
+/*
+ * Dump the queued data
+ */
+static void show_queues(struct Scsi_Host *shpnt)
+{
+ unsigned long flags;
+ Scsi_Cmnd *ptr;
+
+ save_flags(flags);
+ cli();
+ printk("QUEUE STATUS:\nissue_SC:\n");
+ for(ptr=ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ show_command(ptr);
+
+ printk("current_SC:\n");
+ if(CURRENT_SC)
+ show_command(CURRENT_SC);
+ else
+ printk("none\n");
+
+ printk("disconnected_SC:\n");
+ for(ptr=DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ show_command(ptr);
+
+ disp_ports(shpnt);
+ disp_enintr(shpnt);
+ restore_flags(flags);
+}
+
+int aha152x_set_info(char *buffer, int length, struct Scsi_Host *shpnt)
+{
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) pos += sprintf(pos, ## args)
+
+static int get_command(char *pos, Scsi_Cmnd *ptr)
+{
+ char *start = pos;
+ int i;
+
+ SPRINTF("0x%08x: target=%d; lun=%d; cmnd=(",
+ (unsigned int) ptr, ptr->target, ptr->lun);
+
+ for(i=0; i<COMMAND_SIZE(ptr->cmnd[0]); i++)
+ SPRINTF("0x%02x", ptr->cmnd[i]);
+
+ SPRINTF("); residual=%d; buffers=%d; phase |",
+ ptr->SCp.this_residual, ptr->SCp.buffers_residual);
+
+ if(ptr->SCp.phase & not_issued ) SPRINTF("not issued|");
+ if(ptr->SCp.phase & in_selection) SPRINTF("in selection|");
+ if(ptr->SCp.phase & disconnected) SPRINTF("disconnected|");
+ if(ptr->SCp.phase & aborted ) SPRINTF("aborted|");
+ if(ptr->SCp.phase & sent_ident ) SPRINTF("send_ident|");
+ if(ptr->SCp.phase & in_other)
+ {
+ SPRINTF("; in other(");
+ switch((ptr->SCp.phase >> 16) & P_MASK)
+ {
+ case P_DATAO:
+ SPRINTF("DATA OUT");
+ break;
+ case P_DATAI:
+ SPRINTF("DATA IN");
+ break;
+ case P_CMD:
+ SPRINTF("COMMAND");
+ break;
+ case P_STATUS:
+ SPRINTF("STATUS");
+ break;
+ case P_MSGO:
+ SPRINTF("MESSAGE OUT");
+ break;
+ case P_MSGI:
+ SPRINTF("MESSAGE IN");
+ break;
+ default:
+ SPRINTF("*illegal*");
+ break;
+ }
+ SPRINTF(")");
+ if(ptr->SCp.phase & (1<<16))
+ SPRINTF("; phaseend");
+ }
+ SPRINTF("; next=0x%08x\n", (unsigned int) ptr->host_scribble);
+
+ return(pos-start);
+}
+
+#undef SPRINTF
+#define SPRINTF(args...) do { if(pos < buffer + length) pos += sprintf(pos, ## args); } while(0)
+
+int aha152x_proc_info(
+ char *buffer,
+ char **start,
+ off_t offset,
+ int length,
+ int hostno,
+ int inout
+ )
+{
+ int i;
+ char *pos = buffer;
+ Scsi_Device *scd;
+ struct Scsi_Host *shpnt;
+ unsigned long flags;
+ Scsi_Cmnd *ptr;
+
+ for(i=0, shpnt= (struct Scsi_Host *) NULL; i<IRQS; i++)
+ if(aha152x_host[i] && aha152x_host[i]->host_no == hostno)
+ shpnt=aha152x_host[i];
+
+ if(!shpnt)
+ return(-ESRCH);
+
+ if(inout) /* Has data been written to the file ? */
+ return(aha152x_set_info(buffer, length, shpnt));
+
+ SPRINTF(AHA152X_REVID "\n");
+
+ save_flags(flags);
+ cli();
+
+ SPRINTF("vital data:\nioports 0x%04x to 0x%04x\n",
+ shpnt->io_port, shpnt->io_port+shpnt->n_io_port-1);
+ SPRINTF("interrupt 0x%02x\n", shpnt->irq);
+ SPRINTF("disconnection/reconnection %s\n",
+ HOSTDATA(shpnt)->reconnect ? "enabled" : "disabled");
+ SPRINTF("parity checking %s\n",
+ HOSTDATA(shpnt)->parity ? "enabled" : "disabled");
+ SPRINTF("synchronous transfers %s\n",
+ HOSTDATA(shpnt)->synchronous ? "enabled" : "disabled");
+ SPRINTF("current queued %d commands\n",
+ HOSTDATA(shpnt)->commands);
+
+#if 0
+ SPRINTF("synchronously operating targets (tick=%ld ns):\n",
+ 250000000/loops_per_sec);
+ for(i=0; i<8; i++)
+ if(HOSTDATA(shpnt)->syncrate[i]&0x7f)
+ SPRINTF("target %d: period %dT/%ldns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2),
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2)*
+ 250000000/loops_per_sec,
+ HOSTDATA(shpnt)->syncrate[i]&0x0f);
+#else
+ SPRINTF("synchronously operating targets (tick=50 ns):\n");
+ for(i=0; i<8; i++)
+ if(HOSTDATA(shpnt)->syncrate[i]&0x7f)
+ SPRINTF("target %d: period %dT/%dns; req/ack offset %d\n",
+ i,
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2),
+ (((HOSTDATA(shpnt)->syncrate[i]&0x70)>>4)+2)*50,
+ HOSTDATA(shpnt)->syncrate[i]&0x0f);
+#endif
+
+#ifdef DEBUG_AHA152X
+#define PDEBUG(flags,txt) if(HOSTDATA(shpnt)->debug & flags) SPRINTF("(%s) ", txt);
+
+ SPRINTF("enabled debugging options:\n");
+
+ PDEBUG(debug_skipports, "skip ports");
+ PDEBUG(debug_queue, "queue");
+ PDEBUG(debug_intr, "interrupt");
+ PDEBUG(debug_selection, "selection");
+ PDEBUG(debug_msgo, "message out");
+ PDEBUG(debug_msgi, "message in");
+ PDEBUG(debug_status, "status");
+ PDEBUG(debug_cmd, "command");
+ PDEBUG(debug_datai, "data in");
+ PDEBUG(debug_datao, "data out");
+ PDEBUG(debug_abort, "abort");
+ PDEBUG(debug_done, "done");
+ PDEBUG(debug_biosparam, "bios parameters");
+ PDEBUG(debug_phases, "phases");
+ PDEBUG(debug_queues, "queues");
+ PDEBUG(debug_reset, "reset");
+
+ SPRINTF("\n");
+#endif
+
+ SPRINTF("queue status:\nnot yet issued commands:\n");
+ for(ptr=ISSUE_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos += get_command(pos, ptr);
+
+ if(CURRENT_SC)
+ {
+ SPRINTF("current command:\n");
+ pos += get_command(pos, CURRENT_SC);
+ }
+
+ SPRINTF("disconnected commands:\n");
+ for(ptr=DISCONNECTED_SC; ptr; ptr = (Scsi_Cmnd *) ptr->host_scribble)
+ pos += get_command(pos, ptr);
+
+ restore_flags(flags);
+
+ scd = scsi_devices;
+
+ SPRINTF("Attached devices: %s\n", (scd)?"":"none");
+
+ while (scd) {
+ if (scd->host == shpnt) {
+
+ SPRINTF("Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
+ scd->channel, scd->id, scd->lun);
+ for (i=0; i<8; i++) {
+ if (scd->vendor[i] >= 0x20)
+ SPRINTF("%c", scd->vendor[i]);
+ else
+ SPRINTF(" ");
+ }
+ SPRINTF(" Model: ");
+ for (i = 0; i < 16; i++) {
+ if (scd->model[i] >= 0x20)
+ SPRINTF("%c", scd->model[i]);
+ else
+ SPRINTF(" ");
+ }
+ SPRINTF(" Rev: ");
+ for (i = 0; i < 4; i++) {
+ if (scd->rev[i] >= 0x20)
+ SPRINTF("%c", scd->rev[i]);
+ else
+ SPRINTF(" ");
+ }
+ SPRINTF("\n");
+
+ SPRINTF(" Type: %d ", scd->type);
+ SPRINTF(" ANSI SCSI revision: %02x",
+ (scd->scsi_level < 3)?1:2);
+
+ if (scd->scsi_level == 2)
+ SPRINTF(" CCS\n");
+ else
+ SPRINTF("\n");
+ }
+ scd = scd->next;
+ }
+
+ *start=buffer+offset;
+ if (pos - buffer < offset)
+ return 0;
+ else if (pos - buffer - offset < length)
+ return pos - buffer - offset;
+ else
+ return length;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA152X;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/aha152x.h b/i386/i386at/gpl/linux/scsi/aha152x.h
new file mode 100644
index 00000000..d62e5cfe
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aha152x.h
@@ -0,0 +1,373 @@
+#ifndef _AHA152X_H
+#define _AHA152X_H
+
+/*
+ * $Id: aha152x.h,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ */
+
+#if defined(__KERNEL__)
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include <asm/io.h>
+
+int aha152x_detect(Scsi_Host_Template *);
+int aha152x_command(Scsi_Cmnd *);
+int aha152x_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int aha152x_abort(Scsi_Cmnd *);
+int aha152x_reset(Scsi_Cmnd *);
+int aha152x_biosparam(Disk *, kdev_t, int*);
+int aha152x_proc_info(char *buffer, char **start, off_t offset, int length, int hostno, int inout);
+
+/* number of queueable commands
+ (unless we support more than 1 cmd_per_lun this should do) */
+#define AHA152X_MAXQUEUE 7
+
+#define AHA152X_REVID "Adaptec 152x SCSI driver; $Revision: 1.1.1.1 $"
+
+extern struct proc_dir_entry proc_scsi_aha152x;
+
+/* Initial value of Scsi_Host entry */
+#define AHA152X { /* next */ NULL, \
+ /* usage_count */ NULL, \
+ /* proc_dir */ &proc_scsi_aha152x, \
+ /* proc_info */ aha152x_proc_info, \
+ /* name */ AHA152X_REVID, \
+ /* detect */ aha152x_detect, \
+ /* release */ NULL, \
+ /* info */ NULL, \
+ /* command */ aha152x_command, \
+ /* queuecommand */ aha152x_queue, \
+ /* abort */ aha152x_abort, \
+ /* reset */ aha152x_reset, \
+ /* slave_attach */ /* NULL */ 0, \
+ /* bios_param */ aha152x_biosparam, \
+ /* can_queue */ 1, \
+ /* this_id */ 7, \
+ /* sg_tablesize */ SG_ALL, \
+ /* cmd_per_lun */ 1, \
+ /* present */ 0, \
+ /* unchecked_isa_dma */ 0, \
+ /* use_clustering */ DISABLE_CLUSTERING }
+#endif
+
+
+/* port addresses */
+#define SCSISEQ (shpnt->io_port+0x00) /* SCSI sequence control */
+#define SXFRCTL0 (shpnt->io_port+0x01) /* SCSI transfer control 0 */
+#define SXFRCTL1 (shpnt->io_port+0x02) /* SCSI transfer control 1 */
+#define SCSISIG (shpnt->io_port+0x03) /* SCSI signal in/out */
+#define SCSIRATE (shpnt->io_port+0x04) /* SCSI rate control */
+#define SELID (shpnt->io_port+0x05) /* selection/reselection ID */
+#define SCSIID SELID /* SCSI ID */
+#define SCSIDAT (shpnt->io_port+0x06) /* SCSI latched data */
+#define SCSIBUS (shpnt->io_port+0x07) /* SCSI data bus */
+#define STCNT0 (shpnt->io_port+0x08) /* SCSI transfer count 0 */
+#define STCNT1 (shpnt->io_port+0x09) /* SCSI transfer count 1 */
+#define STCNT2 (shpnt->io_port+0x0a) /* SCSI transfer count 2 */
+#define SSTAT0 (shpnt->io_port+0x0b) /* SCSI interrupt status 0 */
+#define SSTAT1 (shpnt->io_port+0x0c) /* SCSI interrupt status 1 */
+#define SSTAT2 (shpnt->io_port+0x0d) /* SCSI interrupt status 2 */
+#define SCSITEST (shpnt->io_port+0x0e) /* SCSI test control */
+#define SSTAT3 SCSITEST /* SCSI interrupt status 3 */
+#define SSTAT4 (shpnt->io_port+0x0f) /* SCSI status 4 */
+#define SIMODE0 (shpnt->io_port+0x10) /* SCSI interrupt mode 0 */
+#define SIMODE1 (shpnt->io_port+0x11) /* SCSI interrupt mode 1 */
+#define DMACNTRL0 (shpnt->io_port+0x12) /* DMA control 0 */
+#define DMACNTRL1 (shpnt->io_port+0x13) /* DMA control 1 */
+#define DMASTAT (shpnt->io_port+0x14) /* DMA status */
+#define FIFOSTAT (shpnt->io_port+0x15) /* FIFO status */
+#define DATAPORT (shpnt->io_port+0x16) /* DATA port */
+#define BRSTCNTRL (shpnt->io_port+0x18) /* burst control */
+#define PORTA (shpnt->io_port+0x1a) /* PORT A */
+#define PORTB (shpnt->io_port+0x1b) /* PORT B */
+#define REV (shpnt->io_port+0x1c) /* revision */
+#define STACK (shpnt->io_port+0x1d) /* stack */
+#define TEST (shpnt->io_port+0x1e) /* test register */
+
+/* used in aha152x_porttest */
+#define O_PORTA (0x1a) /* PORT A */
+#define O_PORTB (0x1b) /* PORT B */
+#define O_DMACNTRL1 (0x13) /* DMA control 1 */
+#define O_STACK (0x1d) /* stack */
+#define IO_RANGE 0x20
+
+/* bits and bitmasks to ports */
+
+/* SCSI sequence control */
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRESELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/* SCSI transfer control 0 */
+#define SCSIEN 0x80
+#define DMAEN 0x40
+#define CH1 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define CLRCH1 0x02
+
+/* SCSI transfer control 1 */
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18 /* mask */
+#define STIMESEL_ 3
+#define ENSTIMER 0x04
+#define BYTEALIGN 0x02
+
+/* SCSI signal IN */
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+/* SCSI Phases */
+#define P_MASK (MSGI|CDI|IOI)
+#define P_DATAO (0)
+#define P_DATAI (IOI)
+#define P_CMD (CDI)
+#define P_STATUS (CDI|IOI)
+#define P_MSGO (MSGI|CDI)
+#define P_MSGI (MSGI|CDI|IOI)
+
+/* SCSI signal OUT */
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+/* SCSI rate control */
+#define SXFR 0x70 /* mask */
+#define SXFR_ 4
+#define SOFS 0x0f /* mask */
+
+/* SCSI ID */
+#define OID 0x70
+#define OID_ 4
+#define TID 0x07
+
+/* SCSI transfer count */
+#define GETSTCNT() ( (GETPORT(STCNT2)<<16) \
+ + (GETPORT(STCNT1)<< 8) \
+ + GETPORT(STCNT0) )
+
+#define SETSTCNT(X) { SETPORT(STCNT2, ((X) & 0xFF0000) >> 16); \
+ SETPORT(STCNT1, ((X) & 0x00FF00) >> 8); \
+ SETPORT(STCNT0, ((X) & 0x0000FF) ); }
+
+/* SCSI interrupt status */
+#define TARGET 0x80
+#define SELDO 0x40
+#define SELDI 0x20
+#define SELINGO 0x10
+#define SWRAP 0x08
+#define SDONE 0x04
+#define SPIORDY 0x02
+#define DMADONE 0x01
+
+#define SETSDONE 0x80
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+#define CLRSDONE 0x04
+#define CLRSPIORDY 0x02
+#define CLRDMADONE 0x01
+
+/* SCSI status 1 */
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+#define CLRSELTIMO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+/* SCSI status 2 */
+#define SOFFSET 0x20
+#define SEMPTY 0x10
+#define SFULL 0x08
+#define SFCNT 0x07 /* mask */
+
+/* SCSI status 3 */
+#define SCSICNT 0xf0 /* mask */
+#define SCSICNT_ 4
+#define OFFCNT 0x0f /* mask */
+
+/* SCSI TEST control */
+#define SCTESTU 0x08
+#define SCTESTD 0x04
+#define STCTEST 0x01
+
+/* SCSI status 4 */
+#define SYNCERR 0x04
+#define FWERR 0x02
+#define FRERR 0x01
+
+#define CLRSYNCERR 0x04
+#define CLRFWERR 0x02
+#define CLRFRERR 0x01
+
+/* SCSI interrupt mode 0 */
+#define ENSELDO 0x40
+#define ENSELDI 0x20
+#define ENSELINGO 0x10
+#define ENSWRAP 0x08
+#define ENSDONE 0x04
+#define ENSPIORDY 0x02
+#define ENDMADONE 0x01
+
+/* SCSI interrupt mode 1 */
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/* DMA control 0 */
+#define ENDMA 0x80
+#define _8BIT 0x40
+#define DMA 0x20
+#define WRITE_READ 0x08
+#define INTEN 0x04
+#define RSTFIFO 0x02
+#define SWINT 0x01
+
+/* DMA control 1 */
+#define PWRDWN 0x80
+#define STK 0x07 /* mask */
+
+/* DMA status */
+#define ATDONE 0x80
+#define WORDRDY 0x40
+#define INTSTAT 0x20
+#define DFIFOFULL 0x10
+#define DFIFOEMP 0x08
+
+/* BURST control */
+#define BON 0xf0
+#define BOFF 0x0f
+
+/* TEST REGISTER */
+#define BOFFTMR 0x40
+#define BONTMR 0x20
+#define STCNTH 0x10
+#define STCNTM 0x08
+#define STCNTL 0x04
+#define SCSIBLK 0x02
+#define DMABLK 0x01
+
+/* On the AHA-152x board PORTA and PORTB contain
+ some information about the board's configuration. */
+typedef union {
+ struct {
+ unsigned reserved:2; /* reserved */
+ unsigned tardisc:1; /* Target disconnect: 0=disabled, 1=enabled */
+ unsigned syncneg:1; /* Initial sync neg: 0=disabled, 1=enabled */
+ unsigned msgclasses:2; /* Message classes
+ 0=#4
+ 1=#0, #1, #2, #3, #4
+ 2=#0, #3, #4
+ 3=#0, #4
+ */
+ unsigned boot:1; /* boot: 0=disabled, 1=enabled */
+ unsigned dma:1; /* Transfer mode: 0=PIO; 1=DMA */
+ unsigned id:3; /* SCSI-id */
+ unsigned irq:2; /* IRQ-Channel: 0,3=12, 1=10, 2=11 */
+ unsigned dmachan:2; /* DMA-Channel: 0=0, 1=5, 2=6, 3=7 */
+ unsigned parity:1; /* SCSI-parity: 1=enabled 0=disabled */
+ } fields;
+ unsigned short port;
+} aha152x_config ;
+
+#define cf_parity fields.parity
+#define cf_dmachan fields.dmachan
+#define cf_irq fields.irq
+#define cf_id fields.id
+#define cf_dma fields.dma
+#define cf_boot fields.boot
+#define cf_msgclasses fields.msgclasses
+#define cf_syncneg fields.syncneg
+#define cf_tardisc fields.tardisc
+#define cf_port port
+
+/* Some macros to manipulate ports and their bits */
+
+#define SETPORT(PORT, VAL) \
+ outb( (VAL), (PORT) )
+
+#define SETPORTP(PORT, VAL) \
+ outb_p( (VAL), (PORT) )
+
+#define SETPORTW(PORT, VAL) \
+ outw( (VAL), (PORT) )
+
+#define GETPORT(PORT) \
+ inb( PORT )
+
+#define GETPORTW(PORT) \
+ inw( PORT )
+
+#define SETBITS(PORT, BITS) \
+ outb( (inb(PORT) | (BITS)), (PORT) )
+
+#define CLRBITS(PORT, BITS) \
+ outb( (inb(PORT) & ~(BITS)), (PORT) )
+
+#define CLRSETBITS(PORT, CLR, SET) \
+ outb( (inb(PORT) & ~(CLR)) | (SET) , (PORT) )
+
+#define TESTHI(PORT, BITS) \
+ ((inb(PORT) & (BITS)) == BITS)
+
+#define TESTLO(PORT, BITS) \
+ ((inb(PORT) & (BITS)) == 0)
+
+#ifdef DEBUG_AHA152X
+enum {
+ debug_skipports =0x0001,
+ debug_queue =0x0002,
+ debug_intr =0x0004,
+ debug_selection =0x0008,
+ debug_msgo =0x0010,
+ debug_msgi =0x0020,
+ debug_status =0x0040,
+ debug_cmd =0x0080,
+ debug_datai =0x0100,
+ debug_datao =0x0200,
+ debug_abort =0x0400,
+ debug_done =0x0800,
+ debug_biosparam =0x1000,
+ debug_phases =0x2000,
+ debug_queues =0x4000,
+ debug_reset =0x8000,
+};
+#endif
+
+#endif /* _AHA152X_H */
diff --git a/i386/i386at/gpl/linux/scsi/aha1542.c b/i386/i386at/gpl/linux/scsi/aha1542.c
new file mode 100644
index 00000000..026292eb
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aha1542.c
@@ -0,0 +1,1323 @@
+/* $Id: aha1542.c,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ * linux/kernel/aha1542.c
+ *
+ * Copyright (C) 1992 Tommy Thorn
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Modified by Eric Youngdale
+ * Use request_irq and request_dma to help prevent unexpected conflicts
+ * Set up on-board DMA controller, such that we do not have to
+ * have the bios enabled to use the aha1542.
+ * Modified by David Gentzel
+ * Don't call request_dma if dma mask is 0 (for BusLogic BT-445S VL-Bus
+ * controller).
+ * Modified by Matti Aarnio
+ * Accept parameters from LILO cmd-line. -- 1-Oct-94
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+
+#include "aha1542.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_aha1542 = {
+ PROC_SCSI_AHA1542, 7, "aha1542",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/i386/i386at/gpl/linux/scsi/Attic/aha1542.c,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $";
+*/
+
+/* The adaptec can be configured for quite a number of addresses, but
+I generally do not want the card poking around at random. We allow
+two addresses - this allows people to use the Adaptec with a Midi
+card, which also used 0x330 -- can be overridden with LILO! */
+
+#define MAXBOARDS 2 /* Increase this and the sizes of the
+ arrays below, if you need more.. */
+
+static unsigned int bases[MAXBOARDS]={0x330, 0x334};
+
+/* set by aha1542_setup according to the command line */
+static int setup_called[MAXBOARDS] = {0,0};
+static int setup_buson[MAXBOARDS] = {0,0};
+static int setup_busoff[MAXBOARDS] = {0,0};
+static int setup_dmaspeed[MAXBOARDS] = {-1,-1};
+
+static char *setup_str[MAXBOARDS] = {(char *)NULL,(char *)NULL};
+
+/*
+ * LILO params: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]
+ *
+ * Where: <PORTBASE> is any of the valid AHA addresses:
+ * 0x130, 0x134, 0x230, 0x234, 0x330, 0x334
+ * <BUSON> is the time (in microsecs) that AHA spends on the AT-bus
+ * when transferring data. 1542A power-on default is 11us,
+ * valid values are in range: 2..15 (decimal)
+ * <BUSOFF> is the time that AHA spends OFF THE BUS after while
+ * it is transferring data (not to monopolize the bus).
+ * Power-on default is 4us, valid range: 1..64 microseconds.
+ * <DMASPEED> Default is jumper selected (1542A: on the J1),
+ * but experimenter can alter it with this.
+ * Valid values: 5, 6, 7, 8, 10 (MB/s)
+ * Factory default is 5 MB/s.
+ */
+
+
+/* The DMA-Controller. We need to fool with this because we want to
+ be able to use the aha1542 without having to have the bios enabled */
+#define DMA_MODE_REG 0xd6
+#define DMA_MASK_REG 0xd4
+#define CASCADE 0xc0
+
+#define BIOS_TRANSLATION_1632 0 /* Used by some old 1542A boards */
+#define BIOS_TRANSLATION_6432 1 /* Default case these days */
+#define BIOS_TRANSLATION_25563 2 /* Big disk case */
+
+struct aha1542_hostdata{
+ /* This will effectively start both of them at the first mailbox */
+ int bios_translation; /* Mapping bios uses - for compatibility */
+ int aha1542_last_mbi_used;
+ int aha1542_last_mbo_used;
+ Scsi_Cmnd * SCint[AHA1542_MAILBOXES];
+ struct mailbox mb[2*AHA1542_MAILBOXES];
+ struct ccb ccb[AHA1542_MAILBOXES];
+};
+
+#define HOSTDATA(host) ((struct aha1542_hostdata *) &host->hostdata)
+
+static struct Scsi_Host * aha_host[7] = {NULL,}; /* One for each IRQ level (9-15) */
+
+
+
+
+#define WAITnexttimeout 3000000
+
+static void setup_mailboxes(int base_io, struct Scsi_Host * shpnt);
+static int aha1542_restart(struct Scsi_Host * shost);
+
+#define aha1542_intr_reset(base) outb(IRST, CONTROL(base))
+
+#define WAIT(port, mask, allof, noneof) \
+ { register WAITbits; \
+ register WAITtimeout = WAITnexttimeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+/* Similar to WAIT, except we use the udelay call to regulate the
+ amount of time we wait. */
+#define WAITd(port, mask, allof, noneof, timeout) \
+ { register WAITbits; \
+ register WAITtimeout = timeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ udelay(1000); \
+ if (--WAITtimeout == 0) goto fail; \
+ } \
+ }
+
+static void aha1542_stat(void)
+{
+/* int s = inb(STATUS), i = inb(INTRFLAGS);
+ printk("status=%x intrflags=%x\n", s, i, WAITnexttimeout-WAITtimeout); */
+}
+
+/* This is a bit complicated, but we need to make sure that an interrupt
+ routine does not send something out while we are in the middle of this.
+ Fortunately, it is only at boot time that multi-byte messages
+ are ever sent. */
+static int aha1542_out(unsigned int base, unchar *cmdp, int len)
+{
+ unsigned long flags = 0;
+
+ save_flags(flags);
+ if(len == 1) {
+ while(1==1){
+ WAIT(STATUS(base), CDF, 0, CDF);
+ cli();
+ if(inb(STATUS(base)) & CDF) {restore_flags(flags); continue;}
+ outb(*cmdp, DATA(base));
+ restore_flags(flags);
+ return 0;
+ }
+ } else {
+ cli();
+ while (len--)
+ {
+ WAIT(STATUS(base), CDF, 0, CDF);
+ outb(*cmdp++, DATA(base));
+ }
+ restore_flags(flags);
+ }
+ return 0;
+ fail:
+ restore_flags(flags);
+ printk("aha1542_out failed(%d): ", len+1); aha1542_stat();
+ return 1;
+}
+
+/* Only used at boot time, so we do not need to worry about latency as much
+ here */
+static int aha1542_in(unsigned int base, unchar *cmdp, int len)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ while (len--)
+ {
+ WAIT(STATUS(base), DF, DF, 0);
+ *cmdp++ = inb(DATA(base));
+ }
+ restore_flags(flags);
+ return 0;
+ fail:
+ restore_flags(flags);
+ printk("aha1542_in failed(%d): ", len+1); aha1542_stat();
+ return 1;
+}
+
+/* Similar to aha1542_in, except that we wait a very short period of time.
+ We use this if we know the board is alive and awake, but we are not sure
+ if the board will respond the the command we are about to send or not */
+static int aha1542_in1(unsigned int base, unchar *cmdp, int len)
+{
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ while (len--)
+ {
+ WAITd(STATUS(base), DF, DF, 0, 100);
+ *cmdp++ = inb(DATA(base));
+ }
+ restore_flags(flags);
+ return 0;
+ fail:
+ restore_flags(flags);
+ return 1;
+}
+
+static int makecode(unsigned hosterr, unsigned scsierr)
+{
+ switch (hosterr) {
+ case 0x0:
+ case 0xa: /* Linked command complete without error and linked normally */
+ case 0xb: /* Linked command complete without error, interrupt generated */
+ hosterr = 0;
+ break;
+
+ case 0x11: /* Selection time out-The initiator selection or target
+ reselection was not complete within the SCSI Time out period */
+ hosterr = DID_TIME_OUT;
+ break;
+
+ case 0x12: /* Data overrun/underrun-The target attempted to transfer more data
+ than was allocated by the Data Length field or the sum of the
+ Scatter / Gather Data Length fields. */
+
+ case 0x13: /* Unexpected bus free-The target dropped the SCSI BSY at an unexpected time. */
+
+ case 0x15: /* MBO command was not 00, 01 or 02-The first byte of the CB was
+ invalid. This usually indicates a software failure. */
+
+ case 0x16: /* Invalid CCB Operation Code-The first byte of the CCB was invalid.
+ This usually indicates a software failure. */
+
+ case 0x17: /* Linked CCB does not have the same LUN-A subsequent CCB of a set
+ of linked CCB's does not specify the same logical unit number as
+ the first. */
+ case 0x18: /* Invalid Target Direction received from Host-The direction of a
+ Target Mode CCB was invalid. */
+
+ case 0x19: /* Duplicate CCB Received in Target Mode-More than once CCB was
+ received to service data transfer between the same target LUN
+ and initiator SCSI ID in the same direction. */
+
+ case 0x1a: /* Invalid CCB or Segment List Parameter-A segment list with a zero
+ length segment or invalid segment list boundaries was received.
+ A CCB parameter was invalid. */
+ DEB(printk("Aha1542: %x %x\n", hosterr, scsierr));
+ hosterr = DID_ERROR; /* Couldn't find any better */
+ break;
+
+ case 0x14: /* Target bus phase sequence failure-An invalid bus phase or bus
+ phase sequence was requested by the target. The host adapter
+ will generate a SCSI Reset Condition, notifying the host with
+ a SCRD interrupt */
+ hosterr = DID_RESET;
+ break;
+ default:
+ printk("makecode: unknown hoststatus %x\n", hosterr);
+ break;
+ }
+ return scsierr|(hosterr << 16);
+}
+
+static int aha1542_test_port(int bse, struct Scsi_Host * shpnt)
+{
+ int i;
+ unchar inquiry_cmd[] = {CMD_INQUIRY };
+ unchar inquiry_result[4];
+ unchar *cmdp;
+ int len;
+ volatile int debug = 0;
+
+ /* Quick and dirty test for presence of the card. */
+ if(inb(STATUS(bse)) == 0xff) return 0;
+
+ /* Reset the adapter. I ought to make a hard reset, but it's not really necessary */
+
+ /* DEB(printk("aha1542_test_port called \n")); */
+
+ /* In case some other card was probing here, reset interrupts */
+ aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+
+ outb(SRST|IRST/*|SCRST*/, CONTROL(bse));
+
+ i = jiffies + 2;
+ while (i>jiffies); /* Wait a little bit for things to settle down. */
+
+ debug = 1;
+ /* Expect INIT and IDLE, any of the others are bad */
+ WAIT(STATUS(bse), STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF);
+
+ debug = 2;
+ /* Shouldn't have generated any interrupts during reset */
+ if (inb(INTRFLAGS(bse))&INTRMASK) goto fail;
+
+
+ /* Perform a host adapter inquiry instead so we do not need to set
+ up the mailboxes ahead of time */
+
+ aha1542_out(bse, inquiry_cmd, 1);
+
+ debug = 3;
+ len = 4;
+ cmdp = &inquiry_result[0];
+
+ while (len--)
+ {
+ WAIT(STATUS(bse), DF, DF, 0);
+ *cmdp++ = inb(DATA(bse));
+ }
+
+ debug = 8;
+ /* Reading port should reset DF */
+ if (inb(STATUS(bse)) & DF) goto fail;
+
+ debug = 9;
+ /* When HACC, command is completed, and we're though testing */
+ WAIT(INTRFLAGS(bse), HACC, HACC, 0);
+ /* now initialize adapter */
+
+ debug = 10;
+ /* Clear interrupts */
+ outb(IRST, CONTROL(bse));
+
+ debug = 11;
+
+ return debug; /* 1 = ok */
+ fail:
+ return 0; /* 0 = not ok */
+}
+
+/* A "high" level interrupt handler */
+static void aha1542_intr_handle(int irq, struct pt_regs *regs)
+{
+ void (*my_done)(Scsi_Cmnd *) = NULL;
+ int errstatus, mbi, mbo, mbistatus;
+ int number_serviced;
+ unsigned int flags;
+ struct Scsi_Host * shost;
+ Scsi_Cmnd * SCtmp;
+ int flag;
+ int needs_restart;
+ struct mailbox * mb;
+ struct ccb *ccb;
+
+ shost = aha_host[irq - 9];
+ if(!shost) panic("Splunge!");
+
+ mb = HOSTDATA(shost)->mb;
+ ccb = HOSTDATA(shost)->ccb;
+
+#ifdef DEBUG
+ {
+ flag = inb(INTRFLAGS(shost->io_port));
+ printk("aha1542_intr_handle: ");
+ if (!(flag&ANYINTR)) printk("no interrupt?");
+ if (flag&MBIF) printk("MBIF ");
+ if (flag&MBOA) printk("MBOF ");
+ if (flag&HACC) printk("HACC ");
+ if (flag&SCRD) printk("SCRD ");
+ printk("status %02x\n", inb(STATUS(shost->io_port)));
+ };
+#endif
+ number_serviced = 0;
+ needs_restart = 0;
+
+ while(1==1){
+ flag = inb(INTRFLAGS(shost->io_port));
+
+ /* Check for unusual interrupts. If any of these happen, we should
+ probably do something special, but for now just printing a message
+ is sufficient. A SCSI reset detected is something that we really
+ need to deal with in some way. */
+ if (flag & ~MBIF) {
+ if (flag&MBOA) printk("MBOF ");
+ if (flag&HACC) printk("HACC ");
+ if (flag&SCRD) {
+ needs_restart = 1;
+ printk("SCRD ");
+ }
+ }
+
+ aha1542_intr_reset(shost->io_port);
+
+ save_flags(flags);
+ cli();
+ mbi = HOSTDATA(shost)->aha1542_last_mbi_used + 1;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+
+ do{
+ if(mb[mbi].status != 0) break;
+ mbi++;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+ } while (mbi != HOSTDATA(shost)->aha1542_last_mbi_used);
+
+ if(mb[mbi].status == 0){
+ restore_flags(flags);
+ /* Hmm, no mail. Must have read it the last time around */
+ if (!number_serviced && !needs_restart)
+ printk("aha1542.c: interrupt received, but no mail.\n");
+ /* We detected a reset. Restart all pending commands for
+ devices that use the hard reset option */
+ if(needs_restart) aha1542_restart(shost);
+ return;
+ };
+
+ mbo = (scsi2int(mb[mbi].ccbptr) - ((unsigned int) &ccb[0])) / sizeof(struct ccb);
+ mbistatus = mb[mbi].status;
+ mb[mbi].status = 0;
+ HOSTDATA(shost)->aha1542_last_mbi_used = mbi;
+ restore_flags(flags);
+
+#ifdef DEBUG
+ {
+ if (ccb[mbo].tarstat|ccb[mbo].hastat)
+ printk("aha1542_command: returning %x (status %d)\n",
+ ccb[mbo].tarstat + ((int) ccb[mbo].hastat << 16), mb[mbi].status);
+ };
+#endif
+
+ if(mbistatus == 3) continue; /* Aborted command not found */
+
+#ifdef DEBUG
+ printk("...done %d %d\n",mbo, mbi);
+#endif
+
+ SCtmp = HOSTDATA(shost)->SCint[mbo];
+
+ if (!SCtmp || !SCtmp->scsi_done) {
+ printk("aha1542_intr_handle: Unexpected interrupt\n");
+ printk("tarstat=%x, hastat=%x idlun=%x ccb#=%d \n", ccb[mbo].tarstat,
+ ccb[mbo].hastat, ccb[mbo].idlun, mbo);
+ return;
+ }
+
+ my_done = SCtmp->scsi_done;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+
+ /* Fetch the sense data, and tuck it away, in the required slot. The
+ Adaptec automatically fetches it, and there is no guarantee that
+ we will still have it in the cdb when we come back */
+ if (ccb[mbo].tarstat == 2)
+ memcpy(SCtmp->sense_buffer, &ccb[mbo].cdb[ccb[mbo].cdblen],
+ sizeof(SCtmp->sense_buffer));
+
+
+ /* is there mail :-) */
+
+ /* more error checking left out here */
+ if (mbistatus != 1)
+ /* This is surely wrong, but I don't know what's right */
+ errstatus = makecode(ccb[mbo].hastat, ccb[mbo].tarstat);
+ else
+ errstatus = 0;
+
+#ifdef DEBUG
+ if(errstatus) printk("(aha1542 error:%x %x %x) ",errstatus,
+ ccb[mbo].hastat, ccb[mbo].tarstat);
+#endif
+
+ if (ccb[mbo].tarstat == 2) {
+#ifdef DEBUG
+ int i;
+#endif
+ DEB(printk("aha1542_intr_handle: sense:"));
+#ifdef DEBUG
+ for (i = 0; i < 12; i++)
+ printk("%02x ", ccb[mbo].cdb[ccb[mbo].cdblen+i]);
+ printk("\n");
+#endif
+ /*
+ DEB(printk("aha1542_intr_handle: buf:"));
+ for (i = 0; i < bufflen; i++)
+ printk("%02x ", ((unchar *)buff)[i]);
+ printk("\n");
+ */
+ }
+ DEB(if (errstatus) printk("aha1542_intr_handle: returning %6x\n", errstatus));
+ SCtmp->result = errstatus;
+ HOSTDATA(shost)->SCint[mbo] = NULL; /* This effectively frees up the mailbox slot, as
+ far as queuecommand is concerned */
+ my_done(SCtmp);
+ number_serviced++;
+ };
+}
+
+int aha1542_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar ahacmd = CMD_START_SCSI;
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->target;
+ unchar lun = SCpnt->lun;
+ unsigned long flags;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ int mbo;
+ struct mailbox * mb;
+ struct ccb *ccb;
+
+ DEB(int i);
+
+ mb = HOSTDATA(SCpnt->host)->mb;
+ ccb = HOSTDATA(SCpnt->host)->ccb;
+
+ DEB(if (target > 1) {
+ SCpnt->result = DID_TIME_OUT << 16;
+ done(SCpnt); return 0;});
+
+ if(*cmd == REQUEST_SENSE){
+#ifndef DEBUG
+ if (bufflen != sizeof(SCpnt->sense_buffer)) {
+ printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
+ };
+#endif
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ };
+
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd+2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd+2);
+ else
+ i = -1;
+ if (done)
+ printk("aha1542_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ else
+ printk("aha1542_command: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ aha1542_stat();
+ printk("aha1542_queuecommand: dumping scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
+ printk("\n");
+ if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ return 0; /* we are still testing, so *don't* write */
+#endif
+/* Use the outgoing mailboxes in a round-robin fashion, because this
+ is how the host adapter will scan for them */
+
+ save_flags(flags);
+ cli();
+ mbo = HOSTDATA(SCpnt->host)->aha1542_last_mbo_used + 1;
+ if (mbo >= AHA1542_MAILBOXES) mbo = 0;
+
+ do{
+ if(mb[mbo].status == 0 && HOSTDATA(SCpnt->host)->SCint[mbo] == NULL)
+ break;
+ mbo++;
+ if (mbo >= AHA1542_MAILBOXES) mbo = 0;
+ } while (mbo != HOSTDATA(SCpnt->host)->aha1542_last_mbo_used);
+
+ if(mb[mbo].status || HOSTDATA(SCpnt->host)->SCint[mbo])
+ panic("Unable to find empty mailbox for aha1542.\n");
+
+ HOSTDATA(SCpnt->host)->SCint[mbo] = SCpnt; /* This will effectively prevent someone else from
+ screwing with this cdb. */
+
+ HOSTDATA(SCpnt->host)->aha1542_last_mbo_used = mbo;
+ restore_flags(flags);
+
+#ifdef DEBUG
+ printk("Sending command (%d %x)...",mbo, done);
+#endif
+
+ any2scsi(mb[mbo].ccbptr, &ccb[mbo]); /* This gets trashed for some reason*/
+
+ memset(&ccb[mbo], 0, sizeof(struct ccb));
+
+ ccb[mbo].cdblen = SCpnt->cmd_len;
+
+ direction = 0;
+ if (*cmd == READ_10 || *cmd == READ_6)
+ direction = 8;
+ else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 16;
+
+ memcpy(ccb[mbo].cdb, cmd, ccb[mbo].cdblen);
+
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ struct chain * cptr;
+#ifdef DEBUG
+ unsigned char * ptr;
+#endif
+ int i;
+ ccb[mbo].op = 2; /* SCSI Initiator Command w/scatter-gather*/
+ SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ cptr = (struct chain *) SCpnt->host_scribble;
+ if (cptr == NULL) panic("aha1542.c: unable to allocate DMA memory\n");
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if(sgpnt[i].length == 0 || SCpnt->use_sg > 16 ||
+ (((int)sgpnt[i].address) & 1) || (sgpnt[i].length & 1)){
+ unsigned char * ptr;
+ printk("Bad segment list supplied to aha1542.c (%d, %d)\n",SCpnt->use_sg,i);
+ for(i=0;i<SCpnt->use_sg;i++){
+ printk("%d: %x %x %d\n",i,(unsigned int) sgpnt[i].address, (unsigned int) sgpnt[i].alt_address,
+ sgpnt[i].length);
+ };
+ printk("cptr %x: ",(unsigned int) cptr);
+ ptr = (unsigned char *) &cptr[i];
+ for(i=0;i<18;i++) printk("%02x ", ptr[i]);
+ panic("Foooooooood fight!");
+ };
+ any2scsi(cptr[i].dataptr, sgpnt[i].address);
+ if(((unsigned int) sgpnt[i].address) & 0xff000000) goto baddma;
+ any2scsi(cptr[i].datalen, sgpnt[i].length);
+ };
+ any2scsi(ccb[mbo].datalen, SCpnt->use_sg * sizeof(struct chain));
+ any2scsi(ccb[mbo].dataptr, cptr);
+#ifdef DEBUG
+ printk("cptr %x: ",cptr);
+ ptr = (unsigned char *) cptr;
+ for(i=0;i<18;i++) printk("%02x ", ptr[i]);
+#endif
+ } else {
+ ccb[mbo].op = 0; /* SCSI Initiator Command */
+ SCpnt->host_scribble = NULL;
+ any2scsi(ccb[mbo].datalen, bufflen);
+ if(((unsigned int) buff & 0xff000000)) goto baddma;
+ any2scsi(ccb[mbo].dataptr, buff);
+ };
+ ccb[mbo].idlun = (target&7)<<5 | direction | (lun & 7); /*SCSI Target Id*/
+ ccb[mbo].rsalen = 12;
+ ccb[mbo].linkptr[0] = ccb[mbo].linkptr[1] = ccb[mbo].linkptr[2] = 0;
+ ccb[mbo].commlinkid = 0;
+
+#ifdef DEBUG
+ { int i;
+ printk("aha1542_command: sending.. ");
+ for (i = 0; i < sizeof(ccb[mbo])-10; i++)
+ printk("%02x ", ((unchar *)&ccb[mbo])[i]);
+ };
+#endif
+
+ if (done) {
+ DEB(printk("aha1542_queuecommand: now waiting for interrupt "); aha1542_stat());
+ SCpnt->scsi_done = done;
+ mb[mbo].status = 1;
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
+ DEB(aha1542_stat());
+ }
+ else
+ printk("aha1542_queuecommand: done can't be NULL\n");
+
+ return 0;
+ baddma:
+ panic("Buffer at address > 16Mb used for 1542B");
+}
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ SCpnt->SCp.Status++;
+}
+
+int aha1542_command(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("aha1542_command: ..calling aha1542_queuecommand\n"));
+
+ aha1542_queuecommand(SCpnt, internal_done);
+
+ SCpnt->SCp.Status = 0;
+ while (!SCpnt->SCp.Status)
+ barrier();
+ return SCpnt->result;
+}
+
+/* Initialize mailboxes */
+static void setup_mailboxes(int bse, struct Scsi_Host * shpnt)
+{
+ int i;
+ struct mailbox * mb;
+ struct ccb *ccb;
+
+ unchar cmd[5] = {CMD_MBINIT, AHA1542_MAILBOXES, 0, 0, 0};
+
+ mb = HOSTDATA(shpnt)->mb;
+ ccb = HOSTDATA(shpnt)->ccb;
+
+ for(i=0; i<AHA1542_MAILBOXES; i++){
+ mb[i].status = mb[AHA1542_MAILBOXES+i].status = 0;
+ any2scsi(mb[i].ccbptr, &ccb[i]);
+ };
+ aha1542_intr_reset(bse); /* reset interrupts, so they don't block */
+ any2scsi((cmd+2), mb);
+ aha1542_out(bse, cmd, 5);
+ WAIT(INTRFLAGS(bse), INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: failed setting up mailboxes\n");
+ }
+ aha1542_intr_reset(bse);
+}
+
+static int aha1542_getconfig(int base_io, unsigned char * irq_level, unsigned char * dma_chan, unsigned char * scsi_id)
+{
+ unchar inquiry_cmd[] = {CMD_RETCONF };
+ unchar inquiry_result[3];
+ int i;
+ i = inb(STATUS(base_io));
+ if (i & DF) {
+ i = inb(DATA(base_io));
+ };
+ aha1542_out(base_io, inquiry_cmd, 1);
+ aha1542_in(base_io, inquiry_result, 3);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: query board settings\n");
+ }
+ aha1542_intr_reset(base_io);
+ switch(inquiry_result[0]){
+ case 0x80:
+ *dma_chan = 7;
+ break;
+ case 0x40:
+ *dma_chan = 6;
+ break;
+ case 0x20:
+ *dma_chan = 5;
+ break;
+ case 0x01:
+ printk("DMA priority 0 not available for Adaptec driver\n");
+ return -1;
+ case 0:
+ /* This means that the adapter, although Adaptec 1542 compatible, doesn't use a DMA channel.
+ Currently only aware of the BusLogic BT-445S VL-Bus adapter which needs this. */
+ *dma_chan = 0xFF;
+ break;
+ default:
+ printk("Unable to determine Adaptec DMA priority. Disabling board\n");
+ return -1;
+ };
+ switch(inquiry_result[1]){
+ case 0x40:
+ *irq_level = 15;
+ break;
+ case 0x20:
+ *irq_level = 14;
+ break;
+ case 0x8:
+ *irq_level = 12;
+ break;
+ case 0x4:
+ *irq_level = 11;
+ break;
+ case 0x2:
+ *irq_level = 10;
+ break;
+ case 0x1:
+ *irq_level = 9;
+ break;
+ default:
+ printk("Unable to determine Adaptec IRQ level. Disabling board\n");
+ return -1;
+ };
+ *scsi_id=inquiry_result[2] & 7;
+ return 0;
+}
+
+/* This function should only be called for 1542C boards - we can detect
+ the special firmware settings and unlock the board */
+
+static int aha1542_mbenable(int base)
+{
+ static unchar mbenable_cmd[3];
+ static unchar mbenable_result[2];
+ int retval;
+
+ retval = BIOS_TRANSLATION_6432;
+
+ mbenable_cmd[0]=CMD_EXTBIOS;
+ aha1542_out(base,mbenable_cmd,1);
+ if(aha1542_in1(base,mbenable_result,2))
+ return retval;
+ WAITd(INTRFLAGS(base),INTRMASK,HACC,0,100);
+ aha1542_intr_reset(base);
+
+ if ((mbenable_result[0] & 0x08) || mbenable_result[1]) {
+ mbenable_cmd[0]=CMD_MBENABLE;
+ mbenable_cmd[1]=0;
+ mbenable_cmd[2]=mbenable_result[1];
+ if(mbenable_result[1] & 1) retval = BIOS_TRANSLATION_25563;
+ aha1542_out(base,mbenable_cmd,3);
+ WAIT(INTRFLAGS(base),INTRMASK,HACC,0);
+ };
+ while(0) {
+fail:
+ printk("aha1542_mbenable: Mailbox init failed\n");
+ }
+aha1542_intr_reset(base);
+return retval;
+}
+
+/* Query the board to find out if it is a 1542 or a 1740, or whatever. */
+static int aha1542_query(int base_io, int * transl)
+{
+ unchar inquiry_cmd[] = {CMD_INQUIRY };
+ unchar inquiry_result[4];
+ int i;
+ i = inb(STATUS(base_io));
+ if (i & DF) {
+ i = inb(DATA(base_io));
+ };
+ aha1542_out(base_io, inquiry_cmd, 1);
+ aha1542_in(base_io, inquiry_result, 4);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ while (0) {
+ fail:
+ printk("aha1542_detect: query card type\n");
+ }
+ aha1542_intr_reset(base_io);
+
+ *transl = BIOS_TRANSLATION_6432; /* Default case */
+
+/* For an AHA1740 series board, we ignore the board since there is a
+ hardware bug which can lead to wrong blocks being returned if the board
+ is operating in the 1542 emulation mode. Since there is an extended mode
+ driver, we simply ignore the board and let the 1740 driver pick it up.
+*/
+
+ if (inquiry_result[0] == 0x43) {
+ printk("aha1542.c: Emulation mode not supported for AHA 174N hardware.\n");
+ return 1;
+ };
+
+ /* Always call this - boards that do not support extended bios translation
+ will ignore the command, and we will set the proper default */
+
+ *transl = aha1542_mbenable(base_io);
+
+ return 0;
+}
+
+/* called from init/main.c */
+void aha1542_setup( char *str, int *ints)
+{
+ const char *ahausage = "aha1542: usage: aha1542=<PORTBASE>[,<BUSON>,<BUSOFF>[,<DMASPEED>]]\n";
+ static int setup_idx = 0;
+ int setup_portbase;
+
+ if(setup_idx >= MAXBOARDS)
+ {
+ printk("aha1542: aha1542_setup called too many times! Bad LILO params ?\n");
+ printk(" Entryline 1: %s\n",setup_str[0]);
+ printk(" Entryline 2: %s\n",setup_str[1]);
+ printk(" This line: %s\n",str);
+ return;
+ }
+ if (ints[0] < 1 || ints[0] > 4)
+ {
+ printk("aha1542: %s\n", str );
+ printk(ahausage);
+ printk("aha1542: Wrong parameters may cause system malfunction.. We try anyway..\n");
+ }
+
+ setup_called[setup_idx]=ints[0];
+ setup_str[setup_idx]=str;
+
+ setup_portbase = ints[0] >= 1 ? ints[1] : 0; /* Preserve the default value.. */
+ setup_buson [setup_idx] = ints[0] >= 2 ? ints[2] : 7;
+ setup_busoff [setup_idx] = ints[0] >= 3 ? ints[3] : 5;
+ if (ints[0] >= 4) {
+ int atbt = -1;
+ switch (ints[4]) {
+ case 5:
+ atbt = 0x00;
+ break;
+ case 6:
+ atbt = 0x04;
+ break;
+ case 7:
+ atbt = 0x01;
+ break;
+ case 8:
+ atbt = 0x02;
+ break;
+ case 10:
+ atbt = 0x03;
+ break;
+ default:
+ printk("aha1542: %s\n", str );
+ printk(ahausage);
+ printk("aha1542: Valid values for DMASPEED are 5-8, 10 MB/s. Using jumper defaults.\n");
+ break;
+ }
+ setup_dmaspeed[setup_idx] = atbt;
+ }
+
+ if (setup_portbase != 0)
+ bases[setup_idx] = setup_portbase;
+
+ ++setup_idx;
+}
+
+/* return non-zero on detection */
+int aha1542_detect(Scsi_Host_Template * tpnt)
+{
+ unsigned char dma_chan;
+ unsigned char irq_level;
+ unsigned char scsi_id;
+ unsigned long flags;
+ unsigned int base_io;
+ int trans;
+ struct Scsi_Host * shpnt = NULL;
+ int count = 0;
+ int indx;
+
+ DEB(printk("aha1542_detect: \n"));
+
+ tpnt->proc_dir = &proc_scsi_aha1542;
+
+ for(indx = 0; indx < sizeof(bases)/sizeof(bases[0]); indx++)
+ if(bases[indx] != 0 && !check_region(bases[indx], 4)) {
+ shpnt = scsi_register(tpnt,
+ sizeof(struct aha1542_hostdata));
+
+ /* For now we do this - until kmalloc is more intelligent
+ we are resigned to stupid hacks like this */
+ if ((unsigned int) shpnt > 0xffffff) {
+ printk("Invalid address for shpnt with 1542.\n");
+ goto unregister;
+ }
+
+ if(!aha1542_test_port(bases[indx], shpnt)) goto unregister;
+
+
+ base_io = bases[indx];
+
+ /* Set the Bus on/off-times as not to ruin floppy performance */
+ {
+ unchar oncmd[] = {CMD_BUSON_TIME, 7};
+ unchar offcmd[] = {CMD_BUSOFF_TIME, 5};
+
+ if(setup_called[indx])
+ {
+ oncmd[1] = setup_buson[indx];
+ offcmd[1] = setup_busoff[indx];
+ }
+
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, oncmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, offcmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ if (setup_dmaspeed[indx] >= 0)
+ {
+ unchar dmacmd[] = {CMD_DMASPEED, 0};
+ dmacmd[1] = setup_dmaspeed[indx];
+ aha1542_intr_reset(base_io);
+ aha1542_out(base_io, dmacmd, 2);
+ WAIT(INTRFLAGS(base_io), INTRMASK, HACC, 0);
+ }
+ while (0) {
+ fail:
+ printk("aha1542_detect: setting bus on/off-time failed\n");
+ }
+ aha1542_intr_reset(base_io);
+ }
+ if(aha1542_query(base_io, &trans)) goto unregister;
+
+ if (aha1542_getconfig(base_io, &irq_level, &dma_chan, &scsi_id) == -1) goto unregister;
+
+ printk("Configuring Adaptec (SCSI-ID %d) at IO:%x, IRQ %d", scsi_id, base_io, irq_level);
+ if (dma_chan != 0xFF)
+ printk(", DMA priority %d", dma_chan);
+ printk("\n");
+
+ DEB(aha1542_stat());
+ setup_mailboxes(base_io, shpnt);
+
+ DEB(aha1542_stat());
+
+ DEB(printk("aha1542_detect: enable interrupt channel %d\n", irq_level));
+ save_flags(flags);
+ cli();
+ if (request_irq(irq_level,aha1542_intr_handle, 0, "aha1542")) {
+ printk("Unable to allocate IRQ for adaptec controller.\n");
+ goto unregister;
+ }
+
+ if (dma_chan != 0xFF) {
+ if (request_dma(dma_chan,"aha1542")) {
+ printk("Unable to allocate DMA channel for Adaptec.\n");
+ free_irq(irq_level);
+ goto unregister;
+ }
+
+ if (dma_chan >= 5) {
+ outb((dma_chan - 4) | CASCADE, DMA_MODE_REG);
+ outb(dma_chan - 4, DMA_MASK_REG);
+ }
+ }
+ aha_host[irq_level - 9] = shpnt;
+ shpnt->this_id = scsi_id;
+ shpnt->unique_id = base_io;
+ shpnt->io_port = base_io;
+ shpnt->n_io_port = 4; /* Number of bytes of I/O space used */
+ shpnt->dma_channel = dma_chan;
+ shpnt->irq = irq_level;
+ HOSTDATA(shpnt)->bios_translation = trans;
+ if(trans == 2)
+ printk("aha1542.c: Using extended bios translation\n");
+ HOSTDATA(shpnt)->aha1542_last_mbi_used = (2*AHA1542_MAILBOXES - 1);
+ HOSTDATA(shpnt)->aha1542_last_mbo_used = (AHA1542_MAILBOXES - 1);
+ memset(HOSTDATA(shpnt)->SCint, 0, sizeof(HOSTDATA(shpnt)->SCint));
+ restore_flags(flags);
+#if 0
+ DEB(printk(" *** READ CAPACITY ***\n"));
+
+ {
+ unchar buf[8];
+ static unchar cmd[] = { READ_CAPACITY, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+ int i;
+
+ for (i = 0; i < sizeof(buf); ++i) buf[i] = 0x87;
+ for (i = 0; i < 2; ++i)
+ if (!aha1542_command(i, cmd, buf, sizeof(buf))) {
+ printk("aha_detect: LU %d sector_size %d device_size %d\n",
+ i, xscsi2int(buf+4), xscsi2int(buf));
+ }
+ }
+
+ DEB(printk(" *** NOW RUNNING MY OWN TEST *** \n"));
+
+ for (i = 0; i < 4; ++i)
+ {
+ unsigned char cmd[10];
+ static buffer[512];
+
+ cmd[0] = READ_10;
+ cmd[1] = 0;
+ xany2scsi(cmd+2, i);
+ cmd[6] = 0;
+ cmd[7] = 0;
+ cmd[8] = 1;
+ cmd[9] = 0;
+ aha1542_command(0, cmd, buffer, 512);
+ }
+#endif
+ request_region(bases[indx], 4,"aha1542"); /* Register the IO ports that we use */
+ count++;
+ continue;
+ unregister:
+ scsi_unregister(shpnt);
+ continue;
+
+ };
+
+ return count;
+}
+
+static int aha1542_restart(struct Scsi_Host * shost)
+{
+ int i;
+ int count = 0;
+#if 0
+ unchar ahacmd = CMD_START_SCSI;
+#endif
+
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(shost)->SCint[i] &&
+ !(HOSTDATA(shost)->SCint[i]->device->soft_reset))
+ {
+#if 0
+ HOSTDATA(shost)->mb[i].status = 1; /* Indicate ready to restart... */
+#endif
+ count++;
+ }
+
+ printk("Potential to restart %d stalled commands...\n", count);
+#if 0
+ /* start scsi command */
+ if (count) aha1542_out(shost->io_port, &ahacmd, 1);
+#endif
+ return 0;
+}
+
+/* The abort command does not leave the device in a clean state where
+ it is available to be used again. Until this gets worked out, we will
+ leave it commented out. */
+
+int aha1542_abort(Scsi_Cmnd * SCpnt)
+{
+#if 0
+ unchar ahacmd = CMD_START_SCSI;
+ unsigned long flags;
+ struct mailbox * mb;
+ int mbi, mbo, i;
+
+ printk("In aha1542_abort: %x %x\n",
+ inb(STATUS(SCpnt->host->io_port)),
+ inb(INTRFLAGS(SCpnt->host->io_port)));
+
+ save_flags(flags);
+ cli();
+ mb = HOSTDATA(SCpnt->host)->mb;
+ mbi = HOSTDATA(SCpnt->host)->aha1542_last_mbi_used + 1;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+
+ do{
+ if(mb[mbi].status != 0) break;
+ mbi++;
+ if (mbi >= 2*AHA1542_MAILBOXES) mbi = AHA1542_MAILBOXES;
+ } while (mbi != HOSTDATA(SCpnt->host)->aha1542_last_mbi_used);
+ restore_flags(flags);
+
+ if(mb[mbi].status) {
+ printk("Lost interrupt discovered on irq %d - attempting to recover\n",
+ SCpnt->host->irq);
+ aha1542_intr_handle(SCpnt->host->irq, NULL);
+ return 0;
+ }
+
+ /* OK, no lost interrupt. Try looking to see how many pending commands
+ we think we have. */
+
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i])
+ {
+ if(HOSTDATA(SCpnt->host)->SCint[i] == SCpnt) {
+ printk("Timed out command pending for %s\n",
+ kdevname(SCpnt->request.rq_dev));
+ if (HOSTDATA(SCpnt->host)->mb[i].status) {
+ printk("OGMB still full - restarting\n");
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
+ };
+ } else
+ printk("Other pending command %s\n",
+ kdevname(SCpnt->request.rq_dev));
+ }
+
+#endif
+
+ DEB(printk("aha1542_abort\n"));
+#if 0
+ save_flags(flags);
+ cli();
+ for(mbo = 0; mbo < AHA1542_MAILBOXES; mbo++)
+ if (SCpnt == HOSTDATA(SCpnt->host)->SCint[mbo]){
+ mb[mbo].status = 2; /* Abort command */
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1); /* start scsi command */
+ restore_flags(flags);
+ break;
+ };
+#endif
+ return SCSI_ABORT_SNOOZE;
+}
+
+/* We do not implement a reset function here, but the upper level code
+ assumes that it will get some kind of response for the command in
+ SCpnt. We must oblige, or the command will hang the scsi system.
+ For a first go, we assume that the 1542 notifies us with all of the
+ pending commands (it does implement soft reset, after all). */
+
+int aha1542_reset(Scsi_Cmnd * SCpnt)
+{
+ unchar ahacmd = CMD_START_SCSI;
+ int i;
+
+ /*
+ * See if a bus reset was suggested.
+ */
+ if( SCpnt->host->suggest_bus_reset )
+ {
+ /*
+ * This does a scsi reset for all devices on the bus.
+ * In principle, we could also reset the 1542 - should
+ * we do this? Try this first, and we can add that later
+ * if it turns out to be useful.
+ */
+ outb(HRST | SCRST, CONTROL(SCpnt->host->io_port));
+
+ /*
+ * Wait for the thing to settle down a bit. Unfortunately
+ * this is going to basically lock up the machine while we
+ * wait for this to complete. To be 100% correct, we need to
+ * check for timeout, and if we are doing something like this
+ * we are pretty desperate anyways.
+ */
+ WAIT(STATUS(SCpnt->host->io_port),
+ STATMASK, INIT|IDLE, STST|DIAGF|INVDCMD|DF|CDF);
+
+ /*
+ * We need to do this too before the 1542 can interact with
+ * us again.
+ */
+ setup_mailboxes(SCpnt->host->io_port, SCpnt->host);
+
+ /*
+ * Now try and pick up the pieces. Restart all commands
+ * that are currently active on the bus, and reset all of
+ * the datastructures. We have some time to kill while
+ * things settle down, so print a nice message.
+ */
+ printk("Sent BUS RESET to scsi host %d\n", SCpnt->host->host_no);
+
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i] != NULL)
+ {
+ Scsi_Cmnd * SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ SCtmp->result = DID_RESET << 16;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+ printk("Sending DID_RESET for target %d\n", SCpnt->target);
+ SCtmp->scsi_done(SCpnt);
+
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ /*
+ * Now tell the mid-level code what we did here. Since
+ * we have restarted all of the outstanding commands,
+ * then report SUCCESS.
+ */
+ return (SCSI_RESET_SUCCESS | SCSI_RESET_BUS_RESET);
+fail:
+ printk("aha1542.c: Unable to perform hard reset.\n");
+ printk("Power cycle machine to reset\n");
+ return (SCSI_RESET_ERROR | SCSI_RESET_BUS_RESET);
+
+
+ }
+ else
+ {
+ /* This does a selective reset of just the one device */
+ /* First locate the ccb for this command */
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i] == SCpnt)
+ {
+ HOSTDATA(SCpnt->host)->ccb[i].op = 0x81; /* BUS DEVICE RESET */
+ /* Now tell the 1542 to flush all pending commands for this target */
+ aha1542_out(SCpnt->host->io_port, &ahacmd, 1);
+
+ /* Here is the tricky part. What to do next. Do we get an interrupt
+ for the commands that we aborted with the specified target, or
+ do we generate this on our own? Try it without first and see
+ what happens */
+ printk("Sent BUS DEVICE RESET to target %d\n", SCpnt->target);
+
+ /* If the first does not work, then try the second. I think the
+ first option is more likely to be correct. Free the command
+ block for all commands running on this target... */
+ for(i=0; i< AHA1542_MAILBOXES; i++)
+ if(HOSTDATA(SCpnt->host)->SCint[i] &&
+ HOSTDATA(SCpnt->host)->SCint[i]->target == SCpnt->target)
+ {
+ Scsi_Cmnd * SCtmp;
+ SCtmp = HOSTDATA(SCpnt->host)->SCint[i];
+ SCtmp->result = DID_RESET << 16;
+ if (SCtmp->host_scribble) scsi_free(SCtmp->host_scribble, 512);
+ printk("Sending DID_RESET for target %d\n", SCpnt->target);
+ SCtmp->scsi_done(SCpnt);
+
+ HOSTDATA(SCpnt->host)->SCint[i] = NULL;
+ HOSTDATA(SCpnt->host)->mb[i].status = 0;
+ }
+ return SCSI_RESET_SUCCESS;
+ }
+ }
+ /* No active command at this time, so this means that each time we got
+ some kind of response the last time through. Tell the mid-level code
+ to request sense information in order to decide what to do next. */
+ return SCSI_RESET_PUNT;
+}
+
+#include "sd.h"
+
+int aha1542_biosparam(Scsi_Disk * disk, kdev_t dev, int * ip)
+{
+ int translation_algorithm;
+ int size = disk->capacity;
+
+ translation_algorithm = HOSTDATA(disk->device->host)->bios_translation;
+ /* Should this be > 1024, or >= 1024? Enquiring minds want to know. */
+ if((size>>11) > 1024 && translation_algorithm == 2) {
+ /* Please verify that this is the same as what DOS returns */
+ ip[0] = 255;
+ ip[1] = 63;
+ ip[2] = size /255/63;
+ } else {
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ };
+/* if (ip[2] >= 1024) ip[2] = 1024; */
+ return 0;
+}
+
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA1542;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/i386/i386at/gpl/linux/scsi/aha1542.h b/i386/i386at/gpl/linux/scsi/aha1542.h
new file mode 100644
index 00000000..4e0c6503
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aha1542.h
@@ -0,0 +1,177 @@
+#ifndef _AHA1542_H
+
+/* $Id: aha1542.h,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ *
+ * Header file for the adaptec 1542 driver for Linux
+ *
+ * $Log: aha1542.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:40:02 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:20 goel
+ * Linux driver merge.
+ *
+ * Revision 1.1 1992/07/24 06:27:38 root
+ * Initial revision
+ *
+ * Revision 1.2 1992/07/04 18:41:49 root
+ * Replaced distribution with current drivers
+ *
+ * Revision 1.3 1992/06/23 23:58:20 root
+ * Fixes.
+ *
+ * Revision 1.2 1992/05/26 22:13:23 root
+ * Changed bug that prevented DMA above first 2 mbytes.
+ *
+ * Revision 1.1 1992/05/22 21:00:29 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/24 18:01:50 root
+ * Initial revision
+ *
+ * Revision 1.1 1992/04/02 03:23:13 drew
+ * Initial revision
+ *
+ * Revision 1.3 1992/01/27 14:46:29 tthorn
+ * *** empty log message ***
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* I/O Port interface 4.2 */
+/* READ */
+#define STATUS(base) base
+#define STST 0x80 /* Self Test in Progress */
+#define DIAGF 0x40 /* Internal Diagnostic Failure */
+#define INIT 0x20 /* Mailbox Initialization Required */
+#define IDLE 0x10 /* SCSI Host Adapter Idle */
+#define CDF 0x08 /* Command/Data Out Port Full */
+#define DF 0x04 /* Data In Port Full */
+#define INVDCMD 0x01 /* Invalid H A Command */
+#define STATMASK 0xfd /* 0x02 is reserved */
+
+#define INTRFLAGS(base) (STATUS(base)+2)
+#define ANYINTR 0x80 /* Any Interrupt */
+#define SCRD 0x08 /* SCSI Reset Detected */
+#define HACC 0x04 /* HA Command Complete */
+#define MBOA 0x02 /* MBO Empty */
+#define MBIF 0x01 /* MBI Full */
+#define INTRMASK 0x8f
+
+/* WRITE */
+#define CONTROL(base) STATUS(base)
+#define HRST 0x80 /* Hard Reset */
+#define SRST 0x40 /* Soft Reset */
+#define IRST 0x20 /* Interrupt Reset */
+#define SCRST 0x10 /* SCSI Bus Reset */
+
+/* READ/WRITE */
+#define DATA(base) (STATUS(base)+1)
+#define CMD_NOP 0x00 /* No Operation */
+#define CMD_MBINIT 0x01 /* Mailbox Initialization */
+#define CMD_START_SCSI 0x02 /* Start SCSI Command */
+#define CMD_INQUIRY 0x04 /* Adapter Inquiry */
+#define CMD_EMBOI 0x05 /* Enable MailBox Out Interrupt */
+#define CMD_BUSON_TIME 0x07 /* Set Bus-On Time */
+#define CMD_BUSOFF_TIME 0x08 /* Set Bus-Off Time */
+#define CMD_DMASPEED 0x09 /* Set AT Bus Transfer Speed */
+#define CMD_RETDEVS 0x0a /* Return Installed Devices */
+#define CMD_RETCONF 0x0b /* Return Configuration Data */
+#define CMD_RETSETUP 0x0d /* Return Setup Data */
+#define CMD_ECHO 0x1f /* ECHO Command Data */
+
+#define CMD_EXTBIOS 0x28 /* Return extend bios information only 1542C */
+#define CMD_MBENABLE 0x29 /* Set Mailbox Interface enable only 1542C */
+
+/* Mailbox Definition 5.2.1 and 5.2.2 */
+struct mailbox {
+ unchar status; /* Command/Status */
+ unchar ccbptr[3]; /* msb, .., lsb */
+};
+
+/* This is used with scatter-gather */
+struct chain {
+ unchar datalen[3]; /* Size of this part of chain */
+ unchar dataptr[3]; /* Location of data */
+};
+
+/* These belong in scsi.h also */
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16) ; \
+(up)[1] = (((unsigned long)(p)) >> 8); \
+(up)[2] = ((unsigned long)(p));
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+
+struct ccb { /* Command Control Block 5.3 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked*/
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdblen; /* SCSI Command Length */
+ unchar rsalen; /* Request Sense Allocation Length/Disable */
+ unchar datalen[3]; /* Data Length (msb, .., lsb) */
+ unchar dataptr[3]; /* Data Pointer */
+ unchar linkptr[3]; /* Link Pointer */
+ unchar commlinkid; /* Command Linking Identifier */
+ unchar hastat; /* Host Adapter Status (HASTAT) */
+ unchar tarstat; /* Target Device Status */
+ unchar reserved[2];
+ unchar cdb[MAX_CDB+MAX_SENSE];/* SCSI Command Descriptor Block */
+ /* REQUEST SENSE */
+};
+
+int aha1542_detect(Scsi_Host_Template *);
+int aha1542_command(Scsi_Cmnd *);
+int aha1542_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int aha1542_abort(Scsi_Cmnd *);
+int aha1542_reset(Scsi_Cmnd *);
+int aha1542_biosparam(Disk *, kdev_t, int*);
+
+#define AHA1542_MAILBOXES 8
+#define AHA1542_SCATTER 16
+#define AHA1542_CMDLUN 1
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+extern struct proc_dir_entry proc_scsi_aha1542;
+
+#define AHA1542 { NULL, NULL, \
+ &proc_scsi_aha1542,/* proc_dir_entry */ \
+ NULL, \
+ "Adaptec 1542", \
+ aha1542_detect, \
+ NULL, \
+ NULL, \
+ aha1542_command, \
+ aha1542_queuecommand, \
+ aha1542_abort, \
+ aha1542_reset, \
+ NULL, \
+ aha1542_biosparam, \
+ AHA1542_MAILBOXES, \
+ 7, \
+ AHA1542_SCATTER, \
+ AHA1542_CMDLUN, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING}
+
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/aha1740.c b/i386/i386at/gpl/linux/scsi/aha1740.c
new file mode 100644
index 00000000..e3b8f1d0
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aha1740.c
@@ -0,0 +1,528 @@
+/* $Id: aha1740.c,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ * 1993/03/31
+ * linux/kernel/aha1740.c
+ *
+ * Based loosely on aha1542.c which is
+ * Copyright (C) 1992 Tommy Thorn and
+ * Modified by Eric Youngdale
+ *
+ * This file is aha1740.c, written and
+ * Copyright (C) 1992,1993 Brad McLean
+ *
+ * Modifications to makecode and queuecommand
+ * for proper handling of multiple devices courteously
+ * provided by Michael Weller, March, 1993
+ *
+ * aha1740_makecode may still need even more work
+ * if it doesn't work for your devices, take a look.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/dma.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "aha1740.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_aha1740 = {
+ PROC_SCSI_AHA1740, 7, "aha1740",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* IF YOU ARE HAVING PROBLEMS WITH THIS DRIVER, AND WANT TO WATCH
+ IT WORK, THEN:
+#define DEBUG
+*/
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/i386/i386at/gpl/linux/scsi/Attic/aha1740.c,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $";
+*/
+
+static unsigned int slot, base;
+static unsigned char irq_level;
+
+static struct ecb ecb[AHA1740_ECBS]; /* One for each queued operation */
+
+static int aha1740_last_ecb_used = 0; /* optimization */
+
+int aha1740_makecode(unchar *sense, unchar *status)
+{
+ struct statusword
+ {
+ ushort don:1, /* Command Done - No Error */
+ du:1, /* Data underrun */
+ :1, qf:1, /* Queue full */
+ sc:1, /* Specification Check */
+ dor:1, /* Data overrun */
+ ch:1, /* Chaining Halted */
+ intr:1, /* Interrupt issued */
+ asa:1, /* Additional Status Available */
+ sns:1, /* Sense information Stored */
+ :1, ini:1, /* Initialization Required */
+ me:1, /* Major error or exception */
+ :1, eca:1, /* Extended Contingent alliance */
+ :1;
+ } status_word;
+ int retval = DID_OK;
+
+ status_word = * (struct statusword *) status;
+#ifdef DEBUG
+printk("makecode from %x,%x,%x,%x %x,%x,%x,%x",status[0],status[1],status[2],status[3],
+sense[0],sense[1],sense[2],sense[3]);
+#endif
+ if (!status_word.don) /* Anything abnormal was detected */
+ {
+ if ( (status[1]&0x18) || status_word.sc ) /*Additional info available*/
+ {
+ /* Use the supplied info for further diagnostics */
+ switch ( status[2] )
+ {
+ case 0x12:
+ if ( status_word.dor )
+ retval=DID_ERROR; /* It's an Overrun */
+ /* If not overrun, assume underrun and ignore it! */
+ case 0x00: /* No info, assume no error, should not occur */
+ break;
+ case 0x11:
+ case 0x21:
+ retval=DID_TIME_OUT;
+ break;
+ case 0x0a:
+ retval=DID_BAD_TARGET;
+ break;
+ case 0x04:
+ case 0x05:
+ retval=DID_ABORT; /* Either by this driver or the AHA1740
+ itself */
+ break;
+ default:
+ retval=DID_ERROR; /* No further diagnostics possible */
+ }
+ }
+ else
+ { /* Michael suggests, and Brad concurs: */
+ if ( status_word.qf )
+ {
+ retval = DID_TIME_OUT; /* forces a redo */
+ /* I think this specific one should not happen -Brad */
+ printk("aha1740.c: WARNING: AHA1740 queue overflow!\n");
+ }
+ else if ( status[0]&0x60 )
+ {
+ retval = DID_ERROR; /* Didn't find a better error */
+ }
+ /* In any other case return DID_OK so for example
+ CONDITION_CHECKS make it through to the appropriate
+ device driver */
+ }
+ }
+ /* Under all circumstances supply the target status -Michael */
+ return status[3] | retval << 16;
+}
+
+int aha1740_test_port(void)
+{
+ char name[4],tmp;
+
+ /* Okay, look for the EISA ID's */
+ name[0]= 'A' -1 + ((tmp = inb(HID0)) >> 2); /* First character */
+ name[1]= 'A' -1 + ((tmp & 3) << 3);
+ name[1]+= ((tmp = inb(HID1)) >> 5)&0x7; /* Second Character */
+ name[2]= 'A' -1 + (tmp & 0x1f); /* Third Character */
+ name[3]=0;
+ tmp = inb(HID2);
+ if ( strcmp ( name, HID_MFG ) || inb(HID2) != HID_PRD )
+ return 0; /* Not an Adaptec 174x */
+
+/* if ( inb(HID3) != HID_REV )
+ printk("aha1740: Warning; board revision of %d; expected %d\n",
+ inb(HID3),HID_REV); */
+
+ if ( inb(EBCNTRL) != EBCNTRL_VALUE )
+ {
+ printk("aha1740: Board detected, but EBCNTRL = %x, so disabled it.\n",
+ inb(EBCNTRL));
+ return 0;
+ }
+
+ if ( inb(PORTADR) & PORTADDR_ENH )
+ return 1; /* Okay, we're all set */
+
+ printk("aha1740: Board detected, but not in enhanced mode, so disabled it.\n");
+ return 0;
+}
+
+/* A "high" level interrupt handler */
+void aha1740_intr_handle(int irq, struct pt_regs * regs)
+{
+ void (*my_done)(Scsi_Cmnd *);
+ int errstatus, adapstat;
+ int number_serviced;
+ struct ecb *ecbptr;
+ Scsi_Cmnd *SCtmp;
+
+ number_serviced = 0;
+
+ while(inb(G2STAT) & G2STAT_INTPEND)
+ {
+ DEB(printk("aha1740_intr top of loop.\n"));
+ adapstat = inb(G2INTST);
+ ecbptr = (struct ecb *) bus_to_virt(inl(MBOXIN0));
+ outb(G2CNTRL_IRST,G2CNTRL); /* interrupt reset */
+
+ switch ( adapstat & G2INTST_MASK )
+ {
+ case G2INTST_CCBRETRY:
+ case G2INTST_CCBERROR:
+ case G2INTST_CCBGOOD:
+ outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
+ if (!ecbptr)
+ {
+ printk("Aha1740 null ecbptr in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+ continue;
+ }
+ SCtmp = ecbptr->SCpnt;
+ if (!SCtmp)
+ {
+ printk("Aha1740 null SCtmp in interrupt (%x,%x,%x,%d)\n",
+ inb(G2STAT),adapstat,inb(G2INTST),number_serviced++);
+ continue;
+ }
+ if (SCtmp->host_scribble)
+ scsi_free(SCtmp->host_scribble, 512);
+ /* Fetch the sense data, and tuck it away, in the required slot. The
+ Adaptec automatically fetches it, and there is no guarantee that
+ we will still have it in the cdb when we come back */
+ if ( (adapstat & G2INTST_MASK) == G2INTST_CCBERROR )
+ {
+ memcpy(SCtmp->sense_buffer, ecbptr->sense,
+ sizeof(SCtmp->sense_buffer));
+ errstatus = aha1740_makecode(ecbptr->sense,ecbptr->status);
+ }
+ else
+ errstatus = 0;
+ DEB(if (errstatus) printk("aha1740_intr_handle: returning %6x\n", errstatus));
+ SCtmp->result = errstatus;
+ my_done = ecbptr->done;
+ memset(ecbptr,0,sizeof(struct ecb));
+ if ( my_done )
+ my_done(SCtmp);
+ break;
+ case G2INTST_HARDFAIL:
+ printk("aha1740 hardware failure!\n");
+ panic("aha1740.c"); /* Goodbye */
+ case G2INTST_ASNEVENT:
+ printk("aha1740 asynchronous event: %02x %02x %02x %02x %02x\n",adapstat,
+ inb(MBOXIN0),inb(MBOXIN1),inb(MBOXIN2),inb(MBOXIN3)); /* Say What? */
+ outb(G2CNTRL_HRDY,G2CNTRL); /* Host Ready -> Mailbox in complete */
+ break;
+ case G2INTST_CMDGOOD:
+ /* set immediate command success flag here: */
+ break;
+ case G2INTST_CMDERROR:
+ /* Set immediate command failure flag here: */
+ break;
+ }
+ number_serviced++;
+ }
+}
+
+int aha1740_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->target;
+ unsigned long flags;
+ void *buff = SCpnt->request_buffer;
+ int bufflen = SCpnt->request_bufflen;
+ int ecbno;
+ DEB(int i);
+
+
+ if(*cmd == REQUEST_SENSE)
+ {
+ if (bufflen != sizeof(SCpnt->sense_buffer))
+ {
+ printk("Wrong buffer length supplied for request sense (%d)\n",bufflen);
+ }
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ }
+
+#ifdef DEBUG
+ if (*cmd == READ_10 || *cmd == WRITE_10)
+ i = xscsi2int(cmd+2);
+ else if (*cmd == READ_6 || *cmd == WRITE_6)
+ i = scsi2int(cmd+2);
+ else
+ i = -1;
+ printk("aha1740_queuecommand: dev %d cmd %02x pos %d len %d ", target, *cmd, i, bufflen);
+ printk("scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
+ printk("\n");
+#endif
+
+ /* locate an available ecb */
+
+ save_flags(flags);
+ cli();
+ ecbno = aha1740_last_ecb_used + 1; /* An optimization */
+ if (ecbno >= AHA1740_ECBS) ecbno = 0;
+
+ do{
+ if( ! ecb[ecbno].cmdw )
+ break;
+ ecbno++;
+ if (ecbno >= AHA1740_ECBS ) ecbno = 0;
+ } while (ecbno != aha1740_last_ecb_used);
+
+ if( ecb[ecbno].cmdw )
+ panic("Unable to find empty ecb for aha1740.\n");
+
+ ecb[ecbno].cmdw = AHA1740CMD_INIT; /* SCSI Initiator Command doubles as reserved flag */
+
+ aha1740_last_ecb_used = ecbno;
+ restore_flags(flags);
+
+#ifdef DEBUG
+ printk("Sending command (%d %x)...",ecbno, done);
+#endif
+
+ ecb[ecbno].cdblen = SCpnt->cmd_len; /* SCSI Command Descriptor Block Length */
+
+ direction = 0;
+ if (*cmd == READ_10 || *cmd == READ_6)
+ direction = 1;
+ else if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 0;
+
+ memcpy(ecb[ecbno].cdb, cmd, ecb[ecbno].cdblen);
+
+ if (SCpnt->use_sg)
+ {
+ struct scatterlist * sgpnt;
+ struct aha1740_chain * cptr;
+ int i;
+#ifdef DEBUG
+ unsigned char * ptr;
+#endif
+ ecb[ecbno].sg = 1; /* SCSI Initiator Command w/scatter-gather*/
+ SCpnt->host_scribble = (unsigned char *) scsi_malloc(512);
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+ cptr = (struct aha1740_chain *) SCpnt->host_scribble;
+ if (cptr == NULL) panic("aha1740.c: unable to allocate DMA memory\n");
+ for(i=0; i<SCpnt->use_sg; i++)
+ {
+ cptr[i].dataptr = (long) sgpnt[i].address;
+ cptr[i].datalen = sgpnt[i].length;
+ }
+ ecb[ecbno].datalen = SCpnt->use_sg * sizeof(struct aha1740_chain);
+ ecb[ecbno].dataptr = (long) cptr;
+#ifdef DEBUG
+ printk("cptr %x: ",cptr);
+ ptr = (unsigned char *) cptr;
+ for(i=0;i<24;i++) printk("%02x ", ptr[i]);
+#endif
+ }
+ else
+ {
+ SCpnt->host_scribble = NULL;
+ ecb[ecbno].datalen = bufflen;
+ ecb[ecbno].dataptr = (long) buff;
+ }
+ ecb[ecbno].lun = SCpnt->lun;
+ ecb[ecbno].ses = 1; /* Suppress underrun errors */
+ ecb[ecbno].dir= direction;
+ ecb[ecbno].ars=1; /* Yes, get the sense on an error */
+ ecb[ecbno].senselen = 12;
+ ecb[ecbno].senseptr = (long) ecb[ecbno].sense;
+ ecb[ecbno].statusptr = (long) ecb[ecbno].status;
+ ecb[ecbno].done = done;
+ ecb[ecbno].SCpnt = SCpnt;
+#ifdef DEBUG
+ {
+ int i;
+ printk("aha1740_command: sending.. ");
+ for (i = 0; i < sizeof(ecb[ecbno])-10; i++)
+ printk("%02x ", ((unchar *)&ecb[ecbno])[i]);
+ }
+ printk("\n");
+#endif
+ if (done)
+ { /* You may question the code below, which contains potentially
+ non-terminating while loops with interrupts disabled. So did
+ I when I wrote it, but the Adaptec Spec says the card is so fast,
+ that this problem virtually never occurs so I've kept it. We
+ do printk a warning first, so that you'll know if it happens.
+ In practice the only time we've seen this message is when some-
+ thing else is in the driver was broken, like _makecode(), or
+ when a scsi device hung the scsi bus. Even under these conditions,
+ The loop actually only cycled < 3 times (we instrumented it). */
+
+ DEB(printk("aha1740[%d] critical section\n",ecbno));
+ save_flags(flags);
+ cli();
+ if ( ! (inb(G2STAT) & G2STAT_MBXOUT) )
+ {
+ printk("aha1740[%d]_mbxout wait!\n",ecbno);
+ cli(); /* printk may have done a sti()! */
+ }
+ mb();
+ while ( ! (inb(G2STAT) & G2STAT_MBXOUT) ); /* Oh Well. */
+ outl(virt_to_bus(ecb+ecbno), MBOXOUT0);
+ if ( inb(G2STAT) & G2STAT_BUSY )
+ {
+ printk("aha1740[%d]_attn wait!\n",ecbno);
+ cli();
+ }
+ while ( inb(G2STAT) & G2STAT_BUSY ); /* And Again! */
+ outb(ATTN_START | (target & 7), ATTN); /* Start it up */
+ restore_flags(flags);
+ DEB(printk("aha1740[%d] request queued.\n",ecbno));
+ }
+ else
+ printk("aha1740_queuecommand: done can't be NULL\n");
+
+ return 0;
+}
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+int aha1740_command(Scsi_Cmnd * SCpnt)
+{
+ aha1740_queuecommand(SCpnt, internal_done);
+
+ while (!internal_done_flag);
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+/* Query the board for its irq_level. Nothing else matters
+ in enhanced mode on an EISA bus. */
+
+void aha1740_getconfig(void)
+{
+ static int intab[] = { 9,10,11,12,0,14,15,0 };
+
+ irq_level = intab [ inb(INTDEF)&0x7 ];
+ outb(inb(INTDEF) | 0x10, INTDEF);
+}
+
+int aha1740_detect(Scsi_Host_Template * tpnt)
+{
+ tpnt->proc_dir = &proc_scsi_aha1740;
+
+ memset(&ecb, 0, sizeof(struct ecb));
+ DEB(printk("aha1740_detect: \n"));
+
+ for ( slot=MINEISA; slot <= MAXEISA; slot++ )
+ {
+ base = SLOTBASE(slot);
+ /*
+ * The ioports for eisa boards are generally beyond that used in the
+ * check/allocate region code, but this may change at some point,
+ * so we go through the motions.
+ */
+ if(check_region(base, 0x5c)) continue; /* See if in use */
+ if ( aha1740_test_port()) break;
+ }
+ if ( slot > MAXEISA )
+ return 0;
+
+ aha1740_getconfig();
+
+ if ( (inb(G2STAT) & (G2STAT_MBXOUT | G2STAT_BUSY) ) != G2STAT_MBXOUT )
+ { /* If the card isn't ready, hard reset it */
+ outb(G2CNTRL_HRST,G2CNTRL);
+ outb(0,G2CNTRL);
+ }
+
+ printk("Configuring Adaptec at IO:%x, IRQ %d\n",base,
+ irq_level);
+
+ DEB(printk("aha1740_detect: enable interrupt channel %d\n", irq_level));
+
+ if (request_irq(irq_level,aha1740_intr_handle, 0, "aha1740"))
+ {
+ printk("Unable to allocate IRQ for adaptec controller.\n");
+ return 0;
+ }
+ request_region(base, 0x5c,"aha1740"); /* Reserve the space that we need to use */
+ return 1;
+}
+
+/* Note: They following two functions do not apply very well to the Adaptec,
+which basically manages its own affairs quite well without our interference,
+so I haven't put anything into them. I can faintly imagine someone with a
+*very* badly behaved SCSI target (perhaps an old tape?) wanting the abort(),
+but it hasn't happened yet, and doing aborts brings the Adaptec to its
+knees. I cannot (at this moment in time) think of any reason to reset the
+card once it's running. So there. */
+
+int aha1740_abort(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("aha1740_abort called\n"));
+ return SCSI_ABORT_SNOOZE;
+}
+
+/* We do not implement a reset function here, but the upper level code assumes
+ that it will get some kind of response for the command in SCpnt. We must
+ oblige, or the command will hang the scsi system */
+
+int aha1740_reset(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("aha1740_reset called\n"));
+ return SCSI_RESET_PUNT;
+}
+
+int aha1740_biosparam(Disk * disk, kdev_t dev, int* ip)
+{
+ int size = disk->capacity;
+DEB(printk("aha1740_biosparam\n"));
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+/* if (ip[2] >= 1024) ip[2] = 1024; */
+ return 0;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AHA1740;
+
+#include "scsi_module.c"
+#endif
+
+/* Okay, you made it all the way through. As of this writing, 3/31/93, I'm
+brad@saturn.gaylord.com or brad@bradpc.gaylord.com. I'll try to help as time
+permits if you have any trouble with this driver. Happy Linuxing! */
diff --git a/i386/i386at/gpl/linux/scsi/aha1740.h b/i386/i386at/gpl/linux/scsi/aha1740.h
new file mode 100644
index 00000000..79d94697
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aha1740.h
@@ -0,0 +1,193 @@
+#ifndef _AHA1740_H
+
+/* $Id: aha1740.h,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ *
+ * Header file for the adaptec 1740 driver for Linux
+ *
+ * With minor revisions 3/31/93
+ * Written and (C) 1992,1993 Brad McLean. See aha1740.c
+ * for more info
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+/* Eisa Enhanced mode operation - slot locating and addressing */
+#define MINEISA 1 /* I don't have an EISA Spec to know these ranges, so I */
+#define MAXEISA 8 /* Just took my machine's specifications. Adjust to fit.*/
+ /* I just saw an ad, and bumped this from 6 to 8 */
+#define SLOTBASE(x) ((x << 12)+ 0xc80 )
+#define BASE (base)
+
+/* EISA configuration registers & values */
+#define HID0 (base + 0x0)
+#define HID1 (base + 0x1)
+#define HID2 (base + 0x2)
+#define HID3 (base + 0x3)
+#define EBCNTRL (base + 0x4)
+#define PORTADR (base + 0x40)
+#define BIOSADR (base + 0x41)
+#define INTDEF (base + 0x42)
+#define SCSIDEF (base + 0x43)
+#define BUSDEF (base + 0x44)
+#define RESV0 (base + 0x45)
+#define RESV1 (base + 0x46)
+#define RESV2 (base + 0x47)
+
+#define HID_MFG "ADP"
+#define HID_PRD 0
+#define HID_REV 2
+#define EBCNTRL_VALUE 1
+#define PORTADDR_ENH 0x80
+/* READ */
+#define G2INTST (BASE + 0x56)
+#define G2STAT (BASE + 0x57)
+#define MBOXIN0 (BASE + 0x58)
+#define MBOXIN1 (BASE + 0x59)
+#define MBOXIN2 (BASE + 0x5a)
+#define MBOXIN3 (BASE + 0x5b)
+#define G2STAT2 (BASE + 0x5c)
+
+#define G2INTST_MASK 0xf0 /* isolate the status */
+#define G2INTST_CCBGOOD 0x10 /* CCB Completed */
+#define G2INTST_CCBRETRY 0x50 /* CCB Completed with a retry */
+#define G2INTST_HARDFAIL 0x70 /* Adapter Hardware Failure */
+#define G2INTST_CMDGOOD 0xa0 /* Immediate command success */
+#define G2INTST_CCBERROR 0xc0 /* CCB Completed with error */
+#define G2INTST_ASNEVENT 0xd0 /* Asynchronous Event Notification */
+#define G2INTST_CMDERROR 0xe0 /* Immediate command error */
+
+#define G2STAT_MBXOUT 4 /* Mailbox Out Empty Bit */
+#define G2STAT_INTPEND 2 /* Interrupt Pending Bit */
+#define G2STAT_BUSY 1 /* Busy Bit (attention pending) */
+
+#define G2STAT2_READY 0 /* Host Ready Bit */
+
+/* WRITE (and ReadBack) */
+#define MBOXOUT0 (BASE + 0x50)
+#define MBOXOUT1 (BASE + 0x51)
+#define MBOXOUT2 (BASE + 0x52)
+#define MBOXOUT3 (BASE + 0x53)
+#define ATTN (BASE + 0x54)
+#define G2CNTRL (BASE + 0x55)
+
+#define ATTN_IMMED 0x10 /* Immediate Command */
+#define ATTN_START 0x40 /* Start CCB */
+#define ATTN_ABORT 0x50 /* Abort CCB */
+
+#define G2CNTRL_HRST 0x80 /* Hard Reset */
+#define G2CNTRL_IRST 0x40 /* Clear EISA Interrupt */
+#define G2CNTRL_HRDY 0x20 /* Sets HOST ready */
+
+/* This is used with scatter-gather */
+struct aha1740_chain {
+ u32 dataptr; /* Location of data */
+ u32 datalen; /* Size of this part of chain */
+};
+
+/* These belong in scsi.h */
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16) ; \
+(up)[1] = (((unsigned long)(p)) >> 8); \
+(up)[2] = ((unsigned long)(p));
+
+#define scsi2int(up) ( (((long)*(up)) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+#define MAX_STATUS 32
+
+struct ecb { /* Enhanced Control Block 6.1 */
+ u16 cmdw; /* Command Word */
+ /* Flag Word 1 */
+ u16 cne:1, /* Control Block Chaining */
+ :6, di:1, /* Disable Interrupt */
+ :2, ses:1, /* Suppress Underrun error */
+ :1, sg:1, /* Scatter/Gather */
+ :1, dsb:1, /* Disable Status Block */
+ ars:1; /* Automatic Request Sense */
+ /* Flag Word 2 */
+ u16 lun:3, /* Logical Unit */
+ tag:1, /* Tagged Queuing */
+ tt:2, /* Tag Type */
+ nd:1, /* No Disconnect */
+ :1, dat:1, /* Data transfer - check direction */
+ dir:1, /* Direction of transfer 1 = datain */
+ st:1, /* Suppress Transfer */
+ chk:1, /* Calculate Checksum */
+ :2, rec:1, :1; /* Error Recovery */
+ u16 nil0; /* nothing */
+ u32 dataptr; /* Data or Scatter List ptr */
+ u32 datalen; /* Data or Scatter List len */
+ u32 statusptr; /* Status Block ptr */
+ u32 linkptr; /* Chain Address */
+ u32 nil1; /* nothing */
+ u32 senseptr; /* Sense Info Pointer */
+ u8 senselen; /* Sense Length */
+ u8 cdblen; /* CDB Length */
+ u16 datacheck; /* Data checksum */
+ u8 cdb[MAX_CDB]; /* CDB area */
+/* Hardware defined portion ends here, rest is driver defined */
+ u8 sense[MAX_SENSE]; /* Sense area */
+ u8 status[MAX_STATUS]; /* Status area */
+ Scsi_Cmnd *SCpnt; /* Link to the SCSI Command Block */
+ void (*done)(Scsi_Cmnd *); /* Completion Function */
+};
+
+#define AHA1740CMD_NOP 0x00 /* No OP */
+#define AHA1740CMD_INIT 0x01 /* Initiator SCSI Command */
+#define AHA1740CMD_DIAG 0x05 /* Run Diagnostic Command */
+#define AHA1740CMD_SCSI 0x06 /* Initialize SCSI */
+#define AHA1740CMD_SENSE 0x08 /* Read Sense Information */
+#define AHA1740CMD_DOWN 0x09 /* Download Firmware (yeah, I bet!) */
+#define AHA1740CMD_RINQ 0x0a /* Read Host Adapter Inquiry Data */
+#define AHA1740CMD_TARG 0x10 /* Target SCSI Command */
+
+int aha1740_detect(Scsi_Host_Template *);
+int aha1740_command(Scsi_Cmnd *);
+int aha1740_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int aha1740_abort(Scsi_Cmnd *);
+int aha1740_reset(Scsi_Cmnd *);
+int aha1740_biosparam(Disk *, kdev_t, int*);
+
+#define AHA1740_ECBS 32
+#define AHA1740_SCATTER 16
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+
+#define AHA1740 {NULL, NULL, \
+ NULL, \
+ NULL, \
+ "Adaptec 174x (EISA)", \
+ aha1740_detect, \
+ NULL, \
+ NULL, \
+ aha1740_command, \
+ aha1740_queuecommand, \
+ aha1740_abort, \
+ aha1740_reset, \
+ NULL, \
+ aha1740_biosparam, \
+ AHA1740_ECBS, \
+ 7, \
+ AHA1740_SCATTER, \
+ 1, \
+ 0, \
+ 0, \
+ ENABLE_CLUSTERING}
+
+#endif
+
diff --git a/i386/i386at/gpl/linux/scsi/aic7xxx.c b/i386/i386at/gpl/linux/scsi/aic7xxx.c
new file mode 100644
index 00000000..3b238983
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aic7xxx.c
@@ -0,0 +1,4645 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * Sources include the Adaptec 1740 driver (aha1740.c), the Ultrastor 24F
+ * driver (ultrastor.c), various Linux kernel source, the Adaptec EISA
+ * config file (!adp7771.cfg), the Adaptec AHA-2740A Series User's Guide,
+ * the Linux Kernel Hacker's Guide, Writing a SCSI Device Driver for Linux,
+ * the Adaptec 1542 driver (aha1542.c), the Adaptec EISA overlay file
+ * (adp7770.ovl), the Adaptec AHA-2740 Series Technical Reference Manual,
+ * the Adaptec AIC-7770 Data Book, the ANSI SCSI specification, the
+ * ANSI SCSI-2 specification (draft 10c), ...
+ *
+ * ----------------------------------------------------------------
+ * Modified to include support for wide and twin bus adapters,
+ * DMAing of SCBs, tagged queueing, IRQ sharing, bug fixes,
+ * and other rework of the code.
+ *
+ * Parts of this driver are based on the FreeBSD driver by Justin
+ * T. Gibbs.
+ *
+ * A Boot time option was also added for not resetting the scsi bus.
+ *
+ * Form: aic7xxx=extended,no_reset
+ *
+ * -- Daniel M. Eischen, deischen@iworks.InterWorks.org, 04/03/95
+ *
+ * $Id: aic7xxx.c,v 1.1.1.1 1997/02/25 21:27:46 thomas Exp $
+ *-M*************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <stdarg.h>
+#include <asm/io.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/bios32.h>
+#include <linux/delay.h>
+#include <linux/sched.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include "sd.h"
+#include "scsi.h"
+#include "hosts.h"
+#include "aic7xxx.h"
+#include "aic7xxx_reg.h"
+#include <linux/stat.h>
+
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_aic7xxx = {
+ PROC_SCSI_AIC7XXX, 7, "aic7xxx",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define AIC7XXX_C_VERSION "$Revision: 1.1.1.1 $"
+
+#define NUMBER(arr) (sizeof(arr) / sizeof(arr[0]))
+#define MIN(a,b) ((a < b) ? a : b)
+#define ALL_TARGETS -1
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+/*
+ * Defines for PCI bus support, testing twin bus support, DMAing of
+ * SCBs, tagged queueing, commands (SCBs) per lun, and SCSI bus reset
+ * delay time.
+ *
+ * o PCI bus support - this has been implemented and working since
+ * the December 1, 1994 release of this driver. If you don't have
+ * a PCI bus, then you can configure your kernel without PCI
+ * support because all PCI dependent code is bracketed with
+ * "#ifdef CONFIG_PCI ... #endif CONFIG_PCI".
+ *
+ * o Twin bus support - this has been tested and does work.
+ *
+ * o DMAing of SCBs - thanks to Kai Makisara, this now works.
+ * This define is now taken out and DMAing of SCBs is always
+ * performed (8/12/95 - DE).
+ *
+ * o Tagged queueing - this driver is capable of tagged queueing
+ * but I am unsure as to how well the higher level driver implements
+ * tagged queueing. Therefore, the maximum commands per lun is
+ * set to 2. If you want to implement tagged queueing, ensure
+ * this define is not commented out.
+ *
+ * o Sharing IRQs - allowed for sharing of IRQs. This will allow
+ * for multiple aic7xxx host adapters sharing the same IRQ, but
+ * not for sharing IRQs with other devices. The higher level
+ * PCI code and interrupt handling needs to be modified to
+ * support this.
+ *
+ * o Commands per lun - If tagged queueing is enabled, then you
+ * may want to try increasing AIC7XXX_CMDS_PER_LUN to more
+ * than 2. By default, we limit the SCBs per lun to 2 with
+ * or without tagged queueing enabled. If tagged queueing is
+ * disabled, the sequencer will keep the 2nd SCB in the input
+ * queue until the first one completes - so it is OK to to have
+ * more than 1 SCB queued. If tagged queueing is enabled, then
+ * the sequencer will attempt to send the 2nd SCB to the device
+ * while the first SCB is executing and the device is disconnected.
+ * For adapters limited to 4 SCBs, you may want to actually
+ * decrease the commands per lun to 1, if you often have more
+ * than 2 devices active at the same time. This will allocate
+ * 1 SCB for each device and ensure that there will always be
+ * a free SCB for up to 4 devices active at the same time.
+ *
+ * o 3985 support - The 3985 adapter is much like the 3940, but
+ * has three 7870 controllers as opposed to two for the 3940.
+ * It will get probed and recognized as three different adapters,
+ * but all three controllers share the same bank of 255 SCBs
+ * instead of each controller having their own bank (like the
+ * controllers on the 3940). For this reason, it is important
+ * that all devices be resident on just one channel of the 3985.
+ * In the near future, we'll modify the driver to reserve 1/3
+ * of the SCBs for each controller.
+ *
+ * Daniel M. Eischen, deischen@iworks.InterWorks.org, 01/11/96
+ */
+
+/* Uncomment this for testing twin bus support. */
+#define AIC7XXX_TWIN_SUPPORT
+
+/* Uncomment this for tagged queueing. */
+/* #define AIC7XXX_TAGGED_QUEUEING */
+
+/* Uncomment this for allowing sharing of IRQs. */
+#define AIC7XXX_SHARE_IRQS
+
+/*
+ * You can try raising me if tagged queueing is enabled, or lowering
+ * me if you only have 4 SCBs.
+ */
+#define AIC7XXX_CMDS_PER_LUN 2
+
+/* Set this to the delay in seconds after SCSI bus reset. */
+#define AIC7XXX_RESET_DELAY 15
+
+/*
+ * Uncomment the following define for collection of SCSI transfer statistics
+ * for the /proc filesystem.
+ *
+ * NOTE: This does affect performance since it has to maintain statistics.
+ */
+/* #define AIC7XXX_PROC_STATS */
+
+/*
+ * For debugging the abort/reset code.
+ */
+/* #define AIC7XXX_DEBUG_ABORT */
+
+/*
+ * For general debug messages
+ */
+#define AIC7XXX_DEBUG
+
+/*
+ * Controller type and options
+ */
+typedef enum {
+ AIC_NONE,
+ AIC_7770, /* EISA aic7770 on motherboard */
+ AIC_7771, /* EISA aic7771 on 274x */
+ AIC_284x, /* VLB aic7770 on 284x */
+ AIC_7850, /* PCI aic7850 */
+ AIC_7870, /* PCI aic7870 on motherboard */
+ AIC_7871, /* PCI aic7871 on 294x */
+ AIC_7872, /* PCI aic7872 on 3940 */
+ AIC_7873, /* PCI aic7873 on 3985 */
+ AIC_7874, /* PCI aic7874 on 294x Differential */
+ AIC_7880, /* PCI aic7880 on motherboard */
+ AIC_7881, /* PCI aic7881 on 294x Ultra */
+ AIC_7882, /* PCI aic7882 on 3940 Ultra */
+ AIC_7883, /* PCI aic7883 on 3985 Ultra */
+ AIC_7884 /* PCI aic7884 on 294x Ultra Differential */
+} aha_type;
+
+typedef enum {
+ AIC_777x, /* AIC-7770 based */
+ AIC_785x, /* AIC-7850 based */
+ AIC_787x, /* AIC-7870 based */
+ AIC_788x /* AIC-7880 based */
+} aha_chip_type;
+
+typedef enum {
+ AIC_SINGLE, /* Single Channel */
+ AIC_TWIN, /* Twin Channel */
+ AIC_WIDE /* Wide Channel */
+} aha_bus_type;
+
+typedef enum {
+ AIC_UNKNOWN,
+ AIC_ENABLED,
+ AIC_DISABLED
+} aha_status_type;
+
+typedef enum {
+ LIST_HEAD,
+ LIST_SECOND
+} insert_type;
+
+typedef enum {
+ ABORT_RESET_INACTIVE,
+ ABORT_RESET_PENDING,
+ ABORT_RESET_SUCCESS
+} aha_abort_reset_type;
+
+/*
+ * Define an array of board names that can be indexed by aha_type.
+ * Don't forget to change this when changing the types!
+ */
+static const char * board_names[] = {
+ "<AIC-7xxx Unknown>", /* AIC_NONE */
+ "AIC-7770", /* AIC_7770 */
+ "AHA-2740", /* AIC_7771 */
+ "AHA-2840", /* AIC_284x */
+ "AIC-7850", /* AIC_7850 */
+ "AIC-7870", /* AIC_7870 */
+ "AHA-2940", /* AIC_7871 */
+ "AHA-3940", /* AIC_7872 */
+ "AHA-3985", /* AIC_7873 */
+ "AHA-2940 Differential", /* AIC_7874 */
+ "AIC-7880 Ultra", /* AIC_7880 */
+ "AHA-2940 Ultra", /* AIC_7881 */
+ "AHA-3940 Ultra", /* AIC_7882 */
+ "AHA-3985 Ultra", /* AIC_7883 */
+ "AHA-2940 Ultra Differential" /* AIC_7884 */
+};
+
+/*
+ * There should be a specific return value for this in scsi.h, but
+ * it seems that most drivers ignore it.
+ */
+#define DID_UNDERFLOW DID_ERROR
+
+/*
+ * What we want to do is have the higher level scsi driver requeue
+ * the command to us. There is no specific driver status for this
+ * condition, but the higher level scsi driver will requeue the
+ * command on a DID_BUS_BUSY error.
+ */
+#define DID_RETRY_COMMAND DID_BUS_BUSY
+
+/*
+ * EISA/VL-bus stuff
+ */
+#define MINSLOT 1
+#define MAXSLOT 15
+#define SLOTBASE(x) ((x) << 12)
+#define MAXIRQ 15
+
+/*
+ * Standard EISA Host ID regs (Offset from slot base)
+ */
+#define HID0 0x80 /* 0,1: msb of ID2, 2-7: ID1 */
+#define HID1 0x81 /* 0-4: ID3, 5-7: LSB ID2 */
+#define HID2 0x82 /* product */
+#define HID3 0x83 /* firmware revision */
+
+/*
+ * AIC-7770 I/O range to reserve for a card
+ */
+#define MINREG 0xC00
+#define MAXREG 0xCBF
+
+#define INTDEF 0x5C /* Interrupt Definition Register */
+
+/*
+ * Some defines for the HCNTRL register.
+ */
+#define REQ_PAUSE IRQMS | INTEN | PAUSE
+#define UNPAUSE_274X IRQMS | INTEN
+#define UNPAUSE_284X INTEN
+#define UNPAUSE_294X IRQMS | INTEN
+
+/*
+ * AIC-78X0 PCI registers
+ */
+#define CLASS_PROGIF_REVID 0x08
+#define DEVREVID 0x000000FFul
+#define PROGINFC 0x0000FF00ul
+#define SUBCLASS 0x00FF0000ul
+#define BASECLASS 0xFF000000ul
+
+#define CSIZE_LATTIME 0x0C
+#define CACHESIZE 0x0000003Ful /* only 5 bits */
+#define LATTIME 0x0000FF00ul
+
+#define DEVCONFIG 0x40
+#define MPORTMODE 0x00000400ul /* aic7870 only */
+#define RAMPSM 0x00000200ul /* aic7870 only */
+#define VOLSENSE 0x00000100ul
+#define SCBRAMSEL 0x00000080ul
+#define MRDCEN 0x00000040ul
+#define EXTSCBTIME 0x00000020ul /* aic7870 only */
+#define EXTSCBPEN 0x00000010ul /* aic7870 only */
+#define BERREN 0x00000008ul
+#define DACEN 0x00000004ul
+#define STPWLEVEL 0x00000002ul
+#define DIFACTNEGEN 0x00000001ul /* aic7870 only */
+
+/*
+ *
+ * Define the format of the SEEPROM registers (16 bits).
+ *
+ */
+struct seeprom_config {
+
+/*
+ * SCSI ID Configuration Flags
+ */
+#define CFXFER 0x0007 /* synchronous transfer rate */
+#define CFSYNCH 0x0008 /* enable synchronous transfer */
+#define CFDISC 0x0010 /* enable disconnection */
+#define CFWIDEB 0x0020 /* wide bus device (wide card) */
+/* UNUSED 0x00C0 */
+#define CFSTART 0x0100 /* send start unit SCSI command */
+#define CFINCBIOS 0x0200 /* include in BIOS scan */
+#define CFRNFOUND 0x0400 /* report even if not found */
+/* UNUSED 0xF800 */
+ unsigned short device_flags[16]; /* words 0-15 */
+
+/*
+ * BIOS Control Bits
+ */
+#define CFSUPREM 0x0001 /* support all removeable drives */
+#define CFSUPREMB 0x0002 /* support removeable drives for boot only */
+#define CFBIOSEN 0x0004 /* BIOS enabled */
+/* UNUSED 0x0008 */
+#define CFSM2DRV 0x0010 /* support more than two drives */
+#define CF284XEXTEND 0x0020 /* extended translation (284x cards) */
+/* UNUSED 0x0040 */
+#define CFEXTEND 0x0080 /* extended translation enabled */
+/* UNUSED 0xFF00 */
+ unsigned short bios_control; /* word 16 */
+
+/*
+ * Host Adapter Control Bits
+ */
+/* UNUSED 0x0001 */
+#define CFULTRAEN 0x0002 /* Ultra SCSI speed enable (Ultra cards) */
+#define CF284XSELTO 0x0003 /* Selection timeout (284x cards) */
+#define CF284XFIFO 0x000C /* FIFO Threshold (284x cards) */
+#define CFSTERM 0x0004 /* SCSI low byte termination (non-wide cards) */
+#define CFWSTERM 0x0008 /* SCSI high byte termination (wide card) */
+#define CFSPARITY 0x0010 /* SCSI parity */
+#define CF284XSTERM 0x0020 /* SCSI low byte termination (284x cards) */
+#define CFRESETB 0x0040 /* reset SCSI bus at IC initialization */
+/* UNUSED 0xFF80 */
+ unsigned short adapter_control; /* word 17 */
+
+/*
+ * Bus Release, Host Adapter ID
+ */
+#define CFSCSIID 0x000F /* host adapter SCSI ID */
+/* UNUSED 0x00F0 */
+#define CFBRTIME 0xFF00 /* bus release time */
+ unsigned short brtime_id; /* word 18 */
+
+/*
+ * Maximum targets
+ */
+#define CFMAXTARG 0x00FF /* maximum targets */
+/* UNUSED 0xFF00 */
+ unsigned short max_targets; /* word 19 */
+
+ unsigned short res_1[11]; /* words 20-30 */
+ unsigned short checksum; /* word 31 */
+};
+
+/*
+ * Pause the sequencer and wait for it to actually stop - this
+ * is important since the sequencer can disable pausing for critical
+ * sections.
+ */
+#define PAUSE_SEQUENCER(p) \
+ outb(p->pause, HCNTRL + p->base); \
+ while ((inb(HCNTRL + p->base) & PAUSE) == 0) \
+ ; \
+
+/*
+ * Unpause the sequencer. Unremarkable, yet done often enough to
+ * warrant an easy way to do it.
+ */
+#define UNPAUSE_SEQUENCER(p) \
+ outb(p->unpause, HCNTRL + p->base)
+
+/*
+ * Restart the sequencer program from address zero
+ */
+#define RESTART_SEQUENCER(p) \
+ do { \
+ outb(SEQRESET | FASTMODE, SEQCTL + p->base); \
+ } while (inb(SEQADDR0 + p->base) != 0 && \
+ inb(SEQADDR1 + p->base) != 0); \
+ UNPAUSE_SEQUENCER(p);
+
+/*
+ * If an error occurs during a data transfer phase, run the comand
+ * to completion - it's easier that way - making a note of the error
+ * condition in this location. This then will modify a DID_OK status
+ * into an appropriate error for the higher-level SCSI code.
+ */
+#define aic7xxx_error(cmd) ((cmd)->SCp.Status)
+
+/*
+ * Keep track of the targets returned status.
+ */
+#define aic7xxx_status(cmd) ((cmd)->SCp.sent_command)
+
+/*
+ * The position of the SCSI commands scb within the scb array.
+ */
+#define aic7xxx_position(cmd) ((cmd)->SCp.have_data_in)
+
+/*
+ * Since the sequencer code DMAs the scatter-gather structures
+ * directly from memory, we use this macro to assert that the
+ * kernel structure hasn't changed.
+ */
+#define SG_STRUCT_CHECK(sg) \
+ ((char *) &(sg).address - (char *) &(sg) != 0 || \
+ (char *) &(sg).length - (char *) &(sg) != 8 || \
+ sizeof((sg).address) != 4 || \
+ sizeof((sg).length) != 4 || \
+ sizeof(sg) != 12)
+
+/*
+ * "Static" structures. Note that these are NOT initialized
+ * to zero inside the kernel - we have to initialize them all
+ * explicitly.
+ *
+ * We support multiple adapter cards per interrupt, but keep a
+ * linked list of Scsi_Host structures for each IRQ. On an interrupt,
+ * use the IRQ as an index into aic7xxx_boards[] to locate the card
+ * information.
+ */
+static struct Scsi_Host *aic7xxx_boards[MAXIRQ + 1];
+
+/*
+ * When we detect and register the card, it is possible to
+ * have the card raise a spurious interrupt. Because we need
+ * to support multiple cards, we cannot tell which card caused
+ * the spurious interrupt. And, we might not even have added
+ * the card info to the linked list at the time the spurious
+ * interrupt gets raised. This variable is suppose to keep track
+ * of when we are registering a card and how many spurious
+ * interrupts we have encountered.
+ *
+ * 0 - do not allow spurious interrupts.
+ * 1 - allow 1 spurious interrupt
+ * 2 - have 1 spurious interrupt, do not allow any more.
+ *
+ * I've made it an integer instead of a boolean in case we
+ * want to allow more than one spurious interrupt for debugging
+ * purposes. Otherwise, it could just go from true to false to
+ * true (or something like that).
+ *
+ * When the driver detects the cards, we'll set the count to 1
+ * for each card detection and registration. After the registration
+ * of a card completes, we'll set the count back to 0. So far, it
+ * seems to be enough to allow a spurious interrupt only during
+ * card registration; if a spurious interrupt is going to occur,
+ * this is where it happens.
+ *
+ * We should be able to find a way to avoid getting the spurious
+ * interrupt. But until we do, we have to keep this ugly code.
+ */
+static int aic7xxx_spurious_count;
+
+/*
+ * The driver keeps up to four scb structures per card in memory. Only the
+ * first 25 bytes of the structure are valid for the hardware, the rest used
+ * for driver level bookkeeping.
+ */
+
+struct aic7xxx_scb {
+/* ------------ Begin hardware supported fields ---------------- */
+/* 0*/ unsigned char control;
+/* 1*/ unsigned char target_channel_lun; /* 4/1/3 bits */
+/* 2*/ unsigned char target_status;
+/* 3*/ unsigned char SG_segment_count;
+/* 4*/ unsigned char SG_list_pointer[4] __attribute__ ((packed));
+/* 8*/ unsigned char residual_SG_segment_count;
+/* 9*/ unsigned char residual_data_count[3];
+/*12*/ unsigned char data_pointer[4] __attribute__ ((packed));
+/*16*/ unsigned long data_count;
+/*20*/ unsigned char SCSI_cmd_pointer[4] __attribute__ ((packed));
+/*24*/ unsigned char SCSI_cmd_length;
+#define SCB_PIO_TRANSFER_SIZE 25 /*
+ * amount we need to upload/download
+ * via rep in/outsb to perform
+ * a request sense. The second
+ * RESERVED byte is initialized to
+ * 0 in getscb().
+ */
+/*25*/ u_char next_waiting; /* Used to thread SCBs awaiting selection. */
+ /*-----------------end of hardware supported fields----------------*/
+ struct aic7xxx_scb *next; /* next ptr when in free list */
+ Scsi_Cmnd *cmd; /* Scsi_Cmnd for this scb */
+#define SCB_FREE 0x00
+#define SCB_ACTIVE 0x01
+#define SCB_ABORTED 0x02
+#define SCB_DEVICE_RESET 0x04
+#define SCB_IMMED 0x08
+#define SCB_SENSE 0x10
+ int state; /* current state of scb */
+ unsigned int position; /* Position in scb array */
+ struct scatterlist sg;
+ struct scatterlist sense_sg;
+ unsigned char sense_cmd[6]; /* Allocate 6 characters for sense command */
+};
+
+static struct {
+ unsigned char errno;
+ const char *errmesg;
+} hard_error[] = {
+ { ILLHADDR, "Illegal Host Access" },
+ { ILLSADDR, "Illegal Sequencer Address referrenced" },
+ { ILLOPCODE, "Illegal Opcode in sequencer program" },
+ { PARERR, "Sequencer Ram Parity Error" }
+};
+
+static unsigned char
+generic_sense[] = { REQUEST_SENSE, 0, 0, 0, 255, 0 };
+
+/*
+ * The maximum number of SCBs we could have for ANY type
+ * of card. DON'T FORGET TO CHANGE THE SCB MASK IN THE
+ * SEQUENCER CODE IF THIS IS MODIFIED!
+ */
+#define AIC7XXX_MAXSCB 255
+
+/*
+ * Define a structure used for each host adapter, only one per IRQ.
+ */
+struct aic7xxx_host {
+ int base; /* card base address */
+ int maxscb; /* hardware SCBs */
+ int numscb; /* current number of scbs */
+ int extended; /* extended xlate? */
+ aha_type type; /* card type */
+ aha_chip_type chip_type; /* chip base type */
+ int ultra_enabled; /* Ultra SCSI speed enabled */
+ int chan_num; /* for 3940/3985, channel number */
+ aha_bus_type bus_type; /* normal/twin/wide bus */
+ unsigned char a_scanned; /* 0 not scanned, 1 scanned */
+ unsigned char b_scanned; /* 0 not scanned, 1 scanned */
+ unsigned int isr_count; /* Interrupt count */
+ volatile unsigned char unpause; /* unpause value for HCNTRL */
+ volatile unsigned char pause; /* pause value for HCNTRL */
+ volatile unsigned short needsdtr_copy; /* default config */
+ volatile unsigned short needsdtr;
+ volatile unsigned short sdtr_pending;
+ volatile unsigned short needwdtr_copy; /* default config */
+ volatile unsigned short needwdtr;
+ volatile unsigned short wdtr_pending;
+ volatile unsigned short discenable; /* Targets allowed to disconnect */
+ struct seeprom_config seeprom;
+ int have_seeprom;
+ struct Scsi_Host *next; /* allow for multiple IRQs */
+ struct aic7xxx_scb scb_array[AIC7XXX_MAXSCB]; /* active commands */
+ struct aic7xxx_scb *free_scb; /* list of free SCBs */
+#ifdef AIC7XXX_PROC_STATS
+ /*
+ * Statistics Kept:
+ *
+ * Total Xfers (count for each command that has a data xfer),
+ * broken down further by reads && writes.
+ *
+ * Binned sizes, writes && reads:
+ * < 512, 512, 1-2K, 2-4K, 4-8K, 8-16K, 16-32K, 32-64K, 64K-128K, > 128K
+ *
+ * Total amounts read/written above 512 bytes (amts under ignored)
+ */
+ struct aic7xxx_xferstats {
+ long xfers; /* total xfer count */
+ long w_total; /* total writes */
+ long w_total512; /* 512 byte blocks written */
+ long w_bins[10]; /* binned write */
+ long r_total; /* total reads */
+ long r_total512; /* 512 byte blocks read */
+ long r_bins[10]; /* binned reads */
+ } stats[2][16][8]; /* channel, target, lun */
+#endif /* AIC7XXX_PROC_STATS */
+};
+
+struct aic7xxx_host_config {
+ int irq; /* IRQ number */
+ int base; /* I/O base */
+ int maxscb; /* hardware SCBs */
+ int unpause; /* unpause value for HCNTRL */
+ int pause; /* pause value for HCNTRL */
+ int scsi_id; /* host SCSI ID */
+ int scsi_id_b; /* host SCSI ID B channel for twin cards */
+ int extended; /* extended xlate? */
+ int busrtime; /* bus release time */
+ int walk_scbs; /* external SCB RAM detected; walk the scb array */
+ aha_type type; /* card type */
+ aha_chip_type chip_type; /* chip base type */
+ int ultra_enabled; /* Ultra SCSI speed enabled */
+ int chan_num; /* for 3940/3985, channel number */
+ aha_bus_type bus_type; /* normal/twin/wide bus */
+ aha_status_type parity; /* bus parity enabled/disabled */
+ aha_status_type low_term; /* bus termination low byte */
+ aha_status_type high_term; /* bus termination high byte (wide cards only) */
+};
+
+/*
+ * Valid SCSIRATE values. (p. 3-17)
+ * Provides a mapping of tranfer periods in ns to the proper value to
+ * stick in the scsiscfr reg to use that transfer rate.
+ */
+static struct {
+ short period;
+ /* Rates in Ultra mode have bit 8 of sxfr set */
+#define ULTRA_SXFR 0x100
+ short rate;
+ const char *english;
+} aic7xxx_syncrates[] = {
+ { 50, 0x100, "20.0" },
+ { 62, 0x110, "16.0" },
+ { 75, 0x120, "13.4" },
+ { 100, 0x140, "10.0" },
+ { 100, 0x000, "10.0" },
+ { 125, 0x010, "8.0" },
+ { 150, 0x020, "6.67" },
+ { 175, 0x030, "5.7" },
+ { 200, 0x040, "5.0" },
+ { 225, 0x050, "4.4" },
+ { 250, 0x060, "4.0" },
+ { 275, 0x070, "3.6" }
+};
+
+static int num_aic7xxx_syncrates =
+ sizeof(aic7xxx_syncrates) / sizeof(aic7xxx_syncrates[0]);
+
+#ifdef CONFIG_PCI
+static int number_of_39xxs = 0;
+#endif CONFIG_PCI
+
+#ifdef AIC7XXX_DEBUG
+static void
+debug(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ printk(buf);
+ va_end(ap);
+}
+
+static void
+debug_config(struct aic7xxx_host_config *p)
+{
+ int host_conf, scsi_conf;
+ unsigned char brelease;
+ unsigned char dfthresh;
+
+ static int DFT[] = { 0, 50, 75, 100 };
+ static int SST[] = { 256, 128, 64, 32 };
+ static const char *BUSW[] = { "", "-TWIN", "-WIDE" };
+
+ host_conf = inb(HOSTCONF + p->base);
+ scsi_conf = inb(SCSICONF + p->base);
+
+ /*
+ * The 7870 gets the bus release time and data FIFO threshold
+ * from the serial EEPROM (stored in the config structure) and
+ * scsi_conf register respectively. The 7770 gets the bus
+ * release time and data FIFO threshold from the scsi_conf and
+ * host_conf registers respectively.
+ */
+ if (p->chip_type == AIC_777x)
+ {
+ dfthresh = (host_conf >> 6);
+ }
+ else
+ {
+ dfthresh = (scsi_conf >> 6);
+ }
+
+ brelease = p->busrtime;
+ if (brelease == 0)
+ {
+ brelease = 2;
+ }
+
+ switch (p->type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ printk("%s%s AT EISA SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
+ p->base >> 12);
+ break;
+
+ case AIC_284x:
+ printk("%s%s AT VLB SLOT %d:\n", board_names[p->type], BUSW[p->bus_type],
+ p->base >> 12);
+ break;
+
+ case AIC_7850:
+ case AIC_7870:
+ case AIC_7871:
+ case AIC_7872:
+ case AIC_7873:
+ case AIC_7874:
+ case AIC_7880:
+ case AIC_7881:
+ case AIC_7882:
+ case AIC_7883:
+ case AIC_7884:
+ printk("%s%s (PCI-bus):\n", board_names[p->type], BUSW[p->bus_type]);
+ break;
+
+ default:
+ panic("aic7xxx: (debug_config) internal error.\n");
+ }
+
+ printk(" irq %d\n"
+ " bus release time %d bclks\n"
+ " data fifo threshold %d%%\n",
+ p->irq,
+ brelease,
+ DFT[dfthresh]);
+
+ printk(" SCSI CHANNEL A:\n"
+ " scsi id %d\n"
+ " scsi selection timeout %d ms\n"
+ " scsi bus reset at power-on %sabled\n",
+ scsi_conf & 0x07,
+ SST[(scsi_conf >> 3) & 0x03],
+ (scsi_conf & 0x40) ? "en" : "dis");
+
+ if ((p->chip_type == AIC_777x) && (p->parity == AIC_UNKNOWN))
+ {
+ /*
+ * Set the parity for 7770 based cards.
+ */
+ p->parity = (scsi_conf & 0x20) ? AIC_ENABLED : AIC_DISABLED;
+ }
+ if (p->parity != AIC_UNKNOWN)
+ {
+ printk(" scsi bus parity %sabled\n",
+ (p->parity == AIC_ENABLED) ? "en" : "dis");
+ }
+
+ if ((p->type == AIC_7770) || (p->type == AIC_7771))
+ {
+ p->low_term = (scsi_conf & 0x80) ? AIC_ENABLED : AIC_DISABLED;
+ }
+ if (p->low_term != AIC_UNKNOWN)
+ {
+ printk(" scsi bus termination (low byte) %sabled\n",
+ (p->low_term == AIC_ENABLED) ? "en" : "dis");
+ }
+ if ((p->bus_type == AIC_WIDE) && (p->high_term != AIC_UNKNOWN))
+ {
+ printk(" scsi bus termination (high byte) %sabled\n",
+ (p->high_term == AIC_ENABLED) ? "en" : "dis");
+ }
+}
+
+#if 0
+static void
+debug_scb(struct aic7xxx_scb *scb)
+{
+ printk("control 0x%x, tcl 0x%x, sg_count %d, sg_ptr 0x%x, cmdp 0x%x, cmdlen %d\n",
+ scb->control, scb->target_channel_lun, scb->SG_segment_count,
+ (scb->SG_list_pointer[3] << 24) | (scb->SG_list_pointer[2] << 16) |
+ (scb->SG_list_pointer[1] << 8) | scb->SG_list_pointer[0],
+ (scb->SCSI_cmd_pointer[3] << 24) | (scb->SCSI_cmd_pointer[2] << 16) |
+ (scb->SCSI_cmd_pointer[1] << 8) | scb->SCSI_cmd_pointer[0],
+ scb->SCSI_cmd_length);
+ printk("reserved 0x%x, target status 0x%x, resid SG count %d, resid data count %d\n",
+ (scb->RESERVED[1] << 8) | scb->RESERVED[0], scb->target_status,
+ scb->residual_SG_segment_count, scb->residual_data_count);
+ printk("data ptr 0x%x, data count %d, next waiting %d\n",
+ (scb->data_pointer[3] << 24) | (scb->data_pointer[2] << 16) |
+ (scb->data_pointer[1] << 8) | scb->data_pointer[0],
+ scb->data_count, scb->next_waiting);
+ printk("next ptr 0x%lx, Scsi Cmnd 0x%lx, state 0x%x, position %d\n",
+ (unsigned long) scb->next, (unsigned long) scb->cmd, scb->state,
+ scb->position);
+}
+#endif
+
+#else
+# define debug(fmt, args...)
+# define debug_config(x)
+# define debug_scb(x)
+#endif AIC7XXX_DEBUG
+
+/*
+ * XXX - these options apply unilaterally to _all_ 274x/284x/294x
+ * cards in the system. This should be fixed, but then,
+ * does anyone really have more than one in a machine?
+ */
+static unsigned int aic7xxx_extended = 0; /* extended translation on? */
+static unsigned int aic7xxx_no_reset = 0; /* no resetting of SCSI bus */
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_setup
+ *
+ * Description:
+ * Handle Linux boot parameters. This routine allows for assigning a value
+ * to a parameter with a ':' between the parameter and the value.
+ * ie. aic7xxx=unpause:0x0A,extended
+ *-F*************************************************************************/
+void
+aic7xxx_setup(char *s, int *dummy)
+{
+ int i, n;
+ char *p;
+
+ static struct {
+ const char *name;
+ unsigned int *flag;
+ } options[] = {
+ { "extended", &aic7xxx_extended },
+ { "no_reset", &aic7xxx_no_reset },
+ { NULL, NULL }
+ };
+
+ for (p = strtok(s, ","); p; p = strtok(NULL, ","))
+ {
+ for (i = 0; options[i].name; i++)
+ {
+ n = strlen(options[i].name);
+ if (!strncmp(options[i].name, p, n))
+ {
+ if (p[n] == ':')
+ {
+ *(options[i].flag) = simple_strtoul(p + n + 1, NULL, 0);
+ }
+ else
+ {
+ *(options[i].flag) = !0;
+ }
+ }
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_loadseq
+ *
+ * Description:
+ * Load the sequencer code into the controller memory.
+ *-F*************************************************************************/
+static void
+aic7xxx_loadseq(int base)
+{
+ static unsigned char seqprog[] = {
+ /*
+ * Each sequencer instruction is 29 bits
+ * long (fill in the excess with zeroes)
+ * and has to be loaded from least -> most
+ * significant byte, so this table has the
+ * byte ordering reversed.
+ */
+# include "aic7xxx_seq.h"
+ };
+
+ /*
+ * When the AIC-7770 is paused (as on chip reset), the
+ * sequencer address can be altered and a sequencer
+ * program can be loaded by writing it, byte by byte, to
+ * the sequencer RAM port - the Adaptec documentation
+ * recommends using REP OUTSB to do this, hence the inline
+ * assembly. Since the address autoincrements as we load
+ * the program, reset it back to zero afterward. Disable
+ * sequencer RAM parity error detection while loading, and
+ * make sure the LOADRAM bit is enabled for loading.
+ */
+ outb(PERRORDIS | SEQRESET | LOADRAM, SEQCTL + base);
+
+ outsb(SEQRAM + base, seqprog, sizeof(seqprog));
+
+ /*
+ * WARNING! This is a magic sequence! After extensive
+ * experimentation, it seems that you MUST turn off the
+ * LOADRAM bit before you play with SEQADDR again, else
+ * you will end up with parity errors being flagged on
+ * your sequencer program. (You would also think that
+ * turning off LOADRAM and setting SEQRESET to reset the
+ * address to zero would work, but you need to do it twice
+ * for it to take effect on the address. Timing problem?)
+ */
+ do {
+ /*
+ * Actually, reset it until
+ * the address shows up as
+ * zero just to be safe..
+ */
+ outb(SEQRESET | FASTMODE, SEQCTL + base);
+ } while ((inb(SEQADDR0 + base) != 0) && (inb(SEQADDR1 + base) != 0));
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_delay
+ *
+ * Description:
+ * Delay for specified amount of time.
+ *-F*************************************************************************/
+static void
+aic7xxx_delay(int seconds)
+{
+ unsigned long i;
+
+ i = jiffies + (seconds * HZ); /* compute time to stop */
+
+ while (jiffies < i)
+ {
+ ; /* Do nothing! */
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * rcs_version
+ *
+ * Description:
+ * Return a string containing just the RCS version number from either
+ * an Id or Revison RCS clause.
+ *-F*************************************************************************/
+const char *
+rcs_version(const char *version_info)
+{
+ static char buf[10];
+ char *bp, *ep;
+
+ bp = NULL;
+ strcpy(buf, "????");
+ if (!strncmp(version_info, "$Id: ", 5))
+ {
+ if ((bp = strchr(version_info, ' ')) != NULL)
+ {
+ bp++;
+ if ((bp = strchr(bp, ' ')) != NULL)
+ {
+ bp++;
+ }
+ }
+ }
+ else
+ {
+ if (!strncmp(version_info, "$Revision: ", 11))
+ {
+ if ((bp = strchr(version_info, ' ')) != NULL)
+ {
+ bp++;
+ }
+ }
+ }
+
+ if (bp != NULL)
+ {
+ if ((ep = strchr(bp, ' ')) != NULL)
+ {
+ register int len = ep - bp;
+
+ strncpy(buf, bp, len);
+ buf[len] = '\0';
+ }
+ }
+
+ return buf;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_info
+ *
+ * Description:
+ * Return a string describing the driver.
+ *-F*************************************************************************/
+const char *
+aic7xxx_info(struct Scsi_Host *notused)
+{
+ static char buffer[128];
+
+ strcpy(buffer, "Adaptec AHA274x/284x/294x (EISA/VLB/PCI-Fast SCSI) ");
+ strcat(buffer, rcs_version(AIC7XXX_C_VERSION));
+ strcat(buffer, "/");
+ strcat(buffer, rcs_version(AIC7XXX_H_VERSION));
+ strcat(buffer, "/");
+ strcat(buffer, rcs_version(AIC7XXX_SEQ_VER));
+
+ return buffer;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_length
+ *
+ * Description:
+ * How much data should be transferred for this SCSI command? Stop
+ * at segment sg_last if it's a scatter-gather command so we can
+ * compute underflow easily.
+ *-F*************************************************************************/
+static unsigned
+aic7xxx_length(Scsi_Cmnd *cmd, int sg_last)
+{
+ int i, segments;
+ unsigned length;
+ struct scatterlist *sg;
+
+ segments = cmd->use_sg - sg_last;
+ sg = (struct scatterlist *) cmd->buffer;
+
+ if (cmd->use_sg)
+ {
+ for (i = length = 0; (i < cmd->use_sg) && (i < segments); i++)
+ {
+ length += sg[i].length;
+ }
+ }
+ else
+ {
+ length = cmd->request_bufflen;
+ }
+
+ return (length);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_scsirate
+ *
+ * Description:
+ * Look up the valid period to SCSIRATE conversion in our table
+ *-F*************************************************************************/
+static void
+aic7xxx_scsirate(struct aic7xxx_host *p, unsigned char *scsirate,
+ short period, unsigned char offset,
+ int target, char channel)
+{
+ int i;
+
+ for (i = 0; i < num_aic7xxx_syncrates; i++)
+ {
+ if ((aic7xxx_syncrates[i].period - period) >= 0)
+ {
+ /*
+ * Watch out for Ultra speeds when ultra is not enabled and
+ * vice-versa.
+ */
+ if (p->ultra_enabled)
+ {
+ if (!(aic7xxx_syncrates[i].rate & ULTRA_SXFR))
+ {
+ printk ("aic7xxx: Target %d, channel %c, requests %sMHz transfers, "
+ "but adapter in Ultra mode can only sync at 10MHz or "
+ "above.\n", target, channel, aic7xxx_syncrates[i].english);
+ break; /* Use asynchronous transfers. */
+ }
+ }
+ else
+ {
+ /*
+ * Check for an Ultra device trying to negotiate an Ultra rate
+ * on an adapter with Ultra mode disabled.
+ */
+ if (aic7xxx_syncrates[i].rate & ULTRA_SXFR)
+ {
+ /*
+ * This should only happen if the driver is the first to negotiate
+ * and chooses a high rate. We'll just move down the table until
+ * we hit a non Ultra speed.
+ */
+ continue;
+ }
+ }
+ *scsirate = (aic7xxx_syncrates[i].rate) | (offset & 0x0F);
+ printk("aic7xxx: Target %d, channel %c, now synchronous at %sMHz, "
+ "offset(0x%x).\n",
+ target, channel, aic7xxx_syncrates[i].english, offset);
+ return;
+ }
+ }
+
+ /*
+ * Default to asynchronous transfer
+ */
+ *scsirate = 0;
+ printk("aic7xxx: Target %d, channel %c, using asynchronous transfers.\n",
+ target, channel);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_putscb
+ *
+ * Description:
+ * Transfer a SCB to the controller.
+ *-F*************************************************************************/
+static inline void
+aic7xxx_putscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ unsigned char curscb;
+ int base = p->base;
+
+ curscb = inb(SCBPTR + base);
+ outb(scb->position, SCBPTR + base);
+ outb(SCBAUTO, SCBCNT + base);
+
+ /*
+ * By turning on the SCB auto increment, any reference
+ * to the SCB I/O space postincrements the SCB address
+ * we're looking at. So turn this on and dump the relevant
+ * portion of the SCB to the card.
+ *
+ * We can do 16bit transfers on all but 284x.
+ */
+ if (p->type == AIC_284x)
+ {
+ outsb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+ }
+ else
+ {
+ outsl(SCBARRAY + base, scb, (SCB_PIO_TRANSFER_SIZE + 3) / 4);
+ }
+
+ outb(0, SCBCNT + base);
+ outb(curscb, SCBPTR + base);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_getscb
+ *
+ * Description:
+ * Get a SCB from the controller.
+ *-F*************************************************************************/
+static inline void
+aic7xxx_getscb(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ int base = p->base;
+
+ /*
+ * This is almost identical to aic7xxx_putscb().
+ */
+ outb(SCBAUTO, SCBCNT + base);
+ insb(SCBARRAY + base, scb, SCB_PIO_TRANSFER_SIZE);
+ outb(0, SCBCNT + base);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_match_scb
+ *
+ * Description:
+ * Checks to see if an scb matches the target/channel as specified.
+ * If target is ALL_TARGETS (-1), then we're looking for any device
+ * on the specified channel; this happens when a channel is going
+ * to be reset and all devices on that channel must be aborted.
+ *-F*************************************************************************/
+static int
+aic7xxx_match_scb(struct aic7xxx_scb *scb, int target, char channel)
+{
+ int targ = (scb->target_channel_lun >> 4) & 0x0F;
+ char chan = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (match_scb) comparing target/channel %d/%c to scb %d/%c\n",
+ target, channel, targ, chan);
+#endif
+ if (target == ALL_TARGETS)
+ {
+ return (chan == channel);
+ }
+ else
+ {
+ return ((chan == channel) && (targ == target));
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_busy_target
+ *
+ * Description:
+ * Set the specified target active.
+ *-F*************************************************************************/
+static void
+aic7xxx_busy_target(unsigned char target, char channel, int base)
+{
+ unsigned char active;
+ unsigned long active_port = ACTIVE_A + base;
+
+ if ((target > 0x07) || (channel == 'B'))
+ {
+ /*
+ * targets on the Second channel or above id 7 store info in byte two
+ * of ACTIVE
+ */
+ active_port++;
+ }
+ active = inb(active_port);
+ active |= (0x01 << (target & 0x07));
+ outb(active, active_port);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_unbusy_target
+ *
+ * Description:
+ * Set the specified target inactive.
+ *-F*************************************************************************/
+static void
+aic7xxx_unbusy_target(unsigned char target, char channel, int base)
+{
+ unsigned char active;
+ unsigned long active_port = ACTIVE_A + base;
+
+#ifdef 0
+ printk ("aic7xxx: (unbusy_target) target/channel %d/%c\n",
+ target, channel);
+#endif
+ if ((target > 0x07) || (channel == 'B'))
+ {
+ /*
+ * targets on the Second channel or above id 7 store info in byte two
+ * of ACTIVE
+ */
+ active_port++;
+ }
+ active = inb(active_port);
+ active &= ~(0x01 << (target & 0x07));
+ outb(active, active_port);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_done
+ *
+ * Description:
+ * Calls the higher level scsi done function and frees the scb.
+ *-F*************************************************************************/
+static void
+aic7xxx_done(struct aic7xxx_host *p, struct aic7xxx_scb *scb)
+{
+ long flags;
+ Scsi_Cmnd *cmd = scb->cmd;
+
+#ifdef 0
+ printk ("aic7xxx: (done) target/channel %d/%d\n",
+ cmd->target, cmd->channel);
+#endif
+ /*
+ * This is a critical section, since we don't want the
+ * queue routine mucking with the host data.
+ */
+ save_flags(flags);
+ cli();
+
+ /*
+ * Process the command after marking the scb as free
+ * and adding it to the free list.
+ */
+ scb->state = SCB_FREE;
+ scb->next = p->free_scb;
+ p->free_scb = scb;
+ scb->cmd = NULL;
+
+ restore_flags(flags);
+
+ cmd->scsi_done(cmd);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_add_waiting_scb
+ *
+ * Description:
+ * Add this SCB to the "waiting for selection" list.
+ *-F*************************************************************************/
+static void
+aic7xxx_add_waiting_scb(u_long base,
+ struct aic7xxx_scb *scb,
+ insert_type where)
+{
+ unsigned char head;
+ unsigned char curscb;
+
+ curscb = inb(SCBPTR + base);
+ head = inb(WAITING_SCBH + base);
+ if (head == SCB_LIST_NULL)
+ {
+ /*
+ * List was empty
+ */
+ head = scb->position;
+ }
+ else
+ {
+ if (where == LIST_HEAD)
+ {
+ outb(scb->position, SCBPTR + base);
+ outb(head, SCB_NEXT_WAITING + base);
+ head = scb->position;
+ }
+ else
+ {
+ /* where == LIST_SECOND */
+ unsigned char third_scb;
+
+ outb(head, SCBPTR + base);
+ third_scb = inb(SCB_NEXT_WAITING + base);
+ outb(scb->position, SCB_NEXT_WAITING + base);
+ outb(scb->position, SCBPTR + base);
+ outb(third_scb, SCB_NEXT_WAITING + base);
+ }
+ }
+ outb(head, WAITING_SCBH + base);
+ outb(curscb, SCBPTR + base);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort_waiting_scb
+ *
+ * Description:
+ * Manipulate the waiting for selection list and return the
+ * scb that follows the one that we remove.
+ *-F*************************************************************************/
+static unsigned char
+aic7xxx_abort_waiting_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ unsigned char prev, unsigned char timedout_scb)
+{
+ unsigned char curscb, next;
+ int target = (scb->target_channel_lun >> 4) & 0x0F;
+ char channel = (scb->target_channel_lun & SELBUSB) ? 'B' : 'A';
+ int base = p->base;
+
+ /*
+ * Select the SCB we want to abort and
+ * pull the next pointer out of it.
+ */
+ curscb = inb(SCBPTR + base);
+ outb(scb->position, SCBPTR + base);
+ next = inb(SCB_NEXT_WAITING + base);
+
+ /*
+ * Clear the necessary fields
+ */
+ outb(0, SCBARRAY + base);
+ outb(SCB_LIST_NULL, SCB_NEXT_WAITING + base);
+ aic7xxx_unbusy_target(target, channel, base);
+
+ /*
+ * Update the waiting list
+ */
+ if (prev == SCB_LIST_NULL)
+ {
+ /*
+ * First in the list
+ */
+ outb(next, WAITING_SCBH + base);
+ }
+ else
+ {
+ /*
+ * Select the scb that pointed to us and update its next pointer.
+ */
+ outb(prev, SCBPTR + base);
+ outb(next, SCB_NEXT_WAITING + base);
+ }
+ /*
+ * Update the tail pointer
+ */
+ if (inb(WAITING_SCBT + base) == scb->position)
+ {
+ outb(prev, WAITING_SCBT + base);
+ }
+
+ /*
+ * Point us back at the original scb position
+ * and inform the SCSI system that the command
+ * has been aborted.
+ */
+ outb(curscb, SCBPTR + base);
+ scb->state |= SCB_ABORTED;
+ scb->cmd->result = (DID_RESET << 16);
+ aic7xxx_done(p, scb);
+
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_waiting_scb) target/channel %d/%c, prev %d, "
+ "to_scb %d, next %d\n", target, channel, prev, timedout_scb, next);
+#endif
+ return (next);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_device
+ *
+ * Description:
+ * The device at the given target/channel has been reset. Abort
+ * all active and queued scbs for that target/channel.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset_device(struct aic7xxx_host *p, int target, char channel,
+ unsigned char timedout_scb)
+{
+ int base = p->base;
+ struct aic7xxx_scb *scb;
+ unsigned char active_scb;
+ int i = 0;
+ int found = 0;
+
+ /*
+ * Restore this when we're done
+ */
+ active_scb = inb(SCBPTR + base);
+
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (reset_device) target/channel %d/%c, to_scb %d, "
+ "active_scb %d\n", target, channel, timedout_scb, active_scb);
+#endif
+ /*
+ * Search the QINFIFO.
+ */
+ {
+ int saved_queue[AIC7XXX_MAXSCB];
+ int queued = inb(QINCNT + base);
+
+ for (i = 0; i < (queued - found); i++)
+ {
+ saved_queue[i] = inb(QINFIFO + base);
+ scb = &(p->scb_array[saved_queue[i]]);
+ if (aic7xxx_match_scb(scb, target, channel))
+ {
+ /*
+ * We found an scb that needs to be aborted.
+ */
+ scb->state |= SCB_ABORTED;
+ scb->cmd->result = (DID_RESET << 16);
+ aic7xxx_done(p, scb);
+ outb(scb->position, SCBPTR + base);
+ outb(0, SCBARRAY + base);
+ i--;
+ found++;
+ }
+ }
+ /*
+ * Now put the saved scbs back.
+ */
+ for (queued = 0; queued < i; queued++)
+ {
+ outb(saved_queue[queued], QINFIFO + base);
+ }
+ }
+
+ /*
+ * Search waiting for selection list.
+ */
+ {
+ unsigned char next, prev;
+
+ next = inb(WAITING_SCBH + base); /* Start at head of list. */
+ prev = SCB_LIST_NULL;
+
+ while (next != SCB_LIST_NULL)
+ {
+ scb = &(p->scb_array[next]);
+ /*
+ * Select the SCB.
+ */
+ if (aic7xxx_match_scb(scb, target, channel))
+ {
+ next = aic7xxx_abort_waiting_scb(p, scb, prev, timedout_scb);
+ found++;
+ }
+ else
+ {
+ outb(scb->position, SCBPTR + base);
+ prev = next;
+ next = inb(SCB_NEXT_WAITING + base);
+ }
+ }
+ }
+
+ /*
+ * Go through the entire SCB array now and look for
+ * commands for this target that are active. These
+ * are other (most likely tagged) commands that
+ * were disconnected when the reset occured.
+ */
+ for (i = 0; i < p->numscb; i++)
+ {
+ scb = &(p->scb_array[i]);
+ if ((scb->state & SCB_ACTIVE) && aic7xxx_match_scb(scb, target, channel))
+ {
+ /*
+ * Ensure the target is "free"
+ */
+ aic7xxx_unbusy_target(target, channel, base);
+ outb(scb->position, SCBPTR + base);
+ outb(0, SCBARRAY + base);
+ scb->state |= SCB_ABORTED;
+ scb->cmd->result = (DID_RESET << 16);
+ aic7xxx_done(p, scb);
+ found++;
+ }
+ }
+
+ outb(active_scb, SCBPTR + base);
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_current_bus
+ *
+ * Description:
+ * Reset the current SCSI bus.
+ *-F*************************************************************************/
+static void
+aic7xxx_reset_current_bus(int base)
+{
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (reset_current_bus)\n");
+#endif
+ outb(SCSIRSTO, SCSISEQ + base);
+ udelay(1000);
+ outb(0, SCSISEQ + base);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset_channel
+ *
+ * Description:
+ * Reset the channel.
+ *-F*************************************************************************/
+static int
+aic7xxx_reset_channel(struct aic7xxx_host *p, char channel,
+ unsigned char timedout_scb)
+{
+ int base = p->base;
+ unsigned char sblkctl;
+ char cur_channel;
+ unsigned long offset, offset_max;
+ int found;
+
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (reset_channel) channel %c, to_scb %d\n",
+ channel, timedout_scb);
+#endif
+ /*
+ * Clean up all the state information for the
+ * pending transactions on this bus.
+ */
+ found = aic7xxx_reset_device(p, ALL_TARGETS, channel, timedout_scb);
+
+ if (channel == 'B')
+ {
+ p->needsdtr |= (p->needsdtr_copy & 0xFF00);
+ p->sdtr_pending &= 0x00FF;
+ outb(0, ACTIVE_B + base);
+ offset = TARG_SCRATCH + base + 8;
+ offset_max = TARG_SCRATCH + base + 16;
+ }
+ else
+ {
+ if (p->bus_type == AIC_WIDE)
+ {
+ p->needsdtr = p->needsdtr_copy;
+ p->needwdtr = p->needwdtr_copy;
+ p->sdtr_pending = 0x0;
+ p->wdtr_pending = 0x0;
+ outb(0, ACTIVE_A + base);
+ outb(0, ACTIVE_B + base);
+ offset = TARG_SCRATCH + base;
+ offset_max = TARG_SCRATCH + base + 16;
+ }
+ else
+ {
+ p->needsdtr |= (p->needsdtr_copy & 0x00FF);
+ p->sdtr_pending &= 0xFF00;
+ outb(0, ACTIVE_A + base);
+ offset = TARG_SCRATCH + base;
+ offset_max = TARG_SCRATCH + base + 8;
+ }
+ }
+ while (offset < offset_max)
+ {
+ /*
+ * Revert to async/narrow transfers
+ * until we renegotiate.
+ */
+ u_char targ_scratch;
+ targ_scratch = inb(offset);
+ targ_scratch &= SXFR;
+ outb(targ_scratch, offset);
+ offset++;
+ }
+
+ /*
+ * Reset the bus and unpause/restart the controller
+ */
+
+ /*
+ * Case 1: Command for another bus is active
+ */
+ sblkctl = inb(SBLKCTL + base);
+ cur_channel = (sblkctl & SELBUSB) ? 'B' : 'A';
+ if (cur_channel != channel)
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (reset_channel) Stealthily resetting channel %c\n",
+ channel);
+#endif
+ /*
+ * Stealthily reset the other bus without upsetting the current bus
+ */
+ outb(sblkctl ^ SELBUSB, SBLKCTL + base);
+ aic7xxx_reset_current_bus(base);
+ outb(sblkctl, SBLKCTL + base);
+
+ UNPAUSE_SEQUENCER(p);
+ }
+ /*
+ * Case 2: A command from this bus is active or we're idle
+ */
+ else
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (reset_channel) Resetting current channel %c\n",
+ channel);
+#endif
+ aic7xxx_reset_current_bus(base);
+ RESTART_SEQUENCER(p);
+ }
+
+ return found;
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_isr
+ *
+ * Description:
+ * SCSI controller interrupt handler.
+ *
+ * NOTE: Since we declared this using SA_INTERRUPT, interrupts should
+ * be disabled all through this function unless we say otherwise.
+ *-F*************************************************************************/
+static void
+aic7xxx_isr(int irq, struct pt_regs * regs)
+{
+ int base, intstat;
+ struct aic7xxx_host *p;
+ struct aic7xxx_scb *scb;
+ unsigned char ha_flags;
+ short transfer;
+ unsigned char scsi_id, bus_width;
+ unsigned char offset, rate, scratch, scratch_offset;
+ unsigned char max_offset, rej_byte;
+ unsigned short target_mask;
+ char channel;
+ void *addr;
+ int actual;
+ int scb_index;
+ Scsi_Cmnd *cmd;
+
+ p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+
+ /*
+ * Search for the host with a pending interrupt. If we can't find
+ * one, then we've encountered a spurious interrupt.
+ */
+ while ((p != NULL) && !(inb(INTSTAT + p->base) & INT_PEND))
+ {
+ if (p->next == NULL)
+ {
+ p = NULL;
+ }
+ else
+ {
+ p = (struct aic7xxx_host *) p->next->hostdata;
+ }
+ }
+
+ if (p == NULL)
+ {
+ if (aic7xxx_spurious_count == 1)
+ {
+ aic7xxx_spurious_count = 2;
+ printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
+ return;
+ }
+ else
+ {
+ /*
+ * The best we can do is to set p back to head of list and process
+ * the erroneous interrupt - most likely a BRKADRINT.
+ */
+ p = (struct aic7xxx_host *) aic7xxx_boards[irq]->hostdata;
+ }
+ }
+
+ /*
+ * Keep track of interrupts for /proc/scsi
+ */
+ p->isr_count++;
+
+ if (!p->a_scanned && (p->isr_count == 1))
+ {
+ /*
+ * We must only have one card at this IRQ and it must have been
+ * added to the board data before the spurious interrupt occurred.
+ * It is sufficient that we check isr_count and not the spurious
+ * interrupt count.
+ */
+ printk("aic7xxx: (aic7xxx_isr) Encountered spurious interrupt.\n");
+ return;
+ }
+
+ base = p->base;
+ /*
+ * Handle all the interrupt sources - especially for SCSI
+ * interrupts, we won't get a second chance at them.
+ */
+ intstat = inb(INTSTAT + base);
+
+ if (intstat & BRKADRINT)
+ {
+ int i;
+ unsigned char errno = inb(ERROR + base);
+
+ printk("aic7xxx: (aic7xxx_isr) BRKADRINT error(0x%x):\n", errno);
+ for (i = 0; i < NUMBER(hard_error); i++)
+ {
+ if (errno & hard_error[i].errno)
+ {
+ printk(" %s\n", hard_error[i].errmesg);
+ }
+ }
+
+ panic("aic7xxx: (aic7xxx_isr) BRKADRINT, error(0x%x) seqaddr(0x%x).\n",
+ inb(ERROR + base), (inb(SEQADDR1 + base) << 8) | inb(SEQADDR0 + base));
+ }
+
+ if (intstat & SEQINT)
+ {
+ /*
+ * Although the sequencer is paused immediately on
+ * a SEQINT, an interrupt for a SCSIINT condition will
+ * unpaused the sequencer before this point.
+ */
+ PAUSE_SEQUENCER(p);
+
+ scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
+ scratch_offset = scsi_id;
+ channel = 'A';
+ if (inb(SBLKCTL + base) & SELBUSB)
+ {
+ channel = 'B';
+ scratch_offset += 8;
+ }
+ target_mask = (0x01 << scratch_offset);
+
+ switch (intstat & SEQINT_MASK)
+ {
+ case BAD_PHASE:
+ panic("aic7xxx: (aic7xxx_isr) Unknown scsi bus phase.\n");
+ break;
+
+ case SEND_REJECT:
+ rej_byte = inb(REJBYTE + base);
+ if ((rej_byte & 0xF0) == 0x20)
+ {
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ printk("aic7xxx: Warning - Tagged message received without identify."
+ "Disabling tagged commands for target %d channel %c.\n",
+ scsi_id, channel);
+ scb->cmd->device->tagged_supported = 0;
+ scb->cmd->device->tagged_queue = 0;
+ }
+ else
+ {
+ debug("aic7xxx: Warning - Rejecting unknown message (0x%x) received "
+ "from target %d channel %c.\n", rej_byte, scsi_id, channel);
+ }
+ break;
+
+ case NO_IDENT:
+ panic("aic7xxx: Target %d, channel %c, did not send an IDENTIFY "
+ "message. SAVED_TCL(0x%x).\n",
+ scsi_id, channel, inb(SAVED_TCL + base));
+ break;
+
+ case NO_MATCH:
+ printk("aic7xxx: No active SCB for reconnecting target %d, "
+ "channel %c - Issuing ABORT. SAVED_TCL(0x%x).\n",
+ scsi_id, channel, inb(SAVED_TCL + base));
+ aic7xxx_unbusy_target(scsi_id, channel, base);
+ outb(0, SCBARRAY + base);
+ outb(CLRSELTIMEO, CLRSINT1 + base);
+ RESTART_SEQUENCER(p);
+ break;
+
+ case SDTR_MSG:
+ /*
+ * Help the sequencer to translate the negotiated
+ * transfer rate. Transfer is 1/4 the period
+ * in ns as is returned by the sync negotiation
+ * message. So, we must multiply by four.
+ */
+ transfer = (inb(ARG_1 + base) << 2);
+ offset = inb(ACCUM + base);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
+ /*
+ * The maximum offset for a wide device is 0x08; for a
+ * 8-bit bus device the maximum offset is 0x0F.
+ */
+ if (scratch & WIDEXFER)
+ {
+ max_offset = 0x08;
+ }
+ else
+ {
+ max_offset = 0x0F;
+ }
+ aic7xxx_scsirate(p, &rate, transfer, MIN(offset, max_offset),
+ scsi_id, channel);
+ /*
+ * Preserve the wide transfer flag.
+ */
+ scratch = rate | (scratch & WIDEXFER);
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
+ if ((scratch & 0x0F) == 0)
+ { /*
+ * The requested rate was so low that asynchronous transfers
+ * are faster (not to mention the controller won't support
+ * them), so we issue a reject to ensure we go to asynchronous
+ * transfers.
+ */
+ outb(SEND_REJ, RETURN_1 + base);
+ }
+ else
+ {
+ /*
+ * See if we initiated Sync Negotiation
+ */
+ if (p->sdtr_pending & target_mask)
+ {
+ /*
+ * Don't send an SDTR back to the target.
+ */
+ outb(0, RETURN_1 + base);
+ }
+ else
+ {
+ /*
+ * Send our own SDTR in reply.
+ */
+ printk("aic7xxx: Sending SDTR!!\n");
+ outb(SEND_SDTR, RETURN_1 + base);
+ }
+ }
+ /*
+ * Clear the flags.
+ */
+ p->needsdtr &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
+ break;
+
+ case WDTR_MSG:
+ {
+ bus_width = inb(ARG_1 + base);
+ printk("aic7xxx: Received MSG_WDTR, Target %d, channel %c "
+ "needwdtr(0x%x).\n", scsi_id, channel, p->needwdtr);
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
+
+ if (p->wdtr_pending & target_mask)
+ {
+ /*
+ * Don't send an WDTR back to the target, since we asked first.
+ */
+ outb(0, RETURN_1 + base);
+ switch (bus_width)
+ {
+ case BUS_8_BIT:
+ scratch &= 0x7F;
+ break;
+
+ case BUS_16_BIT:
+ printk("aic7xxx: Target %d, channel %c, using 16 bit "
+ "transfers.\n", scsi_id, channel);
+ scratch |= 0x80;
+ break;
+
+ case BUS_32_BIT:
+ outb(SEND_REJ, RETURN_1 + base);
+ printk("aic7xxx: Target %d, channel %c, requesting 32 bit "
+ "transfers, rejecting...\n", scsi_id, channel);
+ break;
+ }
+ }
+ else
+ {
+ /*
+ * Send our own WDTR in reply.
+ */
+ printk("aic7xxx: Will send WDTR!!\n");
+ switch (bus_width)
+ {
+ case BUS_8_BIT:
+ scratch &= 0x7F;
+ break;
+
+ case BUS_32_BIT:
+ /*
+ * Negotiate 16 bits.
+ */
+ bus_width = BUS_16_BIT;
+ /* Yes, we mean to fall thru here. */
+
+ case BUS_16_BIT:
+ printk("aic7xxx: Target %d, channel %c, using 16 bit "
+ "transfers.\n", scsi_id, channel);
+ scratch |= 0x80;
+ break;
+ }
+ outb(bus_width | SEND_WDTR, RETURN_1 + base);
+ }
+ p->needwdtr &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
+ break;
+ }
+
+ case REJECT_MSG:
+ {
+ /*
+ * What we care about here is if we had an
+ * outstanding SDTR or WDTR message for this
+ * target. If we did, this is a signal that
+ * the target is refusing negotiation.
+ */
+
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
+
+ if (p->wdtr_pending & target_mask)
+ {
+ /*
+ * note 8bit xfers and clear flag
+ */
+ scratch &= 0x7F;
+ p->needwdtr &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ printk("aic7xxx: Target %d, channel %c, refusing WIDE negotiation. "
+ "Using 8 bit transfers.\n", scsi_id, channel);
+ }
+ else
+ {
+ if (p->sdtr_pending & target_mask)
+ {
+ /*
+ * note asynch xfers and clear flag
+ */
+ scratch &= 0xF0;
+ p->needsdtr &= ~target_mask;
+ p->sdtr_pending &= ~target_mask;
+ printk("aic7xxx: Target %d, channel %c, refusing synchronous "
+ "negotiation. Using asynchronous transfers.\n",
+ scsi_id, channel);
+ }
+ /*
+ * Otherwise, we ignore it.
+ */
+ }
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ outb(scratch, SCSIRATE + base);
+ break;
+ }
+
+ case BAD_STATUS:
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ outb(0, RETURN_1 + base); /* CHECK_CONDITION may change this */
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
+ "scb(%d) state(0x%x) cmd(0x%x).\n",
+ intstat, scb_index, scb->state, (unsigned int) scb->cmd);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ aic7xxx_getscb(p, scb);
+ aic7xxx_status(cmd) = scb->target_status;
+
+ cmd->result |= scb->target_status;
+
+ switch (status_byte(scb->target_status))
+ {
+ case GOOD:
+ printk("aic7xxx: Interrupted for status of GOOD???\n");
+ break;
+
+ case CHECK_CONDITION:
+ if ((aic7xxx_error(cmd) == 0) && !(cmd->flags & WAS_SENSE))
+ {
+ unsigned char tcl;
+ unsigned char control;
+ void *req_buf;
+
+ tcl = scb->target_channel_lun;
+
+ /*
+ * Send a sense command to the requesting target.
+ */
+ cmd->flags |= WAS_SENSE;
+ memcpy((void *) scb->sense_cmd, (void *) generic_sense,
+ sizeof(generic_sense));
+
+ scb->sense_cmd[1] = (cmd->lun << 5);
+ scb->sense_cmd[4] = sizeof(cmd->sense_buffer);
+
+ scb->sense_sg.address = (char *) &cmd->sense_buffer;
+ scb->sense_sg.length = sizeof(cmd->sense_buffer);
+ req_buf = &scb->sense_sg;
+ cmd->cmd_len = COMMAND_SIZE(cmd->cmnd[0]);
+ control = scb->control;
+
+ memset(scb, 0, SCB_PIO_TRANSFER_SIZE);
+ scb->control = control & DISCENB;
+ scb->target_channel_lun = tcl;
+ addr = scb->sense_cmd;
+ scb->SCSI_cmd_length = COMMAND_SIZE(scb->sense_cmd[0]);
+ memcpy(scb->SCSI_cmd_pointer, &addr,
+ sizeof(scb->SCSI_cmd_pointer));
+ scb->SG_segment_count = 1;
+ memcpy(scb->SG_list_pointer, &req_buf,
+ sizeof(scb->SG_list_pointer));
+ scb->data_count = scb->sense_sg.length;
+ memcpy(scb->data_pointer, &(scb->sense_sg.address), 4);
+
+ aic7xxx_putscb(p, scb);
+ outb(SCB_LIST_NULL, SCB_NEXT_WAITING + base);
+ /*
+ * Ensure that the target is "BUSY" so we don't get overlapping
+ * commands if we happen to be doing tagged I/O.
+ */
+ aic7xxx_busy_target(scsi_id, channel, base);
+
+ aic7xxx_add_waiting_scb(base, scb, LIST_HEAD);
+ outb(SEND_SENSE, RETURN_1 + base);
+ } /* first time sense, no errors */
+
+ cmd->flags &= ~ASKED_FOR_SENSE;
+ if (aic7xxx_error(cmd) == 0)
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+
+ case BUSY:
+ printk("aic7xxx: Target busy.\n");
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_BUS_BUSY;
+ }
+ break;
+
+ case QUEUE_FULL:
+ printk("aic7xxx: Queue full.\n");
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+
+ default:
+ printk("aic7xxx: Unexpected target status(0x%x).\n",
+ scb->target_status);
+ if (!aic7xxx_error(cmd))
+ {
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ break;
+ } /* end switch */
+ } /* end else of */
+ break;
+
+ case RESIDUAL:
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
+ "scb(%d) state(0x%x) cmd(0x%x).\n",
+ intstat, scb_index, scb->state, (unsigned int) scb->cmd);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ /*
+ * Don't destroy valid residual information with
+ * residual coming from a check sense operation.
+ */
+ if (!(cmd->flags & WAS_SENSE))
+ {
+ /*
+ * We had an underflow. At this time, there's only
+ * one other driver that bothers to check for this,
+ * and cmd->underflow seems to be set rather half-
+ * heartedly in the higher-level SCSI code.
+ */
+ actual = aic7xxx_length(cmd, scb->residual_SG_segment_count);
+
+ actual -= (inb(SCB_RESID_DCNT2 + base) << 16) |
+ (inb(SCB_RESID_DCNT1 + base) << 8) |
+ inb(SCB_RESID_DCNT0 + base);
+
+ if (actual < cmd->underflow)
+ {
+ printk("aic7xxx: Target %d underflow - "
+ "Wanted (at least) (%u) got(%u) count(%d).\n",
+ cmd->target, cmd->underflow, actual,
+ inb(SCB_RESID_SGCNT + base));
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ aic7xxx_status(cmd) = scb->target_status;
+ }
+ }
+ }
+ break;
+
+ case ABORT_TAG:
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
+ "scb(%d) state(0x%x) cmd(0x%x)\n",
+ intstat, scb_index, scb->state, (unsigned int) scb->cmd);
+ }
+ else
+ {
+ cmd = scb->cmd;
+ /*
+ * We didn't receive a valid tag back from the target
+ * on a reconnect.
+ */
+ printk("aic7xxx: Invalid tag received on target %d, channel %c, "
+ "lun %d - Sending ABORT_TAG.\n",
+ scsi_id, channel, cmd->lun & 0x07);
+
+ cmd->result = (DID_RETRY_COMMAND << 16);
+ aic7xxx_done(p, scb);
+ }
+ break;
+
+ case AWAITING_MSG:
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
+ "scb(%d) state(0x%x) cmd(0x%x).\n",
+ intstat, scb_index, scb->state, (unsigned int) scb->cmd);
+ }
+ else
+ {
+ /*
+ * This SCB had a zero length command, informing the sequencer
+ * that we wanted to send a special message to this target.
+ * We only do this for BUS_DEVICE_RESET messages currently.
+ */
+ if (scb->state & SCB_DEVICE_RESET)
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (isr) sending bus device reset to target %d\n",
+ scsi_id);
+#endif
+ outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
+ outb(1, MSG_LEN + base);
+ }
+ else
+ {
+ panic("aic7xxx: AWAITING_SCB for an SCB that does "
+ "not have a waiting message.\n");
+ }
+ }
+ break;
+
+ case IMMEDDONE:
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (isr) received IMMEDDONE for target %d, scb %d, state %d\n",
+ scsi_id, scb_index, scb->state);
+#endif
+ if (scb->state & SCB_DEVICE_RESET)
+ {
+ int found;
+
+ /*
+ * Go back to async/narrow transfers and renogiate.
+ */
+ aic7xxx_unbusy_target(scsi_id, channel, base);
+ p->needsdtr |= (p->needsdtr_copy & target_mask);
+ p->needwdtr |= (p->needwdtr_copy & target_mask);
+ p->sdtr_pending &= ~target_mask;
+ p->wdtr_pending &= ~target_mask;
+ scratch = inb(TARG_SCRATCH + base + scratch_offset);
+ scratch &= SXFR;
+ outb(scratch, TARG_SCRATCH + base + scratch_offset);
+ found = aic7xxx_reset_device(p, (int) scsi_id, channel, SCB_LIST_NULL);
+ }
+ else
+ {
+ panic("aic7xxx: Immediate complete for unknown operation.\n");
+ }
+ break;
+
+#if AIC7XXX_NOT_YET
+ /* XXX Fill these in later */
+ case MESG_BUFFER_BUSY:
+ break;
+ case MSGIN_PHASEMIS:
+ break;
+#endif
+
+ case PARITY_ERROR:
+ {
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Referenced SCB not valid during SEQINT(0x%x) "
+ "scb(%d) state(0x%x) cmd(0x%x).\n",
+ intstat, scb_index, scb->state, (unsigned int) scb->cmd);
+ }
+ else
+ {
+ char *phase;
+ unsigned char mesg_out = MSG_NOP;
+ unsigned char lastphase = inb(LASTPHASE + base);
+
+ cmd = scb->cmd;
+ switch (lastphase)
+ {
+ case P_DATAOUT:
+ phase = "Data-Out";
+ break;
+ case P_DATAIN:
+ phase = "Data-In";
+ mesg_out = MSG_INITIATOR_DET_ERROR;
+ break;
+ case P_COMMAND:
+ phase = "Command";
+ break;
+ case P_MESGOUT:
+ phase = "Message-Out";
+ break;
+ case P_STATUS:
+ phase = "Status";
+ mesg_out = MSG_INITIATOR_DET_ERROR;
+ break;
+ case P_MESGIN:
+ phase = "Message-In";
+ mesg_out = MSG_MSG_PARITY_ERROR;
+ break;
+ default:
+ phase = "unknown";
+ break;
+ }
+
+ /*
+ * A parity error has occurred during a data
+ * transfer phase. Flag it and continue.
+ */
+ printk("aic7xxx: Parity error during phase %s on target %d, "
+ "channel %d, lun %d.\n", phase,
+ cmd->target, cmd->channel & 0x01, cmd->lun & 0x07);
+
+ /*
+ * We've set the hardware to assert ATN if we get a parity
+ * error on "in" phases, so all we need to do is stuff the
+ * message buffer with the appropriate message. In phases
+ * have set mesg_out to something other than MSG_NOP.
+ */
+ if (mesg_out != MSG_NOP)
+ {
+ outb(mesg_out, MSG0 + base);
+ outb(1, MSG_LEN + base);
+ aic7xxx_error(cmd) = DID_PARITY;
+ }
+ else
+ {
+ /*
+ * Should we allow the target to make this decision for us?
+ */
+ aic7xxx_error(cmd) = DID_RETRY_COMMAND;
+ }
+ }
+ break;
+ }
+ default: /* unknown */
+ debug("aic7xxx: SEQINT, INTSTAT(0x%x) SCSISIGI(0x%x).\n",
+ intstat, inb(SCSISIGI + base));
+ break;
+ }
+
+ outb(CLRSEQINT, CLRINT + base);
+ UNPAUSE_SEQUENCER(p);
+ }
+
+ if (intstat & SCSIINT)
+ {
+ int status = inb(SSTAT1 + base);
+ scsi_id = (inb(SCSIID + base) >> 4) & 0x0F;
+ channel = 'A';
+ if (inb(SBLKCTL + base) & SELBUSB)
+ {
+ channel = 'B';
+ }
+
+ scb_index = inb(SCBPTR + base);
+ scb = &(p->scb_array[scb_index]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: No command for SCB (SCSIINT).\n");
+ /*
+ * Turn off the interrupt and set status
+ * to zero, so that it falls through the
+ * reset of the SCSIINT code.
+ */
+ outb(status, CLRSINT1 + base);
+ UNPAUSE_SEQUENCER(p);
+ outb(CLRSCSIINT, CLRINT + base);
+ scb = NULL;
+ }
+ else
+ {
+ cmd = scb->cmd;
+
+ /*
+ * Only the SCSI Status 1 register has information
+ * about exceptional conditions that we'd have a
+ * SCSIINT about; anything in SSTAT0 will be handled
+ * by the sequencer. Note that there can be multiple
+ * bits set.
+ */
+ if (status & SELTO)
+ {
+ unsigned char waiting;
+
+ /*
+ * Hardware selection timer has expired. Turn
+ * off SCSI selection sequence.
+ */
+ outb(ENRSELI, SCSISEQ + base);
+ cmd->result = (DID_TIME_OUT << 16);
+ /*
+ * Clear an pending messages for the timed out
+ * target and mark the target as free.
+ */
+ ha_flags = inb(FLAGS + base);
+ outb(0, MSG_LEN + base);
+ aic7xxx_unbusy_target(scsi_id, channel, base);
+
+ outb(0, SCBARRAY + base);
+
+ /*
+ * Shut off the offending interrupt sources, reset
+ * the sequencer address to zero and unpause it,
+ * then call the high-level SCSI completion routine.
+ *
+ * WARNING! This is a magic sequence! After many
+ * hours of guesswork, turning off the SCSI interrupts
+ * in CLRSINT? does NOT clear the SCSIINT bit in
+ * INTSTAT. By writing to the (undocumented, unused
+ * according to the AIC-7770 manual) third bit of
+ * CLRINT, you can clear INTSTAT. But, if you do it
+ * while the sequencer is paused, you get a BRKADRINT
+ * with an Illegal Host Address status, so the
+ * sequencer has to be restarted first.
+ */
+ outb(CLRSELTIMEO, CLRSINT1 + base);
+
+ outb(CLRSCSIINT, CLRINT + base);
+
+ /*
+ * Shift the waiting for selection queue forward
+ */
+ waiting = inb(WAITING_SCBH + base);
+ outb(waiting, SCBPTR + base);
+ waiting = inb(SCB_NEXT_WAITING + base);
+ outb(waiting, WAITING_SCBH + base);
+
+ RESTART_SEQUENCER(p);
+ aic7xxx_done(p, scb);
+#if 0
+ printk("aic7xxx: SELTO SCB(%d) state(0x%x) cmd(0x%x).\n",
+ scb->position, scb->state, (unsigned int) scb->cmd);
+#endif
+ }
+ else
+ {
+ if (!(status & BUSFREE))
+ {
+ /*
+ * We don't know what's going on. Turn off the
+ * interrupt source and try to continue.
+ */
+ printk("aic7xxx: SSTAT1(0x%x).\n", status);
+ outb(status, CLRSINT1 + base);
+ UNPAUSE_SEQUENCER(p);
+ outb(CLRSCSIINT, CLRINT + base);
+ }
+ }
+ } /* else */
+ }
+
+ if (intstat & CMDCMPLT)
+ {
+ int complete;
+
+ /*
+ * The sequencer will continue running when it
+ * issues this interrupt. There may be >1 commands
+ * finished, so loop until we've processed them all.
+ */
+ do {
+ complete = inb(QOUTFIFO + base);
+
+ scb = &(p->scb_array[complete]);
+ if (!(scb->state & SCB_ACTIVE) || (scb->cmd == NULL))
+ {
+ printk("aic7xxx: Warning - No command for SCB %d (CMDCMPLT).\n"
+ " QOUTCNT(%d) SCB state(0x%x) cmd(0x%x) pos(%d).\n",
+ complete, inb(QOUTFIFO + base),
+ scb->state, (unsigned int) scb->cmd, scb->position);
+ outb(CLRCMDINT, CLRINT + base);
+ continue;
+ }
+ cmd = scb->cmd;
+
+ if ((cmd->flags & WAS_SENSE) && !(cmd->flags & ASKED_FOR_SENSE))
+ {
+ /*
+ * Got sense information.
+ */
+ cmd->flags &= ASKED_FOR_SENSE;
+ }
+#if 0
+ printk("aic7xxx: (complete) State(%d) cmd(0x%x) free(0x%x).\n",
+ scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
+#endif
+
+ /*
+ * Clear interrupt status before checking
+ * the output queue again. This eliminates
+ * a race condition whereby a command could
+ * complete between the queue poll and the
+ * interrupt clearing, so notification of the
+ * command being complete never made it back
+ * up to the kernel.
+ */
+ outb(CLRCMDINT, CLRINT + base);
+ aic7xxx_done(p, scb);
+#if 0
+ if (scb != &p->scb_array[scb->position])
+ {
+ printk("aic7xxx: (complete) Address mismatch, pos(%d).\n", scb->position);
+ }
+ printk("aic7xxx: (complete) State(%d) cmd(0x%x) free(0x%x).\n",
+ scb->state, (unsigned int) scb->cmd, (unsigned int) p->free_scb);
+#endif
+
+#ifdef AIC7XXX_PROC_STATS
+ /*
+ * XXX: we should actually know how much actually transferred
+ * XXX: for each command, but apparently that's too difficult.
+ */
+ actual = aic7xxx_length(cmd, 0);
+ if (!(cmd->flags & WAS_SENSE) && (actual > 0))
+ {
+ struct aic7xxx_xferstats *sp;
+ long *ptr;
+ int x;
+
+ sp = &p->stats[cmd->channel & 0x01][cmd->target & 0x0F][cmd->lun & 0x07];
+ sp->xfers++;
+
+ if (cmd->request.cmd == WRITE)
+ {
+ sp->w_total++;
+ sp->w_total512 += (actual >> 9);
+ ptr = sp->w_bins;
+ }
+ else
+ {
+ sp->r_total++;
+ sp->r_total512 += (actual >> 9);
+ ptr = sp->r_bins;
+ }
+ for (x = 9; x <= 17; x++)
+ {
+ if (actual < (1 << x))
+ {
+ ptr[x - 9]++;
+ break;
+ }
+ }
+ if (x > 17)
+ {
+ ptr[x - 9]++;
+ }
+ }
+#endif /* AIC7XXX_PROC_STATS */
+
+ } while (inb(QOUTCNT + base));
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_probe
+ *
+ * Description:
+ * Probing for EISA boards: it looks like the first two bytes
+ * are a manufacturer code - three characters, five bits each:
+ *
+ * BYTE 0 BYTE 1 BYTE 2 BYTE 3
+ * ?1111122 22233333 PPPPPPPP RRRRRRRR
+ *
+ * The characters are baselined off ASCII '@', so add that value
+ * to each to get the real ASCII code for it. The next two bytes
+ * appear to be a product and revision number, probably vendor-
+ * specific. This is what is being searched for at each port,
+ * and what should probably correspond to the ID= field in the
+ * ECU's .cfg file for the card - if your card is not detected,
+ * make sure your signature is listed in the array.
+ *
+ * The fourth byte's lowest bit seems to be an enabled/disabled
+ * flag (rest of the bits are reserved?).
+ *-F*************************************************************************/
+static aha_type
+aic7xxx_probe(int slot, int base)
+{
+ int i;
+ unsigned char buf[4];
+
+ static struct {
+ int n;
+ unsigned char signature[sizeof(buf)];
+ aha_type type;
+ } AIC7xxx[] = {
+ { 4, { 0x04, 0x90, 0x77, 0x71 }, AIC_7771 }, /* host adapter 274x */
+ { 4, { 0x04, 0x90, 0x77, 0x70 }, AIC_7770 }, /* motherboard 7770 */
+ { 4, { 0x04, 0x90, 0x77, 0x56 }, AIC_284x }, /* 284x, BIOS enabled */
+ { 4, { 0x04, 0x90, 0x77, 0x57 }, AIC_284x } /* 284x, BIOS disabled */
+ };
+
+ /*
+ * The VL-bus cards need to be primed by
+ * writing before a signature check.
+ */
+ for (i = 0; i < sizeof(buf); i++)
+ {
+ outb(0x80 + i, base);
+ buf[i] = inb(base + i);
+ }
+
+ for (i = 0; i < NUMBER(AIC7xxx); i++)
+ {
+ /*
+ * Signature match on enabled card?
+ */
+ if (!memcmp(buf, AIC7xxx[i].signature, AIC7xxx[i].n))
+ {
+ if (inb(base + 4) & 1)
+ {
+ return (AIC7xxx[i].type);
+ }
+
+ printk("aic7xxx: Disabled at slot %d, ignored.\n", slot);
+ }
+ }
+
+ return (AIC_NONE);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * read_2840_seeprom
+ *
+ * Description:
+ * Reads the 2840 serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ *
+ * See read_seeprom (for the 2940) for the instruction set of the 93C46
+ * chip.
+ *
+ * The 2840 interface to the 93C46 serial EEPROM is through the
+ * STATUS_2840 and SEECTL_2840 registers. The CS_2840, CK_2840, and
+ * DO_2840 bits of the SEECTL_2840 register are connected to the chip
+ * select, clock, and data out lines respectively of the serial EEPROM.
+ * The DI_2840 bit of the STATUS_2840 is connected to the data in line
+ * of the serial EEPROM. The EEPROM_TF bit of STATUS_2840 register is
+ * useful in that it gives us an 800 nsec timer. After a read from the
+ * SEECTL_2840 register the timing flag is cleard and goes high 800 nsec
+ * later.
+ *
+ *-F*************************************************************************/
+static int
+read_2840_seeprom(int base, struct seeprom_config *sc)
+{
+ int i = 0, k = 0;
+ unsigned char temp;
+ unsigned short checksum = 0;
+ unsigned short *seeprom = (unsigned short *) sc;
+ struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+ };
+ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+ while ((inb(STATUS_2840 + base) & EEPROM_TF) == 0) \
+ { \
+ ; /* Do nothing */ \
+ } \
+ (void) inb(SEECTL_2840 + base);
+
+ /*
+ * Read the first 32 registers of the seeprom. For the 2840,
+ * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
+ * but only the first 32 are used by Adaptec BIOS. The loop
+ * will range from 0 to 31.
+ */
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ /*
+ * Send chip select for one clock cycle.
+ */
+ outb(CK_2840 | CS_2840, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++)
+ {
+ temp = CS_2840 | seeprom_read.bits[i];
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ temp = temp ^ CK_2840;
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ }
+ /*
+ * Send the 6 bit address (MSB first, LSB last).
+ */
+ for (i = 5; i >= 0; i--)
+ {
+ temp = k;
+ temp = (temp >> i) & 1; /* Mask out all but lower bit. */
+ temp = CS_2840 | temp;
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ temp = temp ^ CK_2840;
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ for (i = 0; i <= 16; i++)
+ {
+ temp = CS_2840;
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ temp = temp ^ CK_2840;
+ seeprom[k] = (seeprom[k] << 1) | (inb(STATUS_2840 + base) & DI_2840);
+ outb(temp, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ }
+ /*
+ * The serial EEPROM has a checksum in the last word. Keep a
+ * running checksum for all words read except for the last
+ * word. We'll verify the checksum after all words have been
+ * read.
+ */
+ if (k < (sizeof(*sc) / 2) - 1)
+ {
+ checksum = checksum + seeprom[k];
+ }
+
+ /*
+ * Reset the chip select for the next command cycle.
+ */
+ outb(0, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ outb(CK_2840, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ outb(0, SEECTL_2840 + base);
+ CLOCK_PULSE(base);
+ }
+
+#if 0
+ printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+ printk("Serial EEPROM:");
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ if (((k % 8) == 0) && (k != 0))
+ {
+ printk("\n ");
+ }
+ printk(" 0x%x", seeprom[k]);
+ }
+ printk("\n");
+#endif
+
+ if (checksum != sc->checksum)
+ {
+ printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+ return (0);
+ }
+
+ return (1);
+#undef CLOCK_PULSE
+}
+
+/*+F*************************************************************************
+ * Function:
+ * read_seeprom
+ *
+ * Description:
+ * Reads the serial EEPROM and returns 1 if successful and 0 if
+ * not successful.
+ *
+ * The instruction set of the 93C46 chip is as follows:
+ *
+ * Start OP
+ * Function Bit Code Address Data Description
+ * -------------------------------------------------------------------
+ * READ 1 10 A5 - A0 Reads data stored in memory,
+ * starting at specified address
+ * EWEN 1 00 11XXXX Write enable must preceed
+ * all programming modes
+ * ERASE 1 11 A5 - A0 Erase register A5A4A3A2A1A0
+ * WRITE 1 01 A5 - A0 D15 - D0 Writes register
+ * ERAL 1 00 10XXXX Erase all registers
+ * WRAL 1 00 01XXXX D15 - D0 Writes to all registers
+ * EWDS 1 00 00XXXX Disables all programming
+ * instructions
+ * *Note: A value of X for address is a don't care condition.
+ *
+ * The 93C46 has a four wire interface: clock, chip select, data in, and
+ * data out. In order to perform one of the above functions, you need
+ * to enable the chip select for a clock period (typically a minimum of
+ * 1 usec, with the clock high and low a minimum of 750 and 250 nsec
+ * respectively. While the chip select remains high, you can clock in
+ * the instructions (above) starting with the start bit, followed by the
+ * OP code, Address, and Data (if needed). For the READ instruction, the
+ * requested 16-bit register contents is read from the data out line but
+ * is preceded by an initial zero (leading 0, followed by 16-bits, MSB
+ * first). The clock cycling from low to high initiates the next data
+ * bit to be sent from the chip.
+ *
+ * The 7870 interface to the 93C46 serial EEPROM is through the SEECTL
+ * register. After successful arbitration for the memory port, the
+ * SEECS bit of the SEECTL register is connected to the chip select.
+ * The SEECK, SEEDO, and SEEDI are connected to the clock, data out,
+ * and data in lines respectively. The SEERDY bit of SEECTL is useful
+ * in that it gives us an 800 nsec timer. After a write to the SEECTL
+ * register, the SEERDY goes high 800 nsec later. The one exception
+ * to this is when we first request access to the memory port. The
+ * SEERDY goes high to signify that access has been granted and, for
+ * this case, has no implied timing.
+ *
+ *-F*************************************************************************/
+static int
+read_seeprom(int base, int offset, struct seeprom_config *sc)
+{
+ int i = 0, k;
+ unsigned long timeout;
+ unsigned char temp;
+ unsigned short checksum = 0;
+ unsigned short *seeprom = (unsigned short *) sc;
+ struct seeprom_cmd {
+ unsigned char len;
+ unsigned char bits[3];
+ };
+ struct seeprom_cmd seeprom_read = {3, {1, 1, 0}};
+
+#define CLOCK_PULSE(p) \
+ while ((inb(SEECTL + base) & SEERDY) == 0) \
+ { \
+ ; /* Do nothing */ \
+ }
+
+ /*
+ * Request access of the memory port. When access is
+ * granted, SEERDY will go high. We use a 1 second
+ * timeout which should be near 1 second more than
+ * is needed. Reason: after the 7870 chip reset, there
+ * should be no contention.
+ */
+ outb(SEEMS, SEECTL + base);
+ timeout = jiffies + 100; /* 1 second timeout */
+ while ((jiffies < timeout) && ((inb(SEECTL + base) & SEERDY) == 0))
+ {
+ ; /* Do nothing! Wait for access to be granted. */
+ }
+ if ((inb(SEECTL + base) & SEERDY) == 0)
+ {
+ outb(0, SEECTL + base);
+ return (0);
+ }
+
+ /*
+ * Read the first 32 registers of the seeprom. For the 7870,
+ * the 93C46 SEEPROM is a 1024-bit device with 64 16-bit registers
+ * but only the first 32 are used by Adaptec BIOS. The loop
+ * will range from 0 to 31.
+ */
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ /*
+ * Send chip select for one clock cycle.
+ */
+ outb(SEEMS | SEECK | SEECS, SEECTL + base);
+ CLOCK_PULSE(base);
+
+ /*
+ * Now we're ready to send the read command followed by the
+ * address of the 16-bit register we want to read.
+ */
+ for (i = 0; i < seeprom_read.len; i++)
+ {
+ temp = SEEMS | SEECS | (seeprom_read.bits[i] << 1);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
+ temp = temp ^ SEECK;
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
+ }
+ /*
+ * Send the 6 bit address (MSB first, LSB last).
+ */
+ for (i = 5; i >= 0; i--)
+ {
+ temp = k + offset;
+ temp = (temp >> i) & 1; /* Mask out all but lower bit. */
+ temp = SEEMS | SEECS | (temp << 1);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
+ temp = temp ^ SEECK;
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
+ }
+
+ /*
+ * Now read the 16 bit register. An initial 0 precedes the
+ * register contents which begins with bit 15 (MSB) and ends
+ * with bit 0 (LSB). The initial 0 will be shifted off the
+ * top of our word as we let the loop run from 0 to 16.
+ */
+ for (i = 0; i <= 16; i++)
+ {
+ temp = SEEMS | SEECS;
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
+ temp = temp ^ SEECK;
+ seeprom[k] = (seeprom[k] << 1) | (inb(SEECTL + base) & SEEDI);
+ outb(temp, SEECTL + base);
+ CLOCK_PULSE(base);
+ }
+
+ /*
+ * The serial EEPROM has a checksum in the last word. Keep a
+ * running checksum for all words read except for the last
+ * word. We'll verify the checksum after all words have been
+ * read.
+ */
+ if (k < (sizeof(*sc) / 2) - 1)
+ {
+ checksum = checksum + seeprom[k];
+ }
+
+ /*
+ * Reset the chip select for the next command cycle.
+ */
+ outb(SEEMS, SEECTL + base);
+ CLOCK_PULSE(base);
+ outb(SEEMS | SEECK, SEECTL + base);
+ CLOCK_PULSE(base);
+ outb(SEEMS, SEECTL + base);
+ CLOCK_PULSE(base);
+ }
+
+ /*
+ * Release access to the memory port and the serial EEPROM.
+ */
+ outb(0, SEECTL + base);
+
+#if 0
+ printk("Computed checksum 0x%x, checksum read 0x%x\n", checksum, sc->checksum);
+ printk("Serial EEPROM:");
+ for (k = 0; k < (sizeof(*sc) / 2); k++)
+ {
+ if (((k % 8) == 0) && (k != 0))
+ {
+ printk("\n ");
+ }
+ printk(" 0x%x", seeprom[k]);
+ }
+ printk("\n");
+#endif
+
+ if (checksum != sc->checksum)
+ {
+ printk("aic7xxx: SEEPROM checksum error, ignoring SEEPROM settings.\n");
+ return (0);
+ }
+
+ return (1);
+#undef CLOCK_PULSE
+}
+
+/*+F*************************************************************************
+ * Function:
+ * detect_maxscb
+ *
+ * Description:
+ * Return the maximum number of SCB's allowed for a given controller.
+ *-F*************************************************************************/
+static int
+detect_maxscb(aha_type type, int base, int walk_scbs)
+{
+ unsigned char sblkctl_reg, scb_byte;
+ int maxscb = 0, i;
+
+ switch (type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ case AIC_284x:
+ /*
+ * Check for Rev C or E boards. Rev E boards can supposedly have
+ * more than 4 SCBs, while the Rev C boards are limited to 4 SCBs.
+ * Until we know how to access more than 4 SCBs for the Rev E chips,
+ * we limit them, along with the Rev C chips, to 4 SCBs.
+ *
+ * The Rev E boards have a read/write autoflush bit in the
+ * SBLKCTL register, while in the Rev C boards it is read only.
+ */
+ sblkctl_reg = inb(SBLKCTL + base) ^ AUTOFLUSHDIS;
+ outb(sblkctl_reg, SBLKCTL + base);
+ if (inb(SBLKCTL + base) == sblkctl_reg)
+ {
+ /*
+ * We detected a Rev E board.
+ */
+ printk("aic7xxx: %s Rev E and subsequent.\n", board_names[type]);
+ outb(sblkctl_reg ^ AUTOFLUSHDIS, SBLKCTL + base);
+ maxscb = 4;
+ }
+ else
+ {
+ printk("aic7xxx: %s Rev C and previous.\n", board_names[type]);
+ maxscb = 4;
+ }
+ break;
+
+ case AIC_7850:
+ maxscb = 3;
+ break;
+
+ case AIC_7870:
+ case AIC_7871:
+ case AIC_7874:
+ case AIC_7880:
+ case AIC_7881:
+ case AIC_7884:
+ maxscb = 16;
+ break;
+
+ case AIC_7872:
+ case AIC_7873:
+ case AIC_7882:
+ case AIC_7883:
+ /*
+ * Is suppose to have 255 SCBs, but we'll walk the SCBs
+ * looking for more if external RAM is detected.
+ */
+ maxscb = 16;
+ break;
+
+ case AIC_NONE:
+ /*
+ * This should never happen... But just in case.
+ */
+ break;
+ }
+
+ if (walk_scbs)
+ {
+ /*
+ * This adapter has external SCB memory.
+ * Walk the SCBs to determine how many there are.
+ */
+ i = 0;
+ while (i < AIC7XXX_MAXSCB)
+ {
+ outb(i, SCBPTR + base);
+ scb_byte = ~(inb(SCBARRAY + base)); /* complement the byte */
+ outb(scb_byte, SCBARRAY + base); /* write it back out */
+ if (inb(SCBARRAY + base) != scb_byte)
+ {
+ break;
+ }
+ i++;
+ }
+ maxscb = i;
+
+ printk("aic7xxx: Using %d SCB's after checking for SCB memory.\n", maxscb);
+ }
+ else
+ {
+ printk("aic7xxx: Using %d SCB's; No SCB memory check.\n", maxscb);
+ }
+
+ return (maxscb);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_register
+ *
+ * Description:
+ * Register a Adaptec aic7xxx chip SCSI controller with the kernel.
+ *-F*************************************************************************/
+static int
+aic7xxx_register(Scsi_Host_Template *template,
+ struct aic7xxx_host_config *config)
+{
+ int i;
+ unsigned char sblkctl;
+ int max_targets;
+ int found = 1, base;
+ int bios_disabled = FALSE;
+ unsigned char target_settings;
+ unsigned char scsi_conf, host_conf;
+ int have_seeprom = FALSE;
+ struct Scsi_Host *host;
+ struct aic7xxx_host *p;
+ struct seeprom_config sc;
+
+ base = config->base;
+
+ /*
+ * Lock out other contenders for our i/o space.
+ */
+ request_region(MINREG + base, MAXREG - MINREG, "aic7xxx");
+
+ switch (config->type)
+ {
+ case AIC_7770:
+ case AIC_7771:
+ /*
+ * For some 274x boards, we must clear the CHIPRST bit
+ * and pause the sequencer. For some reason, this makes
+ * the driver work. For 284x boards, we give it a
+ * CHIPRST just like the 294x boards.
+ *
+ * Use the BIOS settings to determine the interrupt
+ * trigger type (level or edge) and use this value
+ * for pausing and unpausing the sequencer.
+ */
+ config->unpause = (inb(HCNTRL + base) & IRQMS) | INTEN;
+ config->pause = config->unpause | PAUSE;
+ config->extended = aic7xxx_extended;
+
+ outb(config->pause | CHIPRST, HCNTRL + base);
+ aic7xxx_delay(1);
+ if (inb(HCNTRL + base) & CHIPRST)
+ {
+ printk("aic7xxx: Chip reset not cleared; clearing manually.\n");
+ }
+ outb(config->pause, HCNTRL + base);
+
+ /*
+ * Just to be on the safe side with the 274x, we will re-read the irq
+ * since there was some issue about resetting the board.
+ */
+ config->irq = inb(INTDEF + base) & 0x0F;
+ if ((inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
+ {
+ bios_disabled = TRUE;
+ }
+ host_conf = inb(HOSTCONF + base);
+ config->busrtime = host_conf & 0x3C;
+ /* XXX Is this valid for motherboard based controllers? */
+ /* Setup the FIFO threshold and the bus off time */
+ outb(host_conf & DFTHRSH, BUSSPD + base);
+ outb((host_conf << 2) & BOFF, BUSTIME + base);
+
+ /*
+ * A reminder until this can be detected automatically.
+ */
+ printk("aic7xxx: Extended translation %sabled.\n",
+ config->extended ? "en" : "dis");
+ break;
+
+ case AIC_284x:
+ outb(CHIPRST, HCNTRL + base);
+ config->unpause = UNPAUSE_284X;
+ config->pause = REQ_PAUSE; /* DWG would like to be like the rest */
+ aic7xxx_delay(1);
+ outb(config->pause, HCNTRL + base);
+
+ config->extended = aic7xxx_extended;
+ config->irq = inb(INTDEF + base) & 0x0F;
+ if ((inb(HA_274_BIOSCTRL + base) & BIOSMODE) == BIOSDISABLED)
+ {
+ bios_disabled = TRUE;
+ }
+ host_conf = inb(HOSTCONF + base);
+
+ printk("aic7xxx: Reading SEEPROM...");
+ have_seeprom = read_2840_seeprom(base, &sc);
+ if (!have_seeprom)
+ {
+ printk("aic7xxx: Unable to read SEEPROM.\n");
+ config->busrtime = host_conf & 0x3C;
+ }
+ else
+ {
+ printk("done.\n");
+ config->extended = ((sc.bios_control & CF284XEXTEND) >> 5);
+ config->scsi_id = (sc.brtime_id & CFSCSIID);
+ config->parity = (sc.adapter_control & CFSPARITY) ?
+ AIC_ENABLED : AIC_DISABLED;
+ config->low_term = (sc.adapter_control & CF284XSTERM) ?
+ AIC_ENABLED : AIC_DISABLED;
+ /*
+ * XXX - Adaptec *does* make 284x wide controllers, but the
+ * documents do not say where the high byte termination
+ * enable bit is located. For now, we'll just assume
+ * that it's in the same place as for the 2940 card.
+ */
+ config->high_term = (sc.adapter_control & CFWSTERM) ?
+ AIC_ENABLED : AIC_DISABLED;
+ config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
+ }
+ /* XXX Is this valid for motherboard based controllers? */
+ /* Setup the FIFO threshold and the bus off time */
+ outb(host_conf & DFTHRSH, BUSSPD + base);
+ outb((host_conf << 2) & BOFF, BUSTIME + base);
+
+ printk("aic7xxx: Extended translation %sabled.\n",
+ config->extended ? "en" : "dis");
+ break;
+
+ case AIC_7850:
+ case AIC_7870:
+ case AIC_7871:
+ case AIC_7872:
+ case AIC_7873:
+ case AIC_7874:
+ case AIC_7880:
+ case AIC_7881:
+ case AIC_7882:
+ case AIC_7883:
+ case AIC_7884:
+ outb(CHIPRST, HCNTRL + base);
+ config->unpause = UNPAUSE_294X;
+ config->pause = config->unpause | PAUSE;
+ aic7xxx_delay(1);
+ outb(config->pause, HCNTRL + base);
+
+ config->extended = aic7xxx_extended;
+ config->scsi_id = 7;
+
+ printk("aic7xxx: Reading SEEPROM...");
+ have_seeprom = read_seeprom(base, config->chan_num * (sizeof(sc) / 2), &sc);
+ if (!have_seeprom)
+ {
+ printk("aic7xxx: Unable to read SEEPROM.\n");
+ }
+ else
+ {
+ printk("done.\n");
+ config->extended = ((sc.bios_control & CFEXTEND) >> 7);
+ config->scsi_id = (sc.brtime_id & CFSCSIID);
+ config->parity = (sc.adapter_control & CFSPARITY) ?
+ AIC_ENABLED : AIC_DISABLED;
+ config->low_term = (sc.adapter_control & CFSTERM) ?
+ AIC_ENABLED : AIC_DISABLED;
+ config->high_term = (sc.adapter_control & CFWSTERM) ?
+ AIC_ENABLED : AIC_DISABLED;
+ config->busrtime = ((sc.brtime_id & CFBRTIME) >> 8);
+ if (((config->type == AIC_7880) || (config->type == AIC_7882) ||
+ (config->type == AIC_7883) || (config->type == AIC_7884)) &&
+ (sc.adapter_control & CFULTRAEN))
+ {
+ printk ("aic7xxx: Enabling support for Ultra SCSI speed.\n");
+ config->ultra_enabled = TRUE;
+ }
+ }
+
+ /*
+ * XXX - force data fifo threshold to 100%. Why does this
+ * need to be done?
+ *
+ * We don't know where this is set in the SEEPROM or by the BIOS,
+ * so we default it to 100%.
+ */
+ outb(config->scsi_id | DFTHRSH_100, SCSICONF + base);
+ outb(DFTHRSH_100, DSPCISTATUS + base);
+
+ /*
+ * In case we are a wide card, place scsi ID in second conf byte.
+ */
+ outb(config->scsi_id, (SCSICONF + base + 1));
+
+ printk("aic7xxx: Extended translation %sabled.\n",
+ config->extended ? "en" : "dis");
+ break;
+
+ default:
+ panic("aic7xxx: (aic7xxx_register) Internal error.\n");
+ }
+
+ config->maxscb = detect_maxscb(config->type, base, config->walk_scbs);
+
+ if (config->chip_type == AIC_777x)
+ {
+ if (config->pause & IRQMS)
+ {
+ printk("aic7xxx: Using level sensitive interrupts.\n");
+ }
+ else
+ {
+ printk("aic7xxx: Using edge triggered interrupts.\n");
+ }
+ }
+
+ /*
+ * Read the bus type from the SBLKCTL register. Set the FLAGS
+ * register in the sequencer for twin and wide bus cards.
+ */
+ sblkctl = inb(SBLKCTL + base);
+ switch (sblkctl & SELBUS_MASK)
+ {
+ case SELNARROW: /* narrow/normal bus */
+ config->scsi_id = inb(SCSICONF + base) & 0x07;
+ config->bus_type = AIC_SINGLE;
+ outb(SINGLE_BUS, FLAGS + base);
+ break;
+
+ case SELWIDE: /* Wide bus */
+ config->scsi_id = inb(SCSICONF + base + 1) & 0x0F;
+ config->bus_type = AIC_WIDE;
+ printk("aic7xxx: Enabling wide channel of %s-Wide.\n",
+ board_names[config->type]);
+ outb(WIDE_BUS, FLAGS + base);
+ break;
+
+ case SELBUSB: /* Twin bus */
+ config->scsi_id = inb(SCSICONF + base) & 0x07;
+#ifdef AIC7XXX_TWIN_SUPPORT
+ config->scsi_id_b = inb(SCSICONF + base + 1) & 0x07;
+ config->bus_type = AIC_TWIN;
+ printk("aic7xxx: Enabled channel B of %s-Twin.\n",
+ board_names[config->type]);
+ outb(TWIN_BUS, FLAGS + base);
+#else
+ config->bus_type = AIC_SINGLE;
+ printk("aic7xxx: Channel B of %s-Twin will be ignored.\n",
+ board_names[config->type]);
+ outb(0, FLAGS + base);
+#endif
+ break;
+
+ default:
+ printk("aic7xxx: Unsupported type 0x%x, please "
+ "mail deang@ims.com\n", inb(SBLKCTL + base));
+ outb(0, FLAGS + base);
+ return (0);
+ }
+
+ /*
+ * For the 294x cards, clearing DIAGLEDEN and DIAGLEDON, will
+ * take the card out of diagnostic mode and make the host adatper
+ * LED follow bus activity (will not always be on).
+ */
+ outb(sblkctl & ~(DIAGLEDEN | DIAGLEDON), SBLKCTL + base);
+
+ /*
+ * The IRQ level in i/o port 4 maps directly onto the real
+ * IRQ number. If it's ok, register it with the kernel.
+ *
+ * NB. the Adaptec documentation says the IRQ number is only
+ * in the lower four bits; the ECU information shows the
+ * high bit being used as well. Which is correct?
+ *
+ * The PCI cards get their interrupt from PCI BIOS.
+ */
+ if ((config->chip_type == AIC_777x) && ((config->irq < 9) || (config->irq > 15)))
+ {
+ printk("aic7xxx: Host adapter uses unsupported IRQ level, ignoring.\n");
+ return (0);
+ }
+
+ /*
+ * Check the IRQ to see if it is shared by another aic7xxx
+ * controller. If it is and sharing of IRQs is not defined,
+ * then return 0 hosts found. If sharing of IRQs is allowed
+ * or the IRQ is not shared by another host adapter, then
+ * proceed.
+ */
+#ifndef AIC7XXX_SHARE_IRQS
+ if (aic7xxx_boards[config->irq] != NULL)
+ {
+ printk("aic7xxx: Sharing of IRQ's is not configured.\n");
+ return (0);
+ }
+#endif
+
+ /*
+ * Print out debugging information before re-enabling
+ * the card - a lot of registers on it can't be read
+ * when the sequencer is active.
+ */
+ debug_config(config);
+
+ /*
+ * Before registry, make sure that the offsets of the
+ * struct scatterlist are what the sequencer will expect,
+ * otherwise disable scatter-gather altogether until someone
+ * can fix it. This is important since the sequencer will
+ * DMA elements of the SG array in while executing commands.
+ */
+ if (template->sg_tablesize != SG_NONE)
+ {
+ struct scatterlist sg;
+
+ if (SG_STRUCT_CHECK(sg))
+ {
+ printk("aic7xxx: Warning - Kernel scatter-gather structures changed, "
+ "disabling it.\n");
+ template->sg_tablesize = SG_NONE;
+ }
+ }
+
+ /*
+ * Register each "host" and fill in the returned Scsi_Host
+ * structure as best we can. Some of the parameters aren't
+ * really relevant for bus types beyond ISA, and none of the
+ * high-level SCSI code looks at it anyway. Why are the fields
+ * there? Also save the pointer so that we can find the
+ * information when an IRQ is triggered.
+ */
+ host = scsi_register(template, sizeof(struct aic7xxx_host));
+ host->can_queue = config->maxscb;
+ host->cmd_per_lun = AIC7XXX_CMDS_PER_LUN;
+ host->this_id = config->scsi_id;
+ host->irq = config->irq;
+ if (config->bus_type == AIC_WIDE)
+ {
+ host->max_id = 16;
+ }
+ if (config->bus_type == AIC_TWIN)
+ {
+ host->max_channel = 1;
+ }
+
+ p = (struct aic7xxx_host *) host->hostdata;
+
+ /*
+ * Initialize the scb array by setting the state to free.
+ */
+ for (i = 0; i < AIC7XXX_MAXSCB; i++)
+ {
+ p->scb_array[i].state = SCB_FREE;
+ p->scb_array[i].next = NULL;
+ p->scb_array[i].cmd = NULL;
+ }
+
+ p->isr_count = 0;
+ p->a_scanned = FALSE;
+ p->b_scanned = FALSE;
+ p->base = base;
+ p->maxscb = config->maxscb;
+ p->numscb = 0;
+ p->extended = config->extended;
+ p->type = config->type;
+ p->chip_type = config->chip_type;
+ p->ultra_enabled = config->ultra_enabled;
+ p->chan_num = config->chan_num;
+ p->bus_type = config->bus_type;
+ p->have_seeprom = have_seeprom;
+ p->seeprom = sc;
+ p->free_scb = NULL;
+ p->next = NULL;
+
+ p->unpause = config->unpause;
+ p->pause = config->pause;
+
+ if (aic7xxx_boards[config->irq] == NULL)
+ {
+ /*
+ * Warning! This must be done before requesting the irq. It is
+ * possible for some boards to raise an interrupt as soon as
+ * they are enabled. So when we request the irq from the Linux
+ * kernel, an interrupt is triggered immediately. Therefore, we
+ * must ensure the board data is correctly set before the request.
+ */
+ aic7xxx_boards[config->irq] = host;
+
+ /*
+ * Register IRQ with the kernel.
+ */
+ if (request_irq(config->irq, aic7xxx_isr, SA_INTERRUPT, "aic7xxx"))
+ {
+ printk("aic7xxx: Couldn't register IRQ %d, ignoring.\n", config->irq);
+ aic7xxx_boards[config->irq] = NULL;
+ return (0);
+ }
+ }
+ else
+ {
+ /*
+ * We have found a host adapter sharing an IRQ of a previously
+ * registered host adapter. Add this host adapter's Scsi_Host
+ * to the beginning of the linked list of hosts at the same IRQ.
+ */
+ p->next = aic7xxx_boards[config->irq];
+ aic7xxx_boards[config->irq] = host;
+ }
+
+ /*
+ * Load the sequencer program, then re-enable the board -
+ * resetting the AIC-7770 disables it, leaving the lights
+ * on with nobody home. On the PCI bus you *may* be home,
+ * but then your mailing address is dynamically assigned
+ * so no one can find you anyway :-)
+ */
+ printk("aic7xxx: Downloading sequencer code...");
+ aic7xxx_loadseq(base);
+
+ /*
+ * Set Fast Mode and Enable the board
+ */
+ outb(FASTMODE, SEQCTL + base);
+
+ if (p->chip_type == AIC_777x)
+ {
+ outb(ENABLE, BCTL + base);
+ }
+
+ printk("done.\n");
+
+ /*
+ * Set the SCSI Id, SXFRCTL0, SXFRCTL1, and SIMODE1, for both channels
+ */
+ if (p->bus_type == AIC_TWIN)
+ {
+ /*
+ * Select Channel B.
+ */
+ outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+
+ outb(config->scsi_id_b, SCSIID + base);
+ scsi_conf = inb(SCSICONF + base + 1) & (ENSPCHK | STIMESEL);
+ outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
+ outb(ENSELTIMO , SIMODE1 + base);
+ if (p->ultra_enabled)
+ {
+ outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ }
+ else
+ {
+ outb(DFON | SPIOEN, SXFRCTL0 + base);
+ }
+
+ /*
+ * Select Channel A
+ */
+ outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+ }
+ outb(config->scsi_id, SCSIID + base);
+ scsi_conf = inb(SCSICONF + base) & (ENSPCHK | STIMESEL);
+ outb(scsi_conf | ENSTIMER | ACTNEGEN | STPWEN, SXFRCTL1 + base);
+ outb(ENSELTIMO , SIMODE1 + base);
+ if (p->ultra_enabled)
+ {
+ outb(DFON | SPIOEN | ULTRAEN, SXFRCTL0 + base);
+ }
+ else
+ {
+ outb(DFON | SPIOEN, SXFRCTL0 + base);
+ }
+
+ /*
+ * Look at the information that board initialization or the board
+ * BIOS has left us. In the lower four bits of each target's
+ * scratch space any value other than 0 indicates that we should
+ * initiate synchronous transfers. If it's zero, the user or the
+ * BIOS has decided to disable synchronous negotiation to that
+ * target so we don't activate the needsdtr flag.
+ */
+ p->needsdtr_copy = 0x0;
+ p->sdtr_pending = 0x0;
+ p->needwdtr_copy = 0x0;
+ p->wdtr_pending = 0x0;
+ if (p->bus_type == AIC_SINGLE)
+ {
+ max_targets = 8;
+ }
+ else
+ {
+ max_targets = 16;
+ }
+
+ /*
+ * Grab the disconnection disable table and invert it for our needs
+ */
+ if (have_seeprom)
+ {
+ p->discenable = 0x0;
+ }
+ else
+ {
+ if (bios_disabled)
+ {
+ printk("aic7xxx : Host adapter BIOS disabled. Using default SCSI "
+ "device parameters.\n");
+ p->discenable = 0xFFFF;
+ }
+ else
+ {
+ p->discenable = ~((inb(DISC_DSB + base + 1) << 8) |
+ inb(DISC_DSB + base));
+ }
+ }
+
+ for (i = 0; i < max_targets; i++)
+ {
+ if (have_seeprom)
+ {
+ target_settings = ((sc.device_flags[i] & CFXFER) << 4);
+ if (sc.device_flags[i] & CFSYNCH)
+ {
+ p->needsdtr_copy |= (0x01 << i);
+ }
+ if (sc.device_flags[i] & CFWIDEB)
+ {
+ p->needwdtr_copy |= (0x01 << i);
+ }
+ if (sc.device_flags[i] & CFDISC)
+ {
+ p->discenable |= (0x01 << i);
+ }
+ }
+ else
+ {
+ if (bios_disabled)
+ {
+ target_settings = 0; /* 10 MHz */
+ p->needsdtr_copy |= (0x01 << i);
+ p->needwdtr_copy |= (0x01 << i);
+ }
+ else
+ {
+ target_settings = inb(TARG_SCRATCH + base + i);
+ if (target_settings & 0x0F)
+ {
+ p->needsdtr_copy |= (0x01 << i);
+ /*
+ * Default to asynchronous transfers (0 offset)
+ */
+ target_settings &= 0xF0;
+ }
+ if (target_settings & 0x80)
+ {
+ p->needwdtr_copy |= (0x01 << i);
+ target_settings &= 0x7F;
+ }
+ }
+ }
+ outb(target_settings, (TARG_SCRATCH + base + i));
+ }
+
+ /*
+ * If we are not wide, forget WDTR. This makes the driver
+ * work on some cards that don't leave these fields cleared
+ * when BIOS is not installed.
+ */
+ if (p->bus_type != AIC_WIDE)
+ {
+ p->needwdtr = 0;
+ }
+ p->needsdtr = p->needsdtr_copy;
+ p->needwdtr = p->needwdtr_copy;
+#if 0
+ printk("NeedSdtr = 0x%x, 0x%x\n", p->needsdtr_copy, p->needsdtr);
+ printk("NeedWdtr = 0x%x, 0x%x\n", p->needwdtr_copy, p->needwdtr);
+#endif
+
+ /*
+ * Clear the control byte for every SCB so that the sequencer
+ * doesn't get confused and think that one of them is valid
+ */
+ for (i = 0; i < config->maxscb; i++)
+ {
+ outb(i, SCBPTR + base);
+ outb(0, SCBARRAY + base);
+ }
+
+ /*
+ * For reconnecting targets, the sequencer code needs to
+ * know how many SCBs it has to search through.
+ */
+ outb(config->maxscb, SCBCOUNT + base);
+
+ /*
+ * 2s compliment of SCBCOUNT
+ */
+ i = p->maxscb;
+ outb(-i & 0xff, COMP_SCBCOUNT + base);
+
+ /*
+ * Clear the active flags - no targets are busy.
+ */
+ outb(0, ACTIVE_A + base);
+ outb(0, ACTIVE_B + base);
+
+ /*
+ * We don't have any waiting selections
+ */
+ outb(SCB_LIST_NULL, WAITING_SCBH + base);
+ outb(SCB_LIST_NULL, WAITING_SCBT + base);
+
+ /*
+ * Reset the SCSI bus. Is this necessary?
+ * There may be problems for a warm boot without resetting
+ * the SCSI bus. Either BIOS settings in scratch RAM
+ * will not get reinitialized, or devices may stay at
+ * previous negotiated settings (SDTR and WDTR) while
+ * the driver will think that no negotiations have been
+ * performed.
+ *
+ * Some devices need a long time to "settle" after a SCSI
+ * bus reset.
+ */
+
+ if (!aic7xxx_no_reset)
+ {
+ printk("aic7xxx: Resetting the SCSI bus...");
+ if (p->bus_type == AIC_TWIN)
+ {
+ /*
+ * Select Channel B.
+ */
+ outb((sblkctl & ~SELBUS_MASK) | SELBUSB, SBLKCTL + base);
+
+ outb(SCSIRSTO, SCSISEQ + base);
+ udelay(1000);
+ outb(0, SCSISEQ + base);
+
+ /*
+ * Select Channel A.
+ */
+ outb((sblkctl & ~SELBUS_MASK) | SELNARROW, SBLKCTL + base);
+ }
+
+ outb(SCSIRSTO, SCSISEQ + base);
+ udelay(1000);
+ outb(0, SCSISEQ + base);
+
+ aic7xxx_delay(AIC7XXX_RESET_DELAY);
+
+ printk("done.\n");
+ }
+
+ /*
+ * Unpause the sequencer before returning and enable
+ * interrupts - we shouldn't get any until the first
+ * command is sent to us by the high-level SCSI code.
+ */
+ UNPAUSE_SEQUENCER(p);
+ return (found);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_detect
+ *
+ * Description:
+ * Try to detect and register an Adaptec 7770 or 7870 SCSI controller.
+ *-F*************************************************************************/
+int
+aic7xxx_detect(Scsi_Host_Template *template)
+{
+ int found = 0, slot, base;
+ unsigned char irq = 0;
+ int i;
+ struct aic7xxx_host_config config;
+
+ template->proc_dir = &proc_scsi_aic7xxx;
+ config.chan_num = 0;
+
+ /*
+ * Since we may allow sharing of IRQs, it is imperative
+ * that we "null-out" the aic7xxx_boards array. It is
+ * not guaranteed to be initialized to 0 (NULL). We use
+ * a NULL entry to indicate that no prior hosts have
+ * been found/registered for that IRQ.
+ */
+ for (i = 0; i <= MAXIRQ; i++)
+ {
+ aic7xxx_boards[i] = NULL;
+ }
+
+ /*
+ * Initialize the spurious count to 0.
+ */
+ aic7xxx_spurious_count = 0;
+
+ /*
+ * EISA/VL-bus card signature probe.
+ */
+ for (slot = MINSLOT; slot <= MAXSLOT; slot++)
+ {
+ base = SLOTBASE(slot) + MINREG;
+
+ if (check_region(MINREG + base, MAXREG - MINREG))
+ {
+ /*
+ * Some other driver has staked a
+ * claim to this i/o region already.
+ */
+ continue;
+ }
+
+ config.type = aic7xxx_probe(slot, HID0 + base);
+ if (config.type != AIC_NONE)
+ {
+ /*
+ * We found a card, allow 1 spurious interrupt.
+ */
+ aic7xxx_spurious_count = 1;
+
+ /*
+ * We "find" a AIC-7770 if we locate the card
+ * signature and we can set it up and register
+ * it with the kernel without incident.
+ */
+ config.chip_type = AIC_777x;
+ config.base = base;
+ config.irq = irq;
+ config.parity = AIC_UNKNOWN;
+ config.low_term = AIC_UNKNOWN;
+ config.high_term = AIC_UNKNOWN;
+ config.busrtime = 0;
+ config.walk_scbs = FALSE;
+ config.ultra_enabled = FALSE;
+ found += aic7xxx_register(template, &config);
+
+ /*
+ * Disallow spurious interrupts.
+ */
+ aic7xxx_spurious_count = 0;
+ }
+ }
+
+#ifdef CONFIG_PCI
+ /*
+ * PCI-bus probe.
+ */
+ if (pcibios_present())
+ {
+ struct
+ {
+ unsigned short vendor_id;
+ unsigned short device_id;
+ aha_type card_type;
+ aha_chip_type chip_type;
+ } const aic7xxx_pci_devices[] = {
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7850, AIC_7850, AIC_785x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7870, AIC_7870, AIC_787x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7871, AIC_7871, AIC_787x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7872, AIC_7872, AIC_787x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7873, AIC_7873, AIC_787x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7874, AIC_7874, AIC_787x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7880, AIC_7880, AIC_788x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7881, AIC_7881, AIC_788x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7882, AIC_7882, AIC_788x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7883, AIC_7883, AIC_788x},
+ {PCI_VENDOR_ID_ADAPTEC, PCI_DEVICE_ID_ADAPTEC_7884, AIC_7884, AIC_788x}
+ };
+
+ int error;
+ int done = 0;
+ unsigned int io_port;
+ unsigned short index = 0;
+ unsigned char pci_bus, pci_device_fn;
+ unsigned int csize_lattime;
+ unsigned int class_revid;
+ unsigned int devconfig;
+ char rev_id[] = {'B', 'C', 'D'};
+
+ for (i = 0; i < NUMBER(aic7xxx_pci_devices); i++)
+ {
+ done = FALSE;
+ while (!done)
+ {
+ if (pcibios_find_device(aic7xxx_pci_devices[i].vendor_id,
+ aic7xxx_pci_devices[i].device_id,
+ index, &pci_bus, &pci_device_fn))
+ {
+ done = TRUE;
+ }
+ else /* Found an Adaptec PCI device. */
+ {
+ config.type = aic7xxx_pci_devices[i].card_type;
+ config.chip_type = aic7xxx_pci_devices[i].chip_type;
+ config.chan_num = 0;
+ config.walk_scbs = FALSE;
+ switch (config.type)
+ {
+ case AIC_7872: /* 3940 */
+ case AIC_7882: /* 3940-Ultra */
+ config.walk_scbs = TRUE;
+ config.chan_num = number_of_39xxs & 0x1; /* Has 2 controllers */
+ number_of_39xxs++;
+ if (number_of_39xxs == 2)
+ {
+ number_of_39xxs = 0; /* To be consistent with 3985. */
+ }
+ break;
+
+ case AIC_7873: /* 3985 */
+ case AIC_7883: /* 3985-Ultra */
+ config.chan_num = number_of_39xxs & 0x3; /* Has 3 controllers */
+ number_of_39xxs++;
+ if (number_of_39xxs == 3)
+ {
+ number_of_39xxs = 0;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ /*
+ * Read esundry information from PCI BIOS.
+ */
+ error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, &io_port);
+ error += pcibios_read_config_byte(pci_bus, pci_device_fn,
+ PCI_INTERRUPT_LINE, &irq);
+
+ /*
+ * Ensure that we are using good values for the PCI burst size
+ * and latency timer.
+ */
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CSIZE_LATTIME, &csize_lattime);
+ if ((csize_lattime & CACHESIZE) == 0)
+ {
+ /* Default to 8DWDs - what's the PCI define for this? */
+ csize_lattime |= 8;
+ }
+ if((csize_lattime & LATTIME) == 0)
+ {
+ /* Default to 64 PCLKS (is this a good value?) */
+ /* This may also be availble in the SEEPROM?? */
+ csize_lattime |= (64 << 8);
+ }
+ pcibios_write_config_dword(pci_bus, pci_device_fn,
+ CSIZE_LATTIME, csize_lattime);
+ printk("aic7xxx: BurstLen = %d DWDs, Latency Timer = %d PCLKS\n",
+ (int) (csize_lattime & CACHESIZE),
+ (csize_lattime >> 8) & 0x000000ff);
+
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ CLASS_PROGIF_REVID, &class_revid);
+ if ((class_revid & DEVREVID) < 3)
+ {
+ printk("aic7xxx: %s Rev %c.\n", board_names[config.type],
+ rev_id[class_revid & DEVREVID]);
+ }
+
+ error += pcibios_read_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, &devconfig);
+ if (error)
+ {
+ panic("aic7xxx: (aic7xxx_detect) Error %d reading PCI registers.\n",
+ error);
+ }
+
+ printk("aic7xxx: devconfig = 0x%x.\n", devconfig);
+
+ /*
+ * The first bit of PCI_BASE_ADDRESS_0 is always set, so
+ * we mask it off.
+ */
+ base = io_port & 0xfffffffe;
+
+ /*
+ * I don't think we need to bother with allowing
+ * spurious interrupts for the 787x/7850, but what
+ * the hey.
+ */
+ aic7xxx_spurious_count = 1;
+
+ config.base = base;
+ config.irq = irq;
+ config.parity = AIC_UNKNOWN;
+ config.low_term = AIC_UNKNOWN;
+ config.high_term = AIC_UNKNOWN;
+ config.busrtime = 0;
+ config.ultra_enabled = FALSE;
+ if (devconfig & RAMPSM)
+ {
+ /*
+ * External SRAM present. Have the probe walk the SCBs to see
+ * how much SRAM we have and set the number of SCBs accordingly.
+ * We have to turn off SCBRAMSEL to access the external SCB
+ * SRAM.
+ *
+ * It seems that early versions of the aic7870 didn't use these
+ * bits, hence the hack for the 3940 above. I would guess that
+ * recent 3940s using later aic7870 or aic7880 chips do actually
+ * set RAMPSM.
+ *
+ * The documentation isn't clear, but it sounds like the value
+ * written to devconfig must not have RAMPSM set. The second
+ * sixteen bits of the register are R/O anyway, so it shouldn't
+ * affect RAMPSM either way.
+ */
+ printk ("aic7xxx: External RAM detected. Enabling RAM access.\n");
+ devconfig &= ~(RAMPSM | SCBRAMSEL);
+ pcibios_write_config_dword(pci_bus, pci_device_fn,
+ DEVCONFIG, devconfig);
+ config.walk_scbs = TRUE;
+ }
+ found += aic7xxx_register(template, &config);
+
+ /*
+ * Disable spurious interrupts.
+ */
+ aic7xxx_spurious_count = 0;
+
+ index++;
+ } /* Found an Adaptec PCI device. */
+ }
+ }
+ }
+#endif CONFIG_PCI
+
+ template->name = aic7xxx_info(NULL);
+ return (found);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_buildscb
+ *
+ * Description:
+ * Build a SCB.
+ *-F*************************************************************************/
+static void
+aic7xxx_buildscb(struct aic7xxx_host *p,
+ Scsi_Cmnd *cmd,
+ struct aic7xxx_scb *scb)
+{
+ void *addr;
+ unsigned short mask;
+ struct scatterlist *sg;
+
+ /*
+ * Setup the control byte if we need negotiation and have not
+ * already requested it.
+ */
+#ifdef AIC7XXX_TAGGED_QUEUEING
+ if (cmd->device->tagged_supported)
+ {
+ if (cmd->device->tagged_queue == 0)
+ {
+ printk("aic7xxx: Enabling tagged queuing for target %d, "
+ "channel %d.\n", cmd->target, cmd->channel);
+ cmd->device->tagged_queue = 1;
+ cmd->device->current_tag = 1; /* enable tagging */
+ }
+ cmd->tag = cmd->device->current_tag;
+ cmd->device->current_tag++;
+ scb->control |= TAG_ENB;
+ }
+#endif
+ mask = (0x01 << (cmd->target | (cmd->channel << 3)));
+ if (p->discenable & mask)
+ {
+ scb->control |= DISCENB;
+ }
+ if ((p->needwdtr & mask) && !(p->wdtr_pending & mask))
+ {
+ p->wdtr_pending |= mask;
+ scb->control |= NEEDWDTR;
+#if 0
+ printk("aic7xxx: Sending WDTR request to target %d.\n", cmd->target);
+#endif
+ }
+ else
+ {
+ if ((p->needsdtr & mask) && !(p->sdtr_pending & mask))
+ {
+ p->sdtr_pending |= mask;
+ scb->control |= NEEDSDTR;
+#if 0
+ printk("aic7xxx: Sending SDTR request to target %d.\n", cmd->target);
+#endif
+ }
+ }
+
+#if 0
+ printk("aic7xxx: (build_scb) Target %d, cmd(0x%x) size(%u) wdtr(0x%x) "
+ "mask(0x%x).\n",
+ cmd->target, cmd->cmnd[0], cmd->cmd_len, p->needwdtr, mask);
+#endif
+ scb->target_channel_lun = ((cmd->target << 4) & 0xF0) |
+ ((cmd->channel & 0x01) << 3) | (cmd->lun & 0x07);
+
+ /*
+ * The interpretation of request_buffer and request_bufflen
+ * changes depending on whether or not use_sg is zero; a
+ * non-zero use_sg indicates the number of elements in the
+ * scatter-gather array.
+ */
+
+ /*
+ * XXX - this relies on the host data being stored in a
+ * little-endian format.
+ */
+ addr = cmd->cmnd;
+ scb->SCSI_cmd_length = cmd->cmd_len;
+ memcpy(scb->SCSI_cmd_pointer, &addr, sizeof(scb->SCSI_cmd_pointer));
+
+ if (cmd->use_sg)
+ {
+ scb->SG_segment_count = cmd->use_sg;
+ memcpy(scb->SG_list_pointer, &cmd->request_buffer,
+ sizeof(scb->SG_list_pointer));
+ memcpy(&sg, &cmd->request_buffer, sizeof(sg));
+ memcpy(scb->data_pointer, &(sg[0].address), sizeof(scb->data_pointer));
+ scb->data_count = sg[0].length;
+#if 0
+ debug("aic7xxx: (build_scb) SG segs(%d), length(%u), sg[0].length(%d).\n",
+ cmd->use_sg, aic7xxx_length(cmd, 0), scb->data_count);
+#endif
+ }
+ else
+ {
+#if 0
+ debug("aic7xxx: (build_scb) Creating scatterlist, addr(0x%lx) length(%d).\n",
+ (unsigned long) cmd->request_buffer, cmd->request_bufflen);
+#endif
+ if (cmd->request_bufflen == 0)
+ {
+ /*
+ * In case the higher level SCSI code ever tries to send a zero
+ * length command, ensure the SCB indicates no data. The driver
+ * will interpret a zero length command as a Bus Device Reset.
+ */
+ scb->SG_segment_count = 0;
+ memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
+ memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
+ scb->data_count = 0;
+ }
+ else
+ {
+ scb->SG_segment_count = 1;
+ scb->sg.address = (char *) cmd->request_buffer;
+ scb->sg.length = cmd->request_bufflen;
+ addr = &scb->sg;
+ memcpy(scb->SG_list_pointer, &addr, sizeof(scb->SG_list_pointer));
+ scb->data_count = scb->sg.length;
+ memcpy(scb->data_pointer, &cmd->request_buffer, sizeof(scb->data_pointer));
+ }
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_queue
+ *
+ * Description:
+ * Queue a SCB to the controller.
+ *-F*************************************************************************/
+int
+aic7xxx_queue(Scsi_Cmnd *cmd, void (*fn)(Scsi_Cmnd *))
+{
+ long flags;
+ struct aic7xxx_host *p;
+ struct aic7xxx_scb *scb;
+
+ p = (struct aic7xxx_host *) cmd->host->hostdata;
+
+ /*
+ * Check to see if channel was scanned.
+ */
+ if (!p->a_scanned && (cmd->channel == 0))
+ {
+ printk("aic7xxx: Scanning channel A for devices.\n");
+ p->a_scanned = TRUE;
+ }
+ else
+ {
+ if (!p->b_scanned && (cmd->channel == 1))
+ {
+ printk("aic7xxx: Scanning channel B for devices.\n");
+ p->b_scanned = TRUE;
+ }
+ }
+
+#if 0
+ debug("aic7xxx: (queue) cmd(0x%x) size(%u), target %d, channel %d, lun %d.\n",
+ cmd->cmnd[0], cmd->cmd_len, cmd->target, cmd->channel,
+ cmd->lun & 0x07);
+#endif
+
+ /*
+ * This is a critical section, since we don't want the
+ * interrupt routine mucking with the host data or the
+ * card. Since the kernel documentation is vague on
+ * whether or not we are in a cli/sti pair already, save
+ * the flags to be on the safe side.
+ */
+ save_flags(flags);
+ cli();
+
+ /*
+ * Find a free slot in the SCB array to load this command
+ * into. Since can_queue is set to the maximum number of
+ * SCBs for the card, we should always find one.
+ *
+ * First try to find an scb in the free list. If there are
+ * none in the free list, then check the current number of
+ * of scbs and take an unused one from the scb array.
+ */
+ scb = p->free_scb;
+ if (scb != NULL)
+ { /* found one in the free list */
+ p->free_scb = scb->next; /* remove and update head of list */
+ /*
+ * Warning! For some unknown reason, the scb at the head
+ * of the free list is not the same address that it should
+ * be. That's why we set the scb pointer taken by the
+ * position in the array. The scb at the head of the list
+ * should match this address, but it doesn't.
+ */
+ scb = &(p->scb_array[scb->position]);
+ scb->control = 0;
+ scb->state = SCB_ACTIVE;
+ }
+ else
+ {
+ if (p->numscb >= p->maxscb)
+ {
+ panic("aic7xxx: (aic7xxx_queue) Couldn't find a free SCB.\n");
+ }
+ else
+ {
+ /*
+ * Initialize the scb within the scb array. The
+ * position within the array is the position on
+ * the board that it will be loaded.
+ */
+ scb = &(p->scb_array[p->numscb]);
+ memset(scb, 0, sizeof(*scb));
+
+ scb->position = p->numscb;
+ p->numscb++;
+ scb->state = SCB_ACTIVE;
+ }
+ }
+
+ scb->cmd = cmd;
+ aic7xxx_position(cmd) = scb->position;
+#if 0
+ debug_scb(scb);
+#endif;
+
+ /*
+ * Construct the SCB beforehand, so the sequencer is
+ * paused a minimal amount of time.
+ */
+ aic7xxx_buildscb(p, cmd, scb);
+
+#if 0
+ if (scb != &p->scb_array[scb->position])
+ {
+ printk("aic7xxx: (queue) Address of SCB by position does not match SCB "
+ "address.\n");
+ }
+ printk("aic7xxx: (queue) SCB pos(%d) cmdptr(0x%x) state(%d) freescb(0x%x)\n",
+ scb->position, (unsigned int) scb->cmd,
+ scb->state, (unsigned int) p->free_scb);
+#endif
+ /*
+ * Pause the sequencer so we can play with its registers -
+ * wait for it to acknowledge the pause.
+ *
+ * XXX - should the interrupts be left on while doing this?
+ */
+ PAUSE_SEQUENCER(p);
+
+ /*
+ * Save the SCB pointer and put our own pointer in - this
+ * selects one of the four banks of SCB registers. Load
+ * the SCB, then write its pointer into the queue in FIFO
+ * and restore the saved SCB pointer.
+ */
+ aic7xxx_putscb(p, scb);
+ outb(scb->position, QINFIFO + p->base);
+
+ /*
+ * Make sure the Scsi_Cmnd pointer is saved, the struct it
+ * points to is set up properly, and the parity error flag
+ * is reset, then unpause the sequencer and watch the fun
+ * begin.
+ */
+ cmd->scsi_done = fn;
+ aic7xxx_error(cmd) = DID_OK;
+ aic7xxx_status(cmd) = 0;
+ cmd->result = 0;
+ memset(&cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ UNPAUSE_SEQUENCER(p);
+#if 0
+ printk("aic7xxx: (queue) After - cmd(0x%lx) scb->cmd(0x%lx) pos(%d).\n",
+ (long) cmd, (long) scb->cmd, scb->position);
+#endif;
+ restore_flags(flags);
+ return (0);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort_scb
+ *
+ * Description:
+ * Abort an scb. If the scb has not previously been aborted, then
+ * we attempt to send a BUS_DEVICE_RESET message to the target. If
+ * the scb has previously been unsuccessfully aborted, then we will
+ * reset the channel and have all devices renegotiate. Returns an
+ * enumerated type that indicates the status of the operation.
+ *-F*************************************************************************/
+static aha_abort_reset_type
+aic7xxx_abort_scb(struct aic7xxx_host *p, struct aic7xxx_scb *scb,
+ unsigned char errcode)
+{
+ int base = p->base;
+ int found = FALSE;
+ aha_abort_reset_type scb_status = ABORT_RESET_SUCCESS;
+ char channel = scb->target_channel_lun & SELBUSB ? 'B': 'A';
+
+ /*
+ * Ensure that the card doesn't do anything
+ * behind our back.
+ */
+ PAUSE_SEQUENCER(p);
+
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_scb) scb %d, scb_aborted 0x%x\n",
+ scb->position, (scb->state & SCB_ABORTED));
+#endif
+ /*
+ * First, determine if we want to do a bus reset or simply a bus device
+ * reset. If this is the first time that a transaction has timed out,
+ * just schedule a bus device reset. Otherwise, we reset the bus and
+ * abort all pending I/Os on that bus.
+ */
+ if (scb->state & SCB_ABORTED)
+ {
+ /*
+ * Been down this road before. Do a full bus reset.
+ */
+ found = aic7xxx_reset_channel(p, channel, scb->position);
+ }
+ else
+ {
+ unsigned char active_scb, control;
+ struct aic7xxx_scb *active_scbp;
+
+ /*
+ * Send a Bus Device Reset Message:
+ * The target we select to send the message to may be entirely
+ * different than the target pointed to by the scb that timed
+ * out. If the command is in the QINFIFO or the waiting for
+ * selection list, its not tying up the bus and isn't responsible
+ * for the delay so we pick off the active command which should
+ * be the SCB selected by SCBPTR. If its disconnected or active,
+ * we device reset the target scbp points to. Although it may
+ * be that this target is not responsible for the delay, it may
+ * may also be that we're timing out on a command that just takes
+ * too much time, so we try the bus device reset there first.
+ */
+ active_scb = inb(SCBPTR + base);
+ active_scbp = &(p->scb_array[active_scb]);
+ control = inb(SCBARRAY + base);
+
+ /*
+ * Test to see if scbp is disconnected
+ */
+ outb(scb->position, SCBPTR + base);
+ if (inb(SCBARRAY + base) & DISCONNECTED)
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_scb) scb %d is disconnected.\n", scb->position);
+#endif
+ scb->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
+ scb->SG_segment_count = 0;
+ memset(scb->SG_list_pointer, 0, sizeof(scb->SG_list_pointer));
+ memset(scb->data_pointer, 0, sizeof(scb->data_pointer));
+ scb->data_count = 0;
+ aic7xxx_putscb(p, scb);
+ aic7xxx_error(scb->cmd) = errcode;
+ scb_status = ABORT_RESET_PENDING;
+ aic7xxx_add_waiting_scb(base, scb, LIST_SECOND);
+ UNPAUSE_SEQUENCER(p);
+ }
+ else
+ {
+ /*
+ * Is the active SCB really active?
+ */
+ if (active_scbp->state & SCB_ACTIVE)
+ {
+ unsigned char msg_len = inb(MSG_LEN + base);
+ if (msg_len != 0)
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_scb) scb is active, needs DMA, "
+ "msg_len is non-zero.\n");
+#endif
+ /*
+ * If we're in a message phase, tacking on another message
+ * may confuse the target totally. The bus is probably wedged,
+ * so reset the channel.
+ */
+ channel = (active_scbp->target_channel_lun & SELBUSB) ? 'B': 'A';
+ aic7xxx_reset_channel(p, channel, scb->position);
+ }
+ else
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_scb) scb is active, needs DMA, "
+ "msg_len is zero.\n");
+#endif
+ /*
+ * Load the message buffer and assert attention.
+ */
+ active_scbp->state |= (SCB_DEVICE_RESET | SCB_ABORTED);
+ outb(1, MSG_LEN + base);
+ outb(MSG_BUS_DEVICE_RESET, MSG0 + base);
+ if (active_scbp->target_channel_lun != scb->target_channel_lun)
+ {
+ /*
+ * XXX - We would like to increment the timeout on scb, but
+ * access to that routine is denied because it is hidden
+ * in scsi.c. If we were able to do this, it would give
+ * scb a new lease on life.
+ */
+ ;
+ }
+ aic7xxx_error(scb->cmd) = errcode;
+ scb_status = ABORT_RESET_PENDING;
+ UNPAUSE_SEQUENCER(p);
+ }
+ }
+ else
+ {
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_scb) no active command.\n");
+#endif
+ /*
+ * No active command to single out, so reset
+ * the bus for the timed out target.
+ */
+ aic7xxx_reset_channel(p, channel, scb->position);
+ }
+ }
+ }
+ return (scb_status);
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort_reset
+ *
+ * Description:
+ * Abort or reset the current SCSI command(s). Returns an enumerated
+ * type that indicates the status of the operation.
+ *-F*************************************************************************/
+static aha_abort_reset_type
+aic7xxx_abort_reset(Scsi_Cmnd *cmd, unsigned char errcode)
+{
+ struct aic7xxx_scb *scb;
+ struct aic7xxx_host *p;
+ long flags;
+ aha_abort_reset_type scb_status = ABORT_RESET_SUCCESS;
+
+ p = (struct aic7xxx_host *) cmd->host->hostdata;
+ scb = &(p->scb_array[aic7xxx_position(cmd)]);
+
+ save_flags(flags);
+ cli();
+
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_reset) scb state 0x%x\n", scb->state);
+#endif
+
+ if (scb->state & SCB_ACTIVE)
+ {
+ if (scb->state & SCB_IMMED)
+ {
+ /*
+ * Don't know how set the number of retries to 0.
+ */
+ /* cmd->retries = 0; */
+ aic7xxx_error(cmd) = errcode;
+ aic7xxx_done(p, scb);
+ }
+ else
+ {
+ /*
+ * Abort the operation.
+ */
+ scb_status = aic7xxx_abort_scb(p, scb, errcode);
+ }
+ }
+ else
+ {
+ /*
+ * The scb is not active and must have completed after the timeout
+ * check in scsi.c and before we check the scb state above. For
+ * this case we return SCSI_ABORT_NOT_RUNNING (if abort was called)
+ * or SCSI_RESET_SUCCESS (if reset was called).
+ */
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort_reset) called with no active scb, errcode 0x%x\n",
+ errcode);
+#endif
+ scb_status = ABORT_RESET_INACTIVE;
+ /*
+ * According to the comments in scsi.h and Michael Neuffer, if we do not
+ * have an active command for abort or reset, we should not call the
+ * command done function. Unfortunately, this hangs the system for me
+ * unless we *do* call the done function.
+ *
+ * XXX - Revisit this sometime!
+ */
+ cmd->result = errcode << 16;
+ cmd->scsi_done(cmd);
+ }
+ restore_flags(flags);
+ return (scb_status);
+}
+
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_abort
+ *
+ * Description:
+ * Abort the current SCSI command(s).
+ *-F*************************************************************************/
+int
+aic7xxx_abort(Scsi_Cmnd *cmd)
+{
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (abort) target/channel %d/%d\n", cmd->target, cmd->channel);
+#endif
+
+ switch (aic7xxx_abort_reset(cmd, DID_ABORT))
+ {
+ case ABORT_RESET_INACTIVE:
+ return (SCSI_ABORT_NOT_RUNNING);
+ break;
+ case ABORT_RESET_PENDING:
+ return (SCSI_ABORT_PENDING);
+ break;
+ case ABORT_RESET_SUCCESS:
+ default:
+ return (SCSI_ABORT_SUCCESS);
+ break;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_reset
+ *
+ * Description:
+ * Resetting the bus always succeeds - is has to, otherwise the
+ * kernel will panic! Try a surgical technique - sending a BUS
+ * DEVICE RESET message - on the offending target before pulling
+ * the SCSI bus reset line.
+ *-F*************************************************************************/
+int
+aic7xxx_reset(Scsi_Cmnd *cmd)
+{
+#ifdef AIC7XXX_DEBUG_ABORT
+ printk ("aic7xxx: (reset) target/channel %d/%d\n", cmd->target, cmd->channel);
+#endif
+
+ switch (aic7xxx_abort_reset(cmd, DID_RESET))
+ {
+ case ABORT_RESET_PENDING:
+ return (SCSI_RESET_PENDING);
+ break;
+ case ABORT_RESET_INACTIVE:
+ case ABORT_RESET_SUCCESS:
+ default:
+ return (SCSI_RESET_SUCCESS);
+ break;
+ }
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_biosparam
+ *
+ * Description:
+ * Return the disk geometry for the given SCSI device.
+ *-F*************************************************************************/
+int
+aic7xxx_biosparam(Disk *disk, kdev_t dev, int geom[])
+{
+ int heads, sectors, cylinders;
+ struct aic7xxx_host *p;
+
+ p = (struct aic7xxx_host *) disk->device->host->hostdata;
+
+ /*
+ * XXX - if I could portably find the card's configuration
+ * information, then this could be autodetected instead
+ * of left to a boot-time switch.
+ */
+ heads = 64;
+ sectors = 32;
+ cylinders = disk->capacity / (heads * sectors);
+
+ if (p->extended && cylinders > 1024)
+ {
+ heads = 255;
+ sectors = 63;
+ cylinders = disk->capacity / (255 * 63);
+ }
+
+ geom[0] = heads;
+ geom[1] = sectors;
+ geom[2] = cylinders;
+
+ return (0);
+}
+
+#ifdef MACH
+#include "aic7xxx_proc.src"
+#else
+#include "aic7xxx_proc.c"
+#endif
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = AIC7XXX;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost 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: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
+
diff --git a/i386/i386at/gpl/linux/scsi/aic7xxx.h b/i386/i386at/gpl/linux/scsi/aic7xxx.h
new file mode 100644
index 00000000..76d4d962
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aic7xxx.h
@@ -0,0 +1,67 @@
+/*+M*************************************************************************
+ * Adaptec 274x/284x/294x device driver for Linux.
+ *
+ * Copyright (c) 1994 John Aycock
+ * The University of Calgary Department of Computer Science.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: aic7xxx.h,v 1.1.1.1 1997/02/25 21:27:47 thomas Exp $
+ *-M*************************************************************************/
+#ifndef _aic7xxx_h
+#define _aic7xxx_h
+
+#define AIC7XXX_H_VERSION "$Revision: 1.1.1.1 $"
+
+/*
+ * Scsi_Host_Template (see hosts.h) for AIC-7770/AIC-7870 - some fields
+ * to do with card config are filled in after the card is detected.
+ */
+#define AIC7XXX { \
+ NULL, \
+ NULL, \
+ NULL, \
+ aic7xxx_proc_info, \
+ NULL, \
+ aic7xxx_detect, \
+ NULL, \
+ aic7xxx_info, \
+ NULL, \
+ aic7xxx_queue, \
+ aic7xxx_abort, \
+ aic7xxx_reset, \
+ NULL, \
+ aic7xxx_biosparam, \
+ -1, /* max simultaneous cmds */\
+ -1, /* scsi id of host adapter */\
+ SG_ALL, /* max scatter-gather cmds */\
+ 2, /* cmds per lun (linked cmds) */\
+ 0, /* number of 7xxx's present */\
+ 0, /* no memory DMA restrictions */\
+ ENABLE_CLUSTERING \
+}
+
+extern int aic7xxx_queue(Scsi_Cmnd *, void (*)(Scsi_Cmnd *));
+extern int aic7xxx_biosparam(Disk *, kdev_t, int[]);
+extern int aic7xxx_detect(Scsi_Host_Template *);
+extern int aic7xxx_command(Scsi_Cmnd *);
+extern int aic7xxx_abort(Scsi_Cmnd *);
+extern int aic7xxx_reset(Scsi_Cmnd *);
+
+extern const char *aic7xxx_info(struct Scsi_Host *);
+
+extern int aic7xxx_proc_info(char *, char **, off_t, int, int, int);
+
+#endif /* _aic7xxx_h */
diff --git a/i386/i386at/gpl/linux/scsi/aic7xxx_proc.src b/i386/i386at/gpl/linux/scsi/aic7xxx_proc.src
new file mode 100644
index 00000000..65996822
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aic7xxx_proc.src
@@ -0,0 +1,271 @@
+/*+M*************************************************************************
+ * Adaptec 274x/284x/294x device driver proc support for Linux.
+ *
+ * Copyright (c) 1995 Dean W. Gehnert
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, or (at your option)
+ * any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * ----------------------------------------------------------------
+ * o Modified from the EATA /proc support.
+ * o Additional support for device block statistics provided by
+ * Matthew Jacob.
+ *
+ * Dean W. Gehnert, deang@ims.com, 08/30/95
+ *
+ * $Id: aic7xxx_proc.src,v 1.1.1.1 1997/02/25 21:27:47 thomas Exp $
+ *-M*************************************************************************/
+
+#define BLS buffer + len + size
+#define HDRB \
+" < 512 512-1K 1-2K 2-4K 4-8K 8-16K 16-32K 32-64K 64-128K >128K"
+
+#ifdef PROC_DEBUG
+extern int vsprintf(char *, const char *, va_list);
+
+static void
+proc_debug(const char *fmt, ...)
+{
+ va_list ap;
+ char buf[256];
+
+ va_start(ap, fmt);
+ vsprintf(buf, fmt, ap);
+ printk(buf);
+ va_end(ap);
+}
+#else /* PROC_DEBUG */
+# define proc_debug(fmt, args...)
+#endif /* PROC_DEBUG */
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_set_info
+ *
+ * Description:
+ * Set parameters for the driver from the /proc filesystem.
+ *-F*************************************************************************/
+int
+aic7xxx_set_info(char *buffer, int length, struct Scsi_Host *HBAptr)
+{
+ proc_debug("aic7xxx_set_info(): %s\n", buffer);
+ return (-ENOSYS); /* Currently this is a no-op */
+}
+
+/*+F*************************************************************************
+ * Function:
+ * aic7xxx_proc_info
+ *
+ * Description:
+ * Return information to handle /proc support for the driver.
+ *-F*************************************************************************/
+int
+aic7xxx_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ struct Scsi_Host *HBAptr;
+ struct aic7xxx_host *p;
+ static u8 buff[512];
+ int i;
+ int size = 0;
+ int len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ static char *bus_name[] = {"Single", "Twin", "Wide"};
+
+ HBAptr = NULL;
+ for (i = 0; i < NUMBER(aic7xxx_boards); i++)
+ {
+ if ((HBAptr = aic7xxx_boards[i]) != NULL)
+ {
+ if (HBAptr->host_no == hostno)
+ {
+ break;
+ }
+
+ while ((HBAptr->hostdata != NULL) &&
+ ((HBAptr = ((struct aic7xxx_host *) HBAptr->hostdata)->next) != NULL))
+ {
+ if (HBAptr->host_no == hostno)
+ {
+ break; break;
+ }
+ }
+
+ HBAptr = NULL;
+ }
+ }
+
+ if (HBAptr == NULL)
+ {
+ size += sprintf(BLS, "Can't find adapter for host number %d\n", hostno);
+ len += size; pos = begin + len; size = 0;
+ goto stop_output;
+ }
+
+ if (inout == TRUE) /* Has data been written to the file? */
+ {
+ return (aic7xxx_set_info(buffer, length, HBAptr));
+ }
+
+ if (offset == 0)
+ {
+ memset(buff, 0, sizeof(buff));
+ }
+
+ p = (struct aic7xxx_host *) HBAptr->hostdata;
+
+ size += sprintf(BLS, "Adaptec AIC7xxx driver version: ");
+ size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_C_VERSION));
+ size += sprintf(BLS, "%s/", rcs_version(AIC7XXX_H_VERSION));
+ size += sprintf(BLS, "%s\n", rcs_version(AIC7XXX_SEQ_VER));
+ len += size; pos = begin + len; size = 0;
+
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Compile Options:\n");
+#ifdef AIC7XXX_RESET_DELAY
+ size += sprintf(BLS, " AIC7XXX_RESET_DELAY : %d\n", AIC7XXX_RESET_DELAY);
+#endif
+#ifdef AIC7XXX_TWIN_SUPPORT
+ size += sprintf(BLS, " AIC7XXX_TWIN_SUPPORT : Enabled\n");
+#else
+ size += sprintf(BLS, " AIC7XXX_TWIN_SUPPORT : Disabled\n");
+#endif
+#ifdef AIC7XXX_TAGGED_QUEUEING
+ size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Enabled\n");
+#else
+ size += sprintf(BLS, " AIC7XXX_TAGGED_QUEUEING: Disabled\n");
+#endif
+#ifdef AIC7XXX_SHARE_IRQS
+ size += sprintf(BLS, " AIC7XXX_SHARE_IRQS : Enabled\n");
+#else
+ size += sprintf(BLS, " AIC7XXX_SHARE_IRQS : Disabled\n");
+#endif
+#ifdef AIC7XXX_PROC_STATS
+ size += sprintf(BLS, " AIC7XXX_PROC_STATS : Enabled\n");
+#else
+ size += sprintf(BLS, " AIC7XXX_PROC_STATS : Disabled\n");
+#endif
+ len += size; pos = begin + len; size = 0;
+
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Adapter Configuration:\n");
+ size += sprintf(BLS, " SCSI Adapter: %s\n", board_names[p->type]);
+ size += sprintf(BLS, " Host Bus: %s\n", bus_name[p->bus_type]);
+ size += sprintf(BLS, " Base IO: %#.4x\n", p->base);
+ size += sprintf(BLS, " IRQ: %d\n", HBAptr->irq);
+ size += sprintf(BLS, " SCB: %d (%d)\n", p->numscb, p->maxscb);
+ size += sprintf(BLS, " Interrupts: %d", p->isr_count);
+ if (p->chip_type == AIC_777x)
+ {
+ size += sprintf(BLS, " %s\n",
+ (p->pause & IRQMS) ? "(Level Sensitive)" : "(Edge Triggered)");
+ }
+ else
+ {
+ size += sprintf(BLS, "\n");
+ }
+ size += sprintf(BLS, " Serial EEPROM: %s\n",
+ p->have_seeprom ? "True" : "False");
+ size += sprintf(BLS, " Pause/Unpause: %#.2x/%#.2x\n", p->pause,
+ p->unpause);
+ size += sprintf(BLS, " Extended Translation: %sabled\n",
+ p->extended ? "En" : "Dis");
+ size += sprintf(BLS, " SCSI Bus Reset: %sabled\n",
+ aic7xxx_no_reset ? "Dis" : "En");
+ size += sprintf(BLS, " Ultra SCSI: %sabled\n",
+ p->ultra_enabled ? "En" : "Dis");
+ len += size; pos = begin + len; size = 0;
+
+#ifdef AIC7XXX_PROC_STATS
+ {
+ struct aic7xxx_xferstats *sp;
+ int channel, target, lun;
+
+ /*
+ * XXX: Need to fix this to avoid overflow...
+ */
+ size += sprintf(BLS, "\n");
+ size += sprintf(BLS, "Statistics:\n");
+ for (channel = 0; channel < 2; channel++)
+ {
+ for (target = 0; target < 16; target++)
+ {
+ for (lun = 0; lun < 8; lun++)
+ {
+ sp = &p->stats[channel][target][lun];
+ if (sp->xfers == 0)
+ {
+ continue;
+ }
+ size += sprintf(BLS, "CHAN#%c (TGT %d LUN %d):\n",
+ 'A' + channel, target, lun);
+ size += sprintf(BLS, "nxfers %ld (%ld read;%ld written)\n",
+ sp->xfers, sp->r_total, sp->w_total);
+ size += sprintf(BLS, "blks(512) rd=%ld; blks(512) wr=%ld\n",
+ sp->r_total512, sp->w_total512);
+ size += sprintf(BLS, "%s\n", HDRB);
+ size += sprintf(BLS, " Reads:");
+ size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->r_bins[0],
+ sp->r_bins[1], sp->r_bins[2], sp->r_bins[3]);
+ size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->r_bins[4],
+ sp->r_bins[5], sp->r_bins[6], sp->r_bins[7]);
+ size += sprintf(BLS, "%6ld %6ld\n", sp->r_bins[8],
+ sp->r_bins[9]);
+ size += sprintf(BLS, "Writes:");
+ size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->w_bins[0],
+ sp->w_bins[1], sp->w_bins[2], sp->w_bins[3]);
+ size += sprintf(BLS, "%6ld %6ld %6ld %6ld ", sp->w_bins[4],
+ sp->w_bins[5], sp->w_bins[6], sp->w_bins[7]);
+ size += sprintf(BLS, "%6ld %6ld\n", sp->w_bins[8],
+ sp->w_bins[9]);
+ size += sprintf(BLS, "\n");
+ }
+ }
+ }
+ len += size; pos = begin + len; size = 0;
+ }
+#endif /* AIC7XXX_PROC_STATS */
+
+stop_output:
+ proc_debug("2pos: %ld offset: %ld len: %d\n", pos, offset, len);
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin); /* Start slop */
+ if (len > length)
+ {
+ len = length; /* Ending slop */
+ }
+ proc_debug("3pos: %ld offset: %ld len: %d\n", pos, offset, len);
+
+ return (len);
+}
+
+/*
+ * 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: 2
+ * c-brace-imaginary-offset: 0
+ * c-brace-offset: -2
+ * c-argdecl-indent: 2
+ * c-label-offset: -2
+ * c-continued-statement-offset: 2
+ * c-continued-brace-offset: 0
+ * indent-tabs-mode: nil
+ * tab-width: 8
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/scsi/aic7xxx_reg.h b/i386/i386at/gpl/linux/scsi/aic7xxx_reg.h
new file mode 100644
index 00000000..4a7f612c
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aic7xxx_reg.h
@@ -0,0 +1,746 @@
+/*+M*************************************************************************
+ * Adaptec AIC7xxx register and scratch ram definitions.
+ *
+ * Copyright (c) 1994, 1995, 1996 Justin T. Gibbs.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2, 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; see the file COPYING. If not, write to
+ * the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.
+ *
+ * $Id: aic7xxx_reg.h,v 1.1.1.1 1997/02/25 21:27:47 thomas Exp $
+ *-M*************************************************************************/
+
+/*
+ * This header is shared by the sequencer code and the kernel level driver.
+ *
+ * All page numbers refer to the Adaptec AIC-7770 Data Book availible from
+ * Adaptec's Technical Documents Department 1-800-934-2766
+ */
+
+/*
+ * SCSI Sequence Control (p. 3-11).
+ * Each bit, when set starts a specific SCSI sequence on the bus
+ */
+#define SCSISEQ 0x000
+#define TEMODEO 0x80
+#define ENSELO 0x40
+#define ENSELI 0x20
+#define ENRSELI 0x10
+#define ENAUTOATNO 0x08
+#define ENAUTOATNI 0x04
+#define ENAUTOATNP 0x02
+#define SCSIRSTO 0x01
+
+/*
+ * SCSI Transfer Control 0 Register (pp. 3-13).
+ * Controls the SCSI module data path.
+ */
+#define SXFRCTL0 0x001
+#define DFON 0x80
+#define DFPEXP 0x40
+#define ULTRAEN 0x20
+#define CLRSTCNT 0x10
+#define SPIOEN 0x08
+#define SCAMEN 0x04
+#define CLRCHN 0x02
+/* UNUSED 0x01 */
+
+/*
+ * SCSI Transfer Control 1 Register (pp. 3-14,15).
+ * Controls the SCSI module data path.
+ */
+#define SXFRCTL1 0x002
+#define BITBUCKET 0x80
+#define SWRAPEN 0x40
+#define ENSPCHK 0x20
+#define STIMESEL 0x18
+#define ENSTIMER 0x04
+#define ACTNEGEN 0x02
+#define STPWEN 0x01 /* Powered Termination */
+
+/*
+ * SCSI Control Signal Read Register (p. 3-15).
+ * Reads the actual state of the SCSI bus pins
+ */
+#define SCSISIGI 0x003
+#define CDI 0x80
+#define IOI 0x40
+#define MSGI 0x20
+#define ATNI 0x10
+#define SELI 0x08
+#define BSYI 0x04
+#define REQI 0x02
+#define ACKI 0x01
+
+/*
+ * Possible phases in SCSISIGI
+ */
+#define PHASE_MASK 0xe0
+#define P_DATAOUT 0x00
+#define P_DATAIN 0x40
+#define P_COMMAND 0x80
+#define P_MESGOUT 0xa0
+#define P_STATUS 0xc0
+#define P_MESGIN 0xe0
+/*
+ * SCSI Contol Signal Write Register (p. 3-16).
+ * Writing to this register modifies the control signals on the bus. Only
+ * those signals that are allowed in the current mode (Initiator/Target) are
+ * asserted.
+ */
+#define SCSISIGO 0x003
+#define CDO 0x80
+#define IOO 0x40
+#define MSGO 0x20
+#define ATNO 0x10
+#define SELO 0x08
+#define BSYO 0x04
+#define REQO 0x02
+#define ACKO 0x01
+
+/*
+ * SCSI Rate Control (p. 3-17).
+ * Contents of this register determine the Synchronous SCSI data transfer
+ * rate and the maximum synchronous Req/Ack offset. An offset of 0 in the
+ * SOFS (3:0) bits disables synchronous data transfers. Any offset value
+ * greater than 0 enables synchronous transfers.
+ */
+#define SCSIRATE 0x004
+#define WIDEXFER 0x80 /* Wide transfer control */
+#define SXFR 0x70 /* Sync transfer rate */
+#define SOFS 0x0f /* Sync offset */
+
+/*
+ * SCSI ID (p. 3-18).
+ * Contains the ID of the board and the current target on the
+ * selected channel.
+ */
+#define SCSIID 0x005
+#define TID 0xf0 /* Target ID mask */
+#define OID 0x0f /* Our ID mask */
+
+/*
+ * SCSI Latched Data (p. 3-19).
+ * Read/Write latchs used to transfer data on the SCSI bus during
+ * Automatic or Manual PIO mode. SCSIDATH can be used for the
+ * upper byte of a 16bit wide asyncronouse data phase transfer.
+ */
+#define SCSIDATL 0x006
+#define SCSIDATH 0x007
+
+/*
+ * SCSI Transfer Count (pp. 3-19,20)
+ * These registers count down the number of bytes transfered
+ * across the SCSI bus. The counter is decremented only once
+ * the data has been safely transfered. SDONE in SSTAT0 is
+ * set when STCNT goes to 0
+ */
+#define STCNT 0x008
+#define STCNT0 0x008
+#define STCNT1 0x009
+#define STCNT2 0x00a
+
+/*
+ * Clear SCSI Interrupt 0 (p. 3-20)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT0.
+ */
+#define CLRSINT0 0x00b
+#define CLRSELDO 0x40
+#define CLRSELDI 0x20
+#define CLRSELINGO 0x10
+#define CLRSWRAP 0x08
+/* UNUSED 0x04 */
+#define CLRSPIORDY 0x02
+/* UNUSED 0x01 */
+
+/*
+ * SCSI Status 0 (p. 3-21)
+ * Contains one set of SCSI Interrupt codes
+ * These are most likely of interest to the sequencer
+ */
+#define SSTAT0 0x00b
+#define TARGET 0x80 /* Board acting as target */
+#define SELDO 0x40 /* Selection Done */
+#define SELDI 0x20 /* Board has been selected */
+#define SELINGO 0x10 /* Selection In Progress */
+#define SWRAP 0x08 /* 24bit counter wrap */
+#define SDONE 0x04 /* STCNT = 0x000000 */
+#define SPIORDY 0x02 /* SCSI PIO Ready */
+#define DMADONE 0x01 /* DMA transfer completed */
+
+/*
+ * Clear SCSI Interrupt 1 (p. 3-23)
+ * Writing a 1 to a bit clears the associated SCSI Interrupt in SSTAT1.
+ */
+#define CLRSINT1 0x00c
+#define CLRSELTIMEO 0x80
+#define CLRATNO 0x40
+#define CLRSCSIRSTI 0x20
+/* UNUSED 0x10 */
+#define CLRBUSFREE 0x08
+#define CLRSCSIPERR 0x04
+#define CLRPHASECHG 0x02
+#define CLRREQINIT 0x01
+
+/*
+ * SCSI Status 1 (p. 3-24)
+ */
+#define SSTAT1 0x00c
+#define SELTO 0x80
+#define ATNTARG 0x40
+#define SCSIRSTI 0x20
+#define PHASEMIS 0x10
+#define BUSFREE 0x08
+#define SCSIPERR 0x04
+#define PHASECHG 0x02
+#define REQINIT 0x01
+
+/*
+ * SCSI Interrupt Mode 1 (pp. 3-28,29)
+ * Setting any bit will enable the corresponding function
+ * in SIMODE1 to interrupt via the IRQ pin.
+ */
+#define SIMODE1 0x011
+#define ENSELTIMO 0x80
+#define ENATNTARG 0x40
+#define ENSCSIRST 0x20
+#define ENPHASEMIS 0x10
+#define ENBUSFREE 0x08
+#define ENSCSIPERR 0x04
+#define ENPHASECHG 0x02
+#define ENREQINIT 0x01
+
+/*
+ * SCSI Data Bus (High) (p. 3-29)
+ * This register reads data on the SCSI Data bus directly.
+ */
+#define SCSIBUSL 0x012
+#define SCSIBUSH 0x013
+
+/*
+ * SCSI/Host Address (p. 3-30)
+ * These registers hold the host address for the byte about to be
+ * transfered on the SCSI bus. They are counted up in the same
+ * manner as STCNT is counted down. SHADDR should always be used
+ * to determine the address of the last byte transfered since HADDR
+ * can be squewed by write ahead.
+ */
+#define SHADDR 0x014
+#define SHADDR0 0x014
+#define SHADDR1 0x015
+#define SHADDR2 0x016
+#define SHADDR3 0x017
+
+/*
+ * Selection/Reselection ID (p. 3-31)
+ * Upper four bits are the device id. The ONEBIT is set when the re/selecting
+ * device did not set its own ID.
+ */
+#define SELID 0x019
+#define SELID_MASK 0xf0
+#define ONEBIT 0x08
+/* UNUSED 0x07 */
+
+/*
+ * SCSI Block Control (p. 3-32)
+ * Controls Bus type and channel selection. In a twin channel configuration
+ * addresses 0x00-0x1e are gated to the appropriate channel based on this
+ * register. SELWIDE allows for the coexistence of 8bit and 16bit devices
+ * on a wide bus.
+ */
+#define SBLKCTL 0x01f
+#define DIAGLEDEN 0x80 /* Aic78X0 only */
+#define DIAGLEDON 0x40 /* Aic78X0 only */
+#define AUTOFLUSHDIS 0x20
+/* UNUSED 0x10 */
+#define SELBUS_MASK 0x0a
+#define SELBUSB 0x08
+/* UNUSED 0x04 */
+#define SELWIDE 0x02
+/* UNUSED 0x01 */
+#define SELNARROW 0x00
+
+/*
+ * Sequencer Control (p. 3-33)
+ * Error detection mode and speed configuration
+ */
+#define SEQCTL 0x060
+#define PERRORDIS 0x80
+#define PAUSEDIS 0x40
+#define FAILDIS 0x20
+#define FASTMODE 0x10
+#define BRKADRINTEN 0x08
+#define STEP 0x04
+#define SEQRESET 0x02
+#define LOADRAM 0x01
+
+/*
+ * Sequencer RAM Data (p. 3-34)
+ * Single byte window into the Scratch Ram area starting at the address
+ * specified by SEQADDR0 and SEQADDR1. To write a full word, simply write
+ * four bytes in sucessesion. The SEQADDRs will increment after the most
+ * significant byte is written
+ */
+#define SEQRAM 0x061
+
+/*
+ * Sequencer Address Registers (p. 3-35)
+ * Only the first bit of SEQADDR1 holds addressing information
+ */
+#define SEQADDR0 0x062
+#define SEQADDR1 0x063
+#define SEQADDR1_MASK 0x01
+
+/*
+ * Accumulator
+ * We cheat by passing arguments in the Accumulator up to the kernel driver
+ */
+#define ACCUM 0x064
+
+#define SINDEX 0x065
+#define DINDEX 0x066
+#define ALLZEROS 0x06a
+#define NONE 0x06a
+#define SINDIR 0x06c
+#define DINDIR 0x06d
+#define FUNCTION1 0x06e
+
+/*
+ * Host Address (p. 3-48)
+ * This register contains the address of the byte about
+ * to be transfered across the host bus.
+ */
+#define HADDR 0x088
+#define HADDR0 0x088
+#define HADDR1 0x089
+#define HADDR2 0x08a
+#define HADDR3 0x08b
+
+#define HCNT 0x08c
+#define HCNT0 0x08c
+#define HCNT1 0x08d
+#define HCNT2 0x08e
+
+/*
+ * SCB Pointer (p. 3-49)
+ * Gate one of the four SCBs into the SCBARRAY window.
+ */
+#define SCBPTR 0x090
+
+/*
+ * Board Control (p. 3-43)
+ */
+#define BCTL 0x084
+/* RSVD 0xf0 */
+#define ACE 0x08 /* Support for external processors */
+/* RSVD 0x06 */
+#define ENABLE 0x01
+
+/*
+ * On the aic78X0 chips, Board Control is replaced by the DSCommand
+ * register (p. 4-64)
+ */
+#define DSCOMMAND 0x084
+#define CACHETHEN 0x80 /* Cache Threshold enable */
+#define DPARCKEN 0x40 /* Data Parity Check Enable */
+#define MPARCKEN 0x20 /* Memory Parity Check Enable */
+#define EXTREQLCK 0x10 /* External Request Lock */
+
+/*
+ * Bus On/Off Time (p. 3-44)
+ */
+#define BUSTIME 0x085
+#define BOFF 0xf0
+#define BON 0x0f
+#define BOFF_60BCLKS 0xf0
+
+/*
+ * Bus Speed (p. 3-45)
+ */
+#define BUSSPD 0x086
+#define DFTHRSH 0xc0
+#define STBOFF 0x38
+#define STBON 0x07
+#define DFTHRSH_100 0xc0
+
+/*
+ * Host Control (p. 3-47) R/W
+ * Overal host control of the device.
+ */
+#define HCNTRL 0x087
+/* UNUSED 0x80 */
+#define POWRDN 0x40
+/* UNUSED 0x20 */
+#define SWINT 0x10
+#define IRQMS 0x08
+#define PAUSE 0x04
+#define INTEN 0x02
+#define CHIPRST 0x01
+
+/*
+ * Interrupt Status (p. 3-50)
+ * Status for system interrupts
+ */
+#define INTSTAT 0x091
+#define SEQINT_MASK 0xf1 /* SEQINT Status Codes */
+#define BAD_PHASE 0x01 /* unknown scsi bus phase */
+#define SEND_REJECT 0x11 /* sending a message reject */
+#define NO_IDENT 0x21 /* no IDENTIFY after reconnect*/
+#define NO_MATCH 0x31 /* no cmd match for reconnect */
+#define SDTR_MSG 0x41 /* SDTR message recieved */
+#define WDTR_MSG 0x51 /* WDTR message recieved */
+#define REJECT_MSG 0x61 /* Reject message recieved */
+#define BAD_STATUS 0x71 /* Bad status from target */
+#define RESIDUAL 0x81 /* Residual byte count != 0 */
+#define ABORT_TAG 0x91 /* Sent an ABORT_TAG message */
+#define AWAITING_MSG 0xa1 /*
+ * Kernel requested to specify
+ * a message to this target
+ * (command was null), so tell
+ * it that it can fill the
+ * message buffer.
+ */
+#define IMMEDDONE 0xb1 /*
+ * An immediate command has
+ * completed
+ */
+#define MSG_BUFFER_BUSY 0xc1 /*
+ * Sequencer wants to use the
+ * message buffer, but it
+ * already contains a message
+ */
+#define MSGIN_PHASEMIS 0xd1 /*
+ * Target changed phase on us
+ * when we were expecting
+ * another msgin byte.
+ */
+#define PARITY_ERROR 0xe1 /*
+ * Sequencer detected a parity
+ * error.
+ */
+#define BRKADRINT 0x08
+#define SCSIINT 0x04
+#define CMDCMPLT 0x02
+#define SEQINT 0x01
+#define INT_PEND (BRKADRINT | SEQINT | SCSIINT | CMDCMPLT)
+
+/*
+ * Hard Error (p. 3-53)
+ * Reporting of catastrophic errors. You usually cannot recover from
+ * these without a full board reset.
+ */
+#define ERROR 0x092
+/* UNUSED 0xf0 */
+#define PARERR 0x08
+#define ILLOPCODE 0x04
+#define ILLSADDR 0x02
+#define ILLHADDR 0x01
+
+/*
+ * Clear Interrupt Status (p. 3-52)
+ */
+#define CLRINT 0x092
+#define CLRBRKADRINT 0x08
+#define CLRSCSIINT 0x04
+#define CLRCMDINT 0x02
+#define CLRSEQINT 0x01
+
+#define DFCNTRL 0x093
+#define WIDEODD 0x40
+#define SCSIEN 0x20
+#define SDMAEN 0x10
+#define SDMAENACK 0x10
+#define HDMAEN 0x08
+#define HDMAENACK 0x08
+#define DIRECTION 0x04
+#define FIFOFLUSH 0x02
+#define FIFORESET 0x01
+
+#define DFSTATUS 0x094
+#define HDONE 0x08
+#define FIFOEMP 0x01
+
+#define DFDAT 0x099
+
+/*
+ * SCB Auto Increment (p. 3-59)
+ * Byte offset into the SCB Array and an optional bit to allow auto
+ * incrementing of the address during download and upload operations
+ */
+#define SCBCNT 0x09a
+#define SCBAUTO 0x80
+#define SCBCNT_MASK 0x1f
+
+/*
+ * Queue In FIFO (p. 3-60)
+ * Input queue for queued SCBs (commands that the seqencer has yet to start)
+ */
+#define QINFIFO 0x09b
+
+/*
+ * Queue In Count (p. 3-60)
+ * Number of queued SCBs
+ */
+#define QINCNT 0x09c
+
+/*
+ * Queue Out FIFO (p. 3-61)
+ * Queue of SCBs that have completed and await the host
+ */
+#define QOUTFIFO 0x09d
+
+/*
+ * Queue Out Count (p. 3-61)
+ * Number of queued SCBs in the Out FIFO
+ */
+#define QOUTCNT 0x09e
+
+/*
+ * SCB Definition (p. 5-4)
+ * The two reserved bytes at SCBARRAY+1[23] are expected to be set to
+ * zero. Bit 3 in SCBARRAY+0 is used as an internal flag to indicate
+ * whether or not to DMA an SCB from host ram. This flag prevents the
+ * "re-fetching" of transactions that are requed because the target is
+ * busy with another command. We also use bits 6 & 7 to indicate whether
+ * or not to initiate SDTR or WDTR repectively when starting this command.
+ */
+#define SCBARRAY 0x0a0
+#define SCB_CONTROL 0x0a0
+#define NEEDWDTR 0x80
+#define DISCENB 0x40
+#define TAG_ENB 0x20
+#define NEEDSDTR 0x10
+#define DISCONNECTED 0x04
+#define SCB_TAG_TYPE 0x03
+#define SCB_TCL 0x0a1
+#define SCB_TARGET_STATUS 0x0a2
+#define SCB_SGCOUNT 0x0a3
+#define SCB_SGPTR 0x0a4
+#define SCB_SGPTR0 0x0a4
+#define SCB_SGPTR1 0x0a5
+#define SCB_SGPTR2 0x0a6
+#define SCB_SGPTR3 0x0a7
+#define SCB_RESID_SGCNT 0x0a8
+#define SCB_RESID_DCNT 0x0a9
+#define SCB_RESID_DCNT0 0x0a9
+#define SCB_RESID_DCNT1 0x0aa
+#define SCB_RESID_DCNT2 0x0ab
+#define SCB_DATAPTR 0x0ac
+#define SCB_DATAPTR0 0x0ac
+#define SCB_DATAPTR1 0x0ad
+#define SCB_DATAPTR2 0x0ae
+#define SCB_DATAPTR3 0x0af
+#define SCB_DATACNT 0x0b0
+#define SCB_DATACNT0 0x0b0
+#define SCB_DATACNT1 0x0b1
+#define SCB_DATACNT2 0x0b2
+/* UNUSED - QUAD PADDING 0x0b3 */
+#define SCB_CMDPTR 0x0b4
+#define SCB_CMDPTR0 0x0b4
+#define SCB_CMDPTR1 0x0b5
+#define SCB_CMDPTR2 0x0b6
+#define SCB_CMDPTR3 0x0b7
+#define SCB_CMDLEN 0x0b8
+#define SCB_NEXT_WAITING 0x0b9
+
+#ifdef linux
+#define SG_SIZEOF 0x0c /* sizeof(struct scatterlist) */
+#else
+#define SG_SIZEOF 0x08 /* sizeof(struct ahc_dma) */
+#endif
+
+/* --------------------- AHA-2840-only definitions -------------------- */
+
+#define SEECTL_2840 0x0c0
+/* UNUSED 0xf8 */
+#define CS_2840 0x04
+#define CK_2840 0x02
+#define DO_2840 0x01
+
+#define STATUS_2840 0x0c1
+#define EEPROM_TF 0x80
+#define BIOS_SEL 0x60
+#define ADSEL 0x1e
+#define DI_2840 0x01
+
+/* --------------------- AIC-7870-only definitions -------------------- */
+
+#define DSPCISTATUS 0x086
+
+/*
+ * Serial EEPROM Control (p. 4-92 in 7870 Databook)
+ * Controls the reading and writing of an external serial 1-bit
+ * EEPROM Device. In order to access the serial EEPROM, you must
+ * first set the SEEMS bit that generates a request to the memory
+ * port for access to the serial EEPROM device. When the memory
+ * port is not busy servicing another request, it reconfigures
+ * to allow access to the serial EEPROM. When this happens, SEERDY
+ * gets set high to verify that the memory port access has been
+ * granted.
+ *
+ * After successful arbitration for the memory port, the SEECS bit of
+ * the SEECTL register is connected to the chip select. The SEECK,
+ * SEEDO, and SEEDI are connected to the clock, data out, and data in
+ * lines respectively. The SEERDY bit of SEECTL is useful in that it
+ * gives us an 800 nsec timer. After a write to the SEECTL register,
+ * the SEERDY goes high 800 nsec later. The one exception to this is
+ * when we first request access to the memory port. The SEERDY goes
+ * high to signify that access has been granted and, for this case, has
+ * no implied timing.
+ *
+ * See 93cx6.c for detailed information on the protocol necessary to
+ * read the serial EEPROM.
+ */
+#define SEECTL 0x01e
+#define EXTARBACK 0x80
+#define EXTARBREQ 0x40
+#define SEEMS 0x20
+#define SEERDY 0x10
+#define SEECS 0x08
+#define SEECK 0x04
+#define SEEDO 0x02
+#define SEEDI 0x01
+
+/* ---------------------- Scratch RAM Offsets ------------------------- */
+/* These offsets are either to values that are initialized by the board's
+ * BIOS or are specified by the sequencer code.
+ *
+ * The host adapter card (at least the BIOS) uses 20-2f for SCSI
+ * device information, 32-33 and 5a-5f as well. As it turns out, the
+ * BIOS trashes 20-2f, writing the synchronous negotiation results
+ * on top of the BIOS values, so we re-use those for our per-target
+ * scratchspace (actually a value that can be copied directly into
+ * SCSIRATE). The kernel driver will enable synchronous negotiation
+ * for all targets that have a value other than 0 in the lower four
+ * bits of the target scratch space. This should work regardless of
+ * whether the bios has been installed.
+ */
+
+/*
+ * 1 byte per target starting at this address for configuration values
+ */
+#define TARG_SCRATCH 0x020
+
+/*
+ * The sequencer will stick the frist byte of any rejected message here so
+ * we can see what is getting thrown away.
+ */
+#define REJBYTE 0x031
+
+/*
+ * Bit vector of targets that have disconnection disabled.
+ */
+#define DISC_DSB 0x032
+#define DISC_DSB_A 0x032
+#define DISC_DSB_B 0x033
+
+/*
+ * Length of pending message
+ */
+#define MSG_LEN 0x034
+
+#define MSG0 0x035
+#define COMP_MSG0 0xcb /* 2's complement of MSG0 */
+#define MSG1 0x036
+#define MSG2 0x037
+#define MSG3 0x038
+#define MSG4 0x039
+#define MSG5 0x03a
+
+/*
+ * These are offsets into the card's scratch ram. Some of the values are
+ * specified in the AHA2742 technical reference manual and are initialized
+ * by the BIOS at boot time.
+ */
+#define LASTPHASE 0x049
+#define ARG_1 0x04a
+#define RETURN_1 0x04a
+#define SEND_SENSE 0x80
+#define SEND_WDTR 0x80
+#define SEND_SDTR 0x80
+#define SEND_REJ 0x40
+
+#define SIGSTATE 0x04b
+
+#define DMAPARAMS 0x04c /* Parameters for DMA Logic */
+
+#define SG_COUNT 0x04d
+#define SG_NEXT 0x04e /* working value of SG pointer */
+#define SG_NEXT0 0x04e
+#define SG_NEXT1 0x04f
+#define SG_NEXT2 0x050
+#define SG_NEXT3 0x051
+
+#define SCBCOUNT 0x052 /*
+ * Number of SCBs supported by
+ * this card.
+ */
+#define FLAGS 0x053
+#define SINGLE_BUS 0x00
+#define TWIN_BUS 0x01
+#define WIDE_BUS 0x02
+#define DPHASE 0x04
+#define MAXOFFSET 0x08
+#define IDENTIFY_SEEN 0x40
+#define RESELECTED 0x80
+
+#define ACTIVE_A 0x054
+#define ACTIVE_B 0x055
+#define SAVED_TCL 0x056 /*
+ * Temporary storage for the
+ * target/channel/lun of a
+ * reconnecting target
+ */
+#define WAITING_SCBH 0x057 /*
+ * head of list of SCBs awaiting
+ * selection
+ */
+#define WAITING_SCBT 0x058 /*
+ * tail of list of SCBs awaiting
+ * selection
+ */
+#define COMP_SCBCOUNT 0x059
+#define SCB_LIST_NULL 0xff
+
+#define SCSICONF 0x05a
+#define HOSTCONF 0x05d
+
+#define HA_274_BIOSCTRL 0x05f
+#define BIOSMODE 0x30
+#define BIOSDISABLED 0x30
+
+/* Message codes */
+#define MSG_EXTENDED 0x01
+#define MSG_SDTR 0x01
+#define MSG_WDTR 0x03
+#define MSG_SDPTRS 0x02
+#define MSG_RDPTRS 0x03
+#define MSG_DISCONNECT 0x04
+#define MSG_INITIATOR_DET_ERROR 0x05
+#define MSG_ABORT 0x06
+#define MSG_REJECT 0x07
+#define MSG_NOP 0x08
+#define MSG_MSG_PARITY_ERROR 0x09
+#define MSG_BUS_DEVICE_RESET 0x0c
+#define MSG_SIMPLE_TAG 0x20
+#define MSG_IDENTIFY 0x80
+
+/* WDTR Message values */
+#define BUS_8_BIT 0x00
+#define BUS_16_BIT 0x01
+#define BUS_32_BIT 0x02
+
+#define MAX_OFFSET_8BIT 0x0f
+#define MAX_OFFSET_16BIT 0x08
+
diff --git a/i386/i386at/gpl/linux/scsi/aic7xxx_seq.h b/i386/i386at/gpl/linux/scsi/aic7xxx_seq.h
new file mode 100644
index 00000000..d08f7d81
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/aic7xxx_seq.h
@@ -0,0 +1,374 @@
+#define AIC7XXX_SEQ_VER "$Id: aic7xxx_seq.h,v 1.1.1.1 1997/02/25 21:27:47 thomas Exp $"
+ 0x10, 0x6a, 0x00, 0x00,
+ 0x01, 0x53, 0x05, 0x1e,
+ 0x08, 0x1f, 0x1f, 0x04,
+ 0x20, 0x0b, 0x32, 0x1a,
+ 0x08, 0x1f, 0x1f, 0x04,
+ 0x20, 0x0b, 0x32, 0x1a,
+ 0xff, 0x57, 0x12, 0x18,
+ 0xff, 0x9c, 0x01, 0x1e,
+ 0xff, 0x9b, 0x90, 0x02,
+ 0xff, 0xa1, 0x6e, 0x02,
+ 0xff, 0x6e, 0x64, 0x02,
+ 0x88, 0xa1, 0x14, 0x1e,
+ 0x00, 0x55, 0x10, 0x1a,
+ 0x20, 0xa0, 0x17, 0x1a,
+ 0x00, 0x55, 0x55, 0x00,
+ 0x00, 0x65, 0x17, 0x10,
+ 0xff, 0x90, 0x9b, 0x02,
+ 0x00, 0x65, 0x01, 0x10,
+ 0xff, 0x57, 0x90, 0x02,
+ 0x00, 0x65, 0x19, 0x10,
+ 0x00, 0x54, 0x10, 0x1a,
+ 0x20, 0xa0, 0x17, 0x1a,
+ 0x00, 0x54, 0x54, 0x00,
+ 0xff, 0x57, 0xb9, 0x02,
+ 0xff, 0x90, 0x57, 0x02,
+ 0xf7, 0x1f, 0x65, 0x02,
+ 0x08, 0xa1, 0x64, 0x02,
+ 0x00, 0x65, 0x65, 0x00,
+ 0xff, 0x65, 0x1f, 0x02,
+ 0x00, 0xa1, 0x1f, 0x17,
+ 0x58, 0x6a, 0x00, 0x00,
+ 0xff, 0xb8, 0x22, 0x1a,
+ 0xa1, 0x6a, 0x91, 0x00,
+ 0x00, 0x65, 0x30, 0x10,
+ 0x40, 0xa0, 0x64, 0x02,
+ 0x07, 0xa1, 0x35, 0x02,
+ 0x00, 0x35, 0x35, 0x00,
+ 0x80, 0x35, 0x35, 0x00,
+ 0x01, 0x6a, 0x34, 0x00,
+ 0xb0, 0xa0, 0x30, 0x1e,
+ 0x36, 0x6a, 0x66, 0x00,
+ 0x20, 0xa0, 0x2e, 0x1e,
+ 0x23, 0xa0, 0x64, 0x02,
+ 0xff, 0x64, 0x6d, 0x02,
+ 0xff, 0x90, 0x6d, 0x02,
+ 0xcb, 0x66, 0x34, 0x06,
+ 0x90, 0xa0, 0x30, 0x1e,
+ 0x00, 0x66, 0x51, 0x17,
+ 0x40, 0x0b, 0x37, 0x1a,
+ 0x20, 0x0b, 0x30, 0x1e,
+ 0xff, 0x6a, 0x34, 0x02,
+ 0x00, 0x19, 0x1f, 0x17,
+ 0x03, 0x53, 0x53, 0x02,
+ 0x80, 0x53, 0x53, 0x00,
+ 0x00, 0x65, 0x39, 0x10,
+ 0x03, 0x53, 0x53, 0x02,
+ 0xff, 0xb9, 0x57, 0x02,
+ 0x02, 0x01, 0x01, 0x00,
+ 0x00, 0x65, 0x4d, 0x17,
+ 0xff, 0x6c, 0x04, 0x02,
+ 0x02, 0x6a, 0x00, 0x00,
+ 0x08, 0x6a, 0x0c, 0x00,
+ 0x60, 0x6a, 0x0b, 0x00,
+ 0x08, 0x0c, 0x04, 0x1b,
+ 0x01, 0x0c, 0x3f, 0x1e,
+ 0x04, 0x0c, 0x44, 0x1e,
+ 0x04, 0x0c, 0x0c, 0x00,
+ 0xe1, 0x6a, 0x91, 0x00,
+ 0xe0, 0x03, 0x64, 0x02,
+ 0xff, 0x64, 0x49, 0x02,
+ 0xff, 0x64, 0x03, 0x02,
+ 0x00, 0x6a, 0x4e, 0x1c,
+ 0x40, 0x64, 0x54, 0x1c,
+ 0x80, 0x64, 0x81, 0x1c,
+ 0xa0, 0x64, 0x90, 0x1c,
+ 0xc0, 0x64, 0x8e, 0x1c,
+ 0xe0, 0x64, 0xa4, 0x1c,
+ 0x01, 0x6a, 0x91, 0x00,
+ 0x7d, 0x6a, 0x4c, 0x00,
+ 0x00, 0x65, 0x55, 0x10,
+ 0xff, 0xa9, 0x08, 0x02,
+ 0xff, 0xaa, 0x09, 0x02,
+ 0xff, 0xab, 0x0a, 0x02,
+ 0x00, 0x65, 0x59, 0x10,
+ 0x79, 0x6a, 0x4c, 0x00,
+ 0x00, 0x65, 0x23, 0x17,
+ 0x04, 0x53, 0x50, 0x1a,
+ 0x00, 0x65, 0x31, 0x17,
+ 0x04, 0x53, 0x53, 0x00,
+ 0x01, 0x4d, 0x5b, 0x18,
+ 0xbf, 0x4c, 0x4c, 0x02,
+ 0x00, 0x4c, 0x17, 0x17,
+ 0x04, 0x0b, 0x7c, 0x1e,
+ 0xff, 0x4d, 0x4d, 0x06,
+ 0xff, 0x4d, 0x7c, 0x1e,
+ 0xff, 0x6a, 0x64, 0x02,
+ 0x0c, 0x4e, 0x4e, 0x06,
+ 0x00, 0x4f, 0x4f, 0x08,
+ 0xff, 0x6a, 0x8e, 0x02,
+ 0xff, 0x6a, 0x8d, 0x02,
+ 0x0c, 0x6a, 0x8c, 0x00,
+ 0xff, 0x4e, 0x88, 0x02,
+ 0xff, 0x4f, 0x89, 0x02,
+ 0xff, 0x50, 0x8a, 0x02,
+ 0xff, 0x51, 0x8b, 0x02,
+ 0x0d, 0x93, 0x93, 0x00,
+ 0x08, 0x94, 0x6a, 0x1e,
+ 0x40, 0x93, 0x93, 0x02,
+ 0x08, 0x93, 0x6c, 0x1a,
+ 0xff, 0x99, 0x88, 0x02,
+ 0xff, 0x99, 0x89, 0x02,
+ 0xff, 0x99, 0x8a, 0x02,
+ 0xff, 0x99, 0x8b, 0x02,
+ 0xff, 0x99, 0x6a, 0x02,
+ 0xff, 0x99, 0x6a, 0x02,
+ 0xff, 0x99, 0x6a, 0x02,
+ 0xff, 0x99, 0x6a, 0x02,
+ 0xff, 0x99, 0x8c, 0x02,
+ 0xff, 0x99, 0x8d, 0x02,
+ 0xff, 0x99, 0x8e, 0x02,
+ 0xff, 0x8c, 0x08, 0x02,
+ 0xff, 0x8d, 0x09, 0x02,
+ 0xff, 0x8e, 0x0a, 0x02,
+ 0x10, 0x0c, 0x59, 0x1e,
+ 0xff, 0x08, 0xa9, 0x02,
+ 0xff, 0x09, 0xaa, 0x02,
+ 0xff, 0x0a, 0xab, 0x02,
+ 0xff, 0x4d, 0xa8, 0x02,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0x00, 0x65, 0x23, 0x17,
+ 0xff, 0xb4, 0x88, 0x02,
+ 0xff, 0xb5, 0x89, 0x02,
+ 0xff, 0xb6, 0x8a, 0x02,
+ 0xff, 0xb7, 0x8b, 0x02,
+ 0xff, 0xb8, 0x8c, 0x02,
+ 0xff, 0x6a, 0x8d, 0x02,
+ 0xff, 0x6a, 0x8e, 0x02,
+ 0xff, 0x8c, 0x08, 0x02,
+ 0xff, 0x8d, 0x09, 0x02,
+ 0xff, 0x8e, 0x0a, 0x02,
+ 0x3d, 0x6a, 0x17, 0x17,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0xa2, 0x6a, 0x12, 0x17,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0xff, 0x34, 0x92, 0x1a,
+ 0x08, 0x6a, 0x07, 0x17,
+ 0x35, 0x6a, 0x65, 0x00,
+ 0xff, 0x34, 0x66, 0x02,
+ 0x10, 0x0c, 0xa1, 0x1a,
+ 0x02, 0x0b, 0x94, 0x1e,
+ 0x01, 0x66, 0x98, 0x18,
+ 0x40, 0x6a, 0x0c, 0x00,
+ 0xff, 0x66, 0x66, 0x06,
+ 0x02, 0x0b, 0x0b, 0x00,
+ 0xff, 0x6c, 0x06, 0x02,
+ 0xff, 0x66, 0x94, 0x1a,
+ 0x08, 0x0c, 0xa2, 0x1a,
+ 0x01, 0x0c, 0x9c, 0x1e,
+ 0x10, 0x0c, 0xa2, 0x1a,
+ 0x10, 0x03, 0x03, 0x00,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0x40, 0x6a, 0x0c, 0x00,
+ 0xff, 0x6a, 0x34, 0x02,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0x64, 0x6a, 0x12, 0x17,
+ 0xff, 0x64, 0x31, 0x02,
+ 0x80, 0x64, 0xe4, 0x1a,
+ 0x04, 0x64, 0xde, 0x1c,
+ 0x02, 0x64, 0xe0, 0x1c,
+ 0x00, 0x6a, 0xb2, 0x1c,
+ 0x03, 0x64, 0xe2, 0x1c,
+ 0x01, 0x64, 0xc5, 0x1c,
+ 0x07, 0x64, 0x02, 0x1d,
+ 0x10, 0x03, 0x03, 0x00,
+ 0x11, 0x6a, 0x91, 0x00,
+ 0x07, 0x6a, 0x07, 0x17,
+ 0x00, 0x65, 0x14, 0x17,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0xff, 0xa8, 0xb4, 0x1e,
+ 0x81, 0x6a, 0x91, 0x00,
+ 0xff, 0xa2, 0xb8, 0x1e,
+ 0x71, 0x6a, 0x91, 0x00,
+ 0x80, 0x4a, 0xb8, 0x18,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x20, 0xa0, 0xbf, 0x1a,
+ 0xff, 0xa1, 0x6e, 0x02,
+ 0xff, 0x6e, 0x64, 0x02,
+ 0x88, 0xa1, 0xbe, 0x1e,
+ 0x00, 0x55, 0x55, 0x04,
+ 0x00, 0x65, 0xbf, 0x10,
+ 0x00, 0x54, 0x54, 0x04,
+ 0xff, 0xb8, 0xc2, 0x1a,
+ 0xb1, 0x6a, 0x91, 0x00,
+ 0x00, 0x65, 0x00, 0x10,
+ 0xff, 0x90, 0x9d, 0x02,
+ 0x02, 0x6a, 0x91, 0x00,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x4a, 0x6a, 0x0e, 0x17,
+ 0x64, 0x6a, 0x0e, 0x17,
+ 0x01, 0x64, 0xd4, 0x1c,
+ 0x03, 0x64, 0xca, 0x1c,
+ 0x00, 0x65, 0xad, 0x10,
+ 0x02, 0x4a, 0xad, 0x18,
+ 0x4a, 0x6a, 0x0e, 0x17,
+ 0x51, 0x6a, 0x91, 0x00,
+ 0xff, 0x4a, 0xb0, 0x1e,
+ 0x40, 0x4a, 0xad, 0x1c,
+ 0x7f, 0x4a, 0x4a, 0x02,
+ 0x35, 0x6a, 0x66, 0x00,
+ 0x35, 0x6a, 0x62, 0x17,
+ 0x10, 0x03, 0x03, 0x00,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x03, 0x4a, 0xad, 0x18,
+ 0x4a, 0x6a, 0x0e, 0x17,
+ 0x64, 0x6a, 0x0e, 0x17,
+ 0x41, 0x6a, 0x91, 0x00,
+ 0xff, 0x4a, 0xb0, 0x1e,
+ 0x40, 0x4a, 0xad, 0x1c,
+ 0x35, 0x6a, 0x66, 0x00,
+ 0x35, 0x6a, 0x53, 0x17,
+ 0x10, 0x03, 0x03, 0x00,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x04, 0xa0, 0xa0, 0x00,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x00, 0x65, 0x40, 0x17,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0xfb, 0x53, 0x53, 0x02,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x78, 0x64, 0xad, 0x1a,
+ 0x07, 0x64, 0x64, 0x02,
+ 0x00, 0x19, 0x56, 0x00,
+ 0xf7, 0x56, 0x56, 0x02,
+ 0x08, 0x1f, 0x64, 0x02,
+ 0x00, 0x56, 0x56, 0x00,
+ 0x00, 0x65, 0x14, 0x17,
+ 0x08, 0x0c, 0xf0, 0x1a,
+ 0x01, 0x0c, 0xeb, 0x1e,
+ 0x10, 0x0c, 0xf0, 0x1a,
+ 0x64, 0x6a, 0x12, 0x17,
+ 0x20, 0x64, 0xf4, 0x1c,
+ 0x00, 0x6a, 0x26, 0x17,
+ 0xfb, 0xa0, 0xa0, 0x02,
+ 0x40, 0x53, 0x53, 0x00,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0x4a, 0x6a, 0x0e, 0x17,
+ 0xff, 0x59, 0x64, 0x02,
+ 0x00, 0x4a, 0x65, 0x06,
+ 0x00, 0x65, 0xfe, 0x12,
+ 0xff, 0x4a, 0x90, 0x02,
+ 0xff, 0x56, 0x64, 0x02,
+ 0x00, 0xa1, 0xfe, 0x18,
+ 0x20, 0xa0, 0xfe, 0x1e,
+ 0x00, 0x65, 0x14, 0x17,
+ 0x00, 0x65, 0xf1, 0x10,
+ 0x10, 0x03, 0x03, 0x00,
+ 0x91, 0x6a, 0x91, 0x00,
+ 0x0d, 0x6a, 0x07, 0x17,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x61, 0x6a, 0x91, 0x00,
+ 0x00, 0x65, 0xb0, 0x10,
+ 0x40, 0x6a, 0x0c, 0x00,
+ 0xff, 0xb8, 0xb8, 0x1e,
+ 0x00, 0x65, 0x00, 0x10,
+ 0x50, 0x6a, 0x60, 0x00,
+ 0xff, 0x34, 0x0b, 0x1f,
+ 0x10, 0x6a, 0x60, 0x00,
+ 0xc1, 0x6a, 0x91, 0x00,
+ 0x01, 0x6a, 0x34, 0x00,
+ 0xff, 0x65, 0x35, 0x02,
+ 0x10, 0x6a, 0x60, 0x01,
+ 0x02, 0x0b, 0x0b, 0x00,
+ 0xff, 0x06, 0x6a, 0x02,
+ 0x10, 0x0c, 0x15, 0x1b,
+ 0x02, 0x0b, 0x10, 0x1f,
+ 0xff, 0x65, 0x66, 0x02,
+ 0xff, 0x12, 0x6d, 0x03,
+ 0xff, 0x06, 0x6a, 0x03,
+ 0xd1, 0x6a, 0x91, 0x00,
+ 0x00, 0x65, 0x3f, 0x10,
+ 0xff, 0x65, 0x93, 0x02,
+ 0x01, 0x0b, 0x1a, 0x1b,
+ 0x10, 0x0c, 0x18, 0x1f,
+ 0x04, 0x65, 0x1c, 0x1b,
+ 0x01, 0x94, 0x1b, 0x1f,
+ 0x40, 0x93, 0x93, 0x02,
+ 0x38, 0x93, 0x1d, 0x1b,
+ 0xff, 0x6a, 0x6a, 0x03,
+ 0xf0, 0x65, 0x65, 0x02,
+ 0x0f, 0x05, 0x64, 0x02,
+ 0x00, 0x65, 0x65, 0x00,
+ 0xff, 0x65, 0x05, 0x03,
+ 0x80, 0x53, 0x74, 0x1f,
+ 0x40, 0x53, 0x74, 0x1b,
+ 0x21, 0x6a, 0x91, 0x01,
+ 0xff, 0x56, 0x64, 0x02,
+ 0xff, 0x65, 0x90, 0x02,
+ 0x00, 0xa1, 0x2b, 0x19,
+ 0x04, 0xa0, 0x2b, 0x1f,
+ 0xff, 0x6a, 0x6a, 0x03,
+ 0x01, 0x65, 0x65, 0x06,
+ 0xff, 0x52, 0x64, 0x02,
+ 0x00, 0x65, 0x26, 0x19,
+ 0x31, 0x6a, 0x91, 0x00,
+ 0x06, 0x6a, 0x07, 0x17,
+ 0x10, 0x03, 0x03, 0x01,
+ 0xff, 0xac, 0x88, 0x02,
+ 0xff, 0xad, 0x89, 0x02,
+ 0xff, 0xae, 0x8a, 0x02,
+ 0xff, 0xaf, 0x8b, 0x02,
+ 0xff, 0xb0, 0x8c, 0x02,
+ 0xff, 0xb1, 0x8d, 0x02,
+ 0xff, 0xb2, 0x8e, 0x02,
+ 0xff, 0x8c, 0x08, 0x02,
+ 0xff, 0x8d, 0x09, 0x02,
+ 0xff, 0x8e, 0x0a, 0x02,
+ 0xff, 0xa3, 0x4d, 0x02,
+ 0xff, 0xa4, 0x4e, 0x02,
+ 0xff, 0xa5, 0x4f, 0x02,
+ 0xff, 0xa6, 0x50, 0x02,
+ 0xff, 0xa7, 0x51, 0x03,
+ 0x04, 0x53, 0x74, 0x1f,
+ 0xff, 0x4d, 0xa3, 0x02,
+ 0xff, 0x4e, 0xa4, 0x02,
+ 0xff, 0x4f, 0xa5, 0x02,
+ 0xff, 0x50, 0xa6, 0x02,
+ 0xff, 0x51, 0xa7, 0x02,
+ 0xff, 0x14, 0xac, 0x02,
+ 0xff, 0x15, 0xad, 0x02,
+ 0xff, 0x16, 0xae, 0x02,
+ 0xff, 0x17, 0xaf, 0x02,
+ 0xff, 0xa9, 0xb0, 0x02,
+ 0xff, 0xaa, 0xb1, 0x02,
+ 0xff, 0xab, 0xb2, 0x03,
+ 0x4c, 0x05, 0x64, 0x0a,
+ 0x08, 0x1f, 0x50, 0x1f,
+ 0x08, 0x64, 0x64, 0x00,
+ 0x20, 0x64, 0x65, 0x07,
+ 0x80, 0xa0, 0x61, 0x1b,
+ 0x08, 0x53, 0x53, 0x00,
+ 0x01, 0x6a, 0x6d, 0x00,
+ 0x03, 0x6a, 0x6d, 0x00,
+ 0x01, 0x6a, 0x6d, 0x00,
+ 0x00, 0x65, 0x67, 0x17,
+ 0xff, 0x4a, 0x6d, 0x02,
+ 0x08, 0x53, 0x5b, 0x1b,
+ 0x0f, 0x6c, 0x6d, 0x02,
+ 0xcb, 0x66, 0x34, 0x07,
+ 0x08, 0x53, 0x53, 0x04,
+ 0x80, 0x04, 0x5f, 0x1b,
+ 0x0f, 0x6a, 0x6d, 0x00,
+ 0x00, 0x65, 0x5a, 0x11,
+ 0x08, 0x6a, 0x6d, 0x00,
+ 0x00, 0x65, 0x5a, 0x11,
+ 0x01, 0x6a, 0x4a, 0x00,
+ 0x01, 0x6a, 0x6d, 0x00,
+ 0x02, 0x6a, 0x6d, 0x00,
+ 0x03, 0x6a, 0x6d, 0x00,
+ 0xff, 0x4a, 0x6d, 0x02,
+ 0xcb, 0x66, 0x34, 0x07,
+ 0x00, 0x65, 0x4d, 0x17,
+ 0x4c, 0x6c, 0x64, 0x0a,
+ 0xff, 0x65, 0x65, 0x06,
+ 0x07, 0x64, 0x64, 0x02,
+ 0xff, 0x6a, 0x4a, 0x02,
+ 0x0f, 0x64, 0x70, 0x1f,
+ 0x19, 0x4a, 0x4a, 0x06,
+ 0xff, 0x64, 0x64, 0x06,
+ 0x00, 0x65, 0x6c, 0x11,
+ 0x2e, 0x4a, 0x4a, 0x0a,
+ 0x19, 0x4a, 0x4a, 0x06,
+ 0x20, 0x01, 0x74, 0x1f,
+ 0x1f, 0x4a, 0x4a, 0x0a,
+ 0xff, 0x6a, 0x6a, 0x03,
diff --git a/i386/i386at/gpl/linux/scsi/constants.c b/i386/i386at/gpl/linux/scsi/constants.c
new file mode 100644
index 00000000..07f071b2
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/constants.c
@@ -0,0 +1,649 @@
+/*
+ * ASCII values for a number of symbolic constants, printing functions,
+ * etc.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#define CONST_COMMAND 0x01
+#define CONST_STATUS 0x02
+#define CONST_SENSE 0x04
+#define CONST_XSENSE 0x08
+#define CONST_CMND 0x10
+#define CONST_MSG 0x20
+#define CONST_HOST 0x40
+#define CONST_DRIVER 0x80
+
+static const char unknown[] = "UNKNOWN";
+
+#ifdef CONFIG_SCSI_CONSTANTS
+#ifdef CONSTANTS
+#undef CONSTANTS
+#endif
+#define CONSTANTS (CONST_COMMAND | CONST_STATUS | CONST_SENSE | CONST_XSENSE \
+ | CONST_CMND | CONST_MSG | CONST_HOST | CONST_DRIVER)
+#endif
+
+#if (CONSTANTS & CONST_COMMAND)
+static const char * group_0_commands[] = {
+/* 00-03 */ "Test Unit Ready", "Rezero Unit", unknown, "Request Sense",
+/* 04-07 */ "Format Unit", "Read Block Limits", unknown, "Reasssign Blocks",
+/* 08-0d */ "Read (6)", unknown, "Write (6)", "Seek (6)", unknown, unknown,
+/* 0e-12 */ unknown, "Read Reverse", "Write Filemarks", "Space", "Inquiry",
+/* 13-16 */ unknown, "Recover Buffered Data", "Mode Select", "Reserve",
+/* 17-1b */ "Release", "Copy", "Erase", "Mode Sense", "Start/Stop Unit",
+/* 1c-1d */ "Receive Diagnostic", "Send Diagnostic",
+/* 1e-1f */ "Prevent/Allow Medium Removal", unknown,
+};
+
+
+static const char *group_1_commands[] = {
+/* 20-22 */ unknown, unknown, unknown,
+/* 23-28 */ unknown, unknown, "Read Capacity", unknown, unknown, "Read (10)",
+/* 29-2d */ unknown, "Write (10)", "Seek (10)", unknown, unknown,
+/* 2e-31 */ "Write Verify","Verify", "Search High", "Search Equal",
+/* 32-34 */ "Search Low", "Set Limits", "Prefetch or Read Position",
+/* 35-37 */ "Synchronize Cache","Lock/Unlock Cache", "Read Defect Data",
+/* 38-3c */ "Medium Scan", "Compare","Copy Verify", "Write Buffer", "Read Buffer",
+/* 3d-3f */ "Update Block", "Read Long", "Write Long",
+};
+
+
+static const char *group_2_commands[] = {
+/* 40-41 */ "Change Definition", "Write Same",
+/* 42-48 */ unknown, unknown, unknown, unknown, unknown, unknown, unknown,
+/* 49-4f */ unknown, unknown, unknown, "Log Select", "Log Sense", unknown, unknown,
+/* 50-55 */ unknown, unknown, unknown, unknown, unknown, "Mode Select (10)",
+/* 56-5b */ unknown, unknown, unknown, unknown, "Mode Sense (10)", unknown,
+/* 5c-5f */ unknown, unknown, unknown,
+};
+
+
+
+#define group(opcode) (((opcode) >> 5) & 7)
+
+#define RESERVED_GROUP 0
+#define VENDOR_GROUP 1
+#define NOTEXT_GROUP 2
+
+static const char **commands[] = {
+ group_0_commands, group_1_commands, group_2_commands,
+ (const char **) RESERVED_GROUP, (const char **) RESERVED_GROUP,
+ (const char **) NOTEXT_GROUP, (const char **) VENDOR_GROUP,
+ (const char **) VENDOR_GROUP
+};
+
+static const char reserved[] = "RESERVED";
+static const char vendor[] = "VENDOR SPECIFIC";
+
+static void print_opcode(int opcode) {
+ const char **table = commands[ group(opcode) ];
+ switch ((unsigned long) table) {
+ case RESERVED_GROUP:
+ printk("%s(0x%02x) ", reserved, opcode);
+ break;
+ case NOTEXT_GROUP:
+ printk("%s(0x%02x) ", unknown, opcode);
+ break;
+ case VENDOR_GROUP:
+ printk("%s(0x%02x) ", vendor, opcode);
+ break;
+ default:
+ printk("%s ",table[opcode & 0x1f]);
+ }
+}
+#else /* CONST & CONST_COMMAND */
+static void print_opcode(int opcode) {
+ printk("0x%02x ", opcode);
+}
+#endif
+
+void print_command (unsigned char *command) {
+ int i,s;
+ print_opcode(command[0]);
+ for ( i = 1, s = COMMAND_SIZE(command[0]); i < s; ++i)
+ printk("%02x ", command[i]);
+ printk("\n");
+}
+
+#if (CONSTANTS & CONST_STATUS)
+static const char * statuses[] = {
+/* 0-4 */ "Good", "Check Condition", "Condition Good", unknown, "Busy",
+/* 5-9 */ unknown, unknown, unknown, "Intermediate Good", unknown,
+/* a-d */ "Intermediate Good", unknown, "Reservation Conflict", unknown,
+/* e-f */ unknown, unknown,
+};
+#endif
+
+void print_status (int status) {
+ status = (status >> 1) & 0xf;
+#if (CONSTANTS & CONST_STATUS)
+ printk("%s ",statuses[status]);
+#else
+ printk("0x%0x ", status);
+#endif
+}
+
+#if (CONSTANTS & CONST_XSENSE)
+#define D 0x001 /* DIRECT ACCESS DEVICE (disk) */
+#define T 0x002 /* SEQUENTIAL ACCESS DEVICE (tape) */
+#define L 0x004 /* PRINTER DEVICE */
+#define P 0x008 /* PROCESSOR DEVICE */
+#define W 0x010 /* WRITE ONCE READ MULTIPLE DEVICE */
+#define R 0x020 /* READ ONLY (CD-ROM) DEVICE */
+#define S 0x040 /* SCANNER DEVICE */
+#define O 0x080 /* OPTICAL MEMORY DEVICE */
+#define M 0x100 /* MEDIA CHANGER DEVICE */
+#define C 0x200 /* COMMUNICATION DEVICE */
+
+struct error_info{
+ unsigned char code1, code2;
+ unsigned short int devices;
+ const char * text;
+};
+
+struct error_info2{
+ unsigned char code1, code2_min, code2_max;
+ unsigned short int devices;
+ const char * text;
+};
+
+static struct error_info2 additional2[] =
+{
+ {0x40,0x00,0x7f,D,"Ram failure (%x)"},
+ {0x40,0x80,0xff,D|T|L|P|W|R|S|O|M|C,"Diagnostic failure on component (%x)"},
+ {0x41,0x00,0xff,D,"Data path failure (%x)"},
+ {0x42,0x00,0xff,D,"Power-on or self-test failure (%x)"},
+ {0, 0, 0, 0, NULL}
+};
+
+static struct error_info additional[] =
+{
+ {0x00,0x01,T,"Filemark detected"},
+ {0x00,0x02,T|S,"End-of-partition/medium detected"},
+ {0x00,0x03,T,"Setmark detected"},
+ {0x00,0x04,T|S,"Beginning-of-partition/medium detected"},
+ {0x00,0x05,T|S,"End-of-data detected"},
+ {0x00,0x06,D|T|L|P|W|R|S|O|M|C,"I/O process terminated"},
+ {0x00,0x11,R,"Audio play operation in progress"},
+ {0x00,0x12,R,"Audio play operation paused"},
+ {0x00,0x13,R,"Audio play operation successfully completed"},
+ {0x00,0x14,R,"Audio play operation stopped due to error"},
+ {0x00,0x15,R,"No current audio status to return"},
+ {0x01,0x00,D|W|O,"No index/sector signal"},
+ {0x02,0x00,D|W|R|O|M,"No seek complete"},
+ {0x03,0x00,D|T|L|W|S|O,"Peripheral device write fault"},
+ {0x03,0x01,T,"No write current"},
+ {0x03,0x02,T,"Excessive write errors"},
+ {0x04,0x00,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, cause not reportable"},
+ {0x04,0x01,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit is in process of becoming ready"},
+ {0x04,0x02,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, initializing command required"},
+ {0x04,0x03,D|T|L|P|W|R|S|O|M|C,
+ "Logical unit not ready, manual intervention required"},
+ {0x04,0x04,D|T|L|O,"Logical unit not ready, format in progress"},
+ {0x05,0x00,D|T|L|W|R|S|O|M|C,"Logical unit does not respond to selection"},
+ {0x06,0x00,D|W|R|O|M,"No reference position found"},
+ {0x07,0x00,D|T|L|W|R|S|O|M,"Multiple peripheral devices selected"},
+ {0x08,0x00,D|T|L|W|R|S|O|M|C,"Logical unit communication failure"},
+ {0x08,0x01,D|T|L|W|R|S|O|M|C,"Logical unit communication time-out"},
+ {0x08,0x02,D|T|L|W|R|S|O|M|C,"Logical unit communication parity error"},
+ {0x09,0x00,D|T|W|R|O,"Track following error"},
+ {0x09,0x01,W|R|O,"Tracking servo failure"},
+ {0x09,0x02,W|R|O,"Focus servo failure"},
+ {0x09,0x03,W|R|O,"Spindle servo failure"},
+ {0x0A,0x00,D|T|L|P|W|R|S|O|M|C,"Error log overflow"},
+ {0x0C,0x00,T|S,"Write error"},
+ {0x0C,0x01,D|W|O,"Write error recovered with auto reallocation"},
+ {0x0C,0x02,D|W|O,"Write error - auto reallocation failed"},
+ {0x10,0x00,D|W|O,"Id crc or ecc error"},
+ {0x11,0x00,D|T|W|R|S|O,"Unrecovered read error"},
+ {0x11,0x01,D|T|W|S|O,"Read retries exhausted"},
+ {0x11,0x02,D|T|W|S|O,"Error too long to correct"},
+ {0x11,0x03,D|T|W|S|O,"Multiple read errors"},
+ {0x11,0x04,D|W|O,"Unrecovered read error - auto reallocate failed"},
+ {0x11,0x05,W|R|O,"L-ec uncorrectable error"},
+ {0x11,0x06,W|R|O,"Circ unrecovered error"},
+ {0x11,0x07,W|O,"Data resynchronization error"},
+ {0x11,0x08,T,"Incomplete block read"},
+ {0x11,0x09,T,"No gap found"},
+ {0x11,0x0A,D|T|O,"Miscorrected error"},
+ {0x11,0x0B,D|W|O,"Unrecovered read error - recommend reassignment"},
+ {0x11,0x0C,D|W|O,"Unrecovered read error - recommend rewrite the data"},
+ {0x12,0x00,D|W|O,"Address mark not found for id field"},
+ {0x13,0x00,D|W|O,"Address mark not found for data field"},
+ {0x14,0x00,D|T|L|W|R|S|O,"Recorded entity not found"},
+ {0x14,0x01,D|T|W|R|O,"Record not found"},
+ {0x14,0x02,T,"Filemark or setmark not found"},
+ {0x14,0x03,T,"End-of-data not found"},
+ {0x14,0x04,T,"Block sequence error"},
+ {0x15,0x00,D|T|L|W|R|S|O|M,"Random positioning error"},
+ {0x15,0x01,D|T|L|W|R|S|O|M,"Mechanical positioning error"},
+ {0x15,0x02,D|T|W|R|O,"Positioning error detected by read of medium"},
+ {0x16,0x00,D|W|O,"Data synchronization mark error"},
+ {0x17,0x00,D|T|W|R|S|O,"Recovered data with no error correction applied"},
+ {0x17,0x01,D|T|W|R|S|O,"Recovered data with retries"},
+ {0x17,0x02,D|T|W|R|O,"Recovered data with positive head offset"},
+ {0x17,0x03,D|T|W|R|O,"Recovered data with negative head offset"},
+ {0x17,0x04,W|R|O,"Recovered data with retries and/or circ applied"},
+ {0x17,0x05,D|W|R|O,"Recovered data using previous sector id"},
+ {0x17,0x06,D|W|O,"Recovered data without ecc - data auto-reallocated"},
+ {0x17,0x07,D|W|O,"Recovered data without ecc - recommend reassignment"},
+ {0x18,0x00,D|T|W|R|O,"Recovered data with error correction applied"},
+ {0x18,0x01,D|W|R|O,"Recovered data with error correction and retries applied"},
+ {0x18,0x02,D|W|R|O,"Recovered data - data auto-reallocated"},
+ {0x18,0x03,R,"Recovered data with circ"},
+ {0x18,0x04,R,"Recovered data with lec"},
+ {0x18,0x05,D|W|R|O,"Recovered data - recommend reassignment"},
+ {0x19,0x00,D|O,"Defect list error"},
+ {0x19,0x01,D|O,"Defect list not available"},
+ {0x19,0x02,D|O,"Defect list error in primary list"},
+ {0x19,0x03,D|O,"Defect list error in grown list"},
+ {0x1A,0x00,D|T|L|P|W|R|S|O|M|C,"Parameter list length error"},
+ {0x1B,0x00,D|T|L|P|W|R|S|O|M|C,"Synchronous data transfer error"},
+ {0x1C,0x00,D|O,"Defect list not found"},
+ {0x1C,0x01,D|O,"Primary defect list not found"},
+ {0x1C,0x02,D|O,"Grown defect list not found"},
+ {0x1D,0x00,D|W|O,"Miscompare during verify operation"},
+ {0x1E,0x00,D|W|O,"Recovered id with ecc correction"},
+ {0x20,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid command operation code"},
+ {0x21,0x00,D|T|W|R|O|M,"Logical block address out of range"},
+ {0x21,0x01,M,"Invalid element address"},
+ {0x22,0x00,D,"Illegal function (should use 20 00, 24 00, or 26 00)"},
+ {0x24,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in cdb"},
+ {0x25,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit not supported"},
+ {0x26,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid field in parameter list"},
+ {0x26,0x01,D|T|L|P|W|R|S|O|M|C,"Parameter not supported"},
+ {0x26,0x02,D|T|L|P|W|R|S|O|M|C,"Parameter value invalid"},
+ {0x26,0x03,D|T|L|P|W|R|S|O|M|C,"Threshold parameters not supported"},
+ {0x27,0x00,D|T|W|O,"Write protected"},
+ {0x28,0x00,D|T|L|P|W|R|S|O|M|C,"Not ready to ready transition (medium may have changed)"},
+ {0x28,0x01,M,"Import or export element accessed"},
+ {0x29,0x00,D|T|L|P|W|R|S|O|M|C,"Power on, reset, or bus device reset occurred"},
+ {0x2A,0x00,D|T|L|W|R|S|O|M|C,"Parameters changed"},
+ {0x2A,0x01,D|T|L|W|R|S|O|M|C,"Mode parameters changed"},
+ {0x2A,0x02,D|T|L|W|R|S|O|M|C,"Log parameters changed"},
+ {0x2B,0x00,D|T|L|P|W|R|S|O|C,"Copy cannot execute since host cannot disconnect"},
+ {0x2C,0x00,D|T|L|P|W|R|S|O|M|C,"Command sequence error"},
+ {0x2C,0x01,S,"Too many windows specified"},
+ {0x2C,0x02,S,"Invalid combination of windows specified"},
+ {0x2D,0x00,T,"Overwrite error on update in place"},
+ {0x2F,0x00,D|T|L|P|W|R|S|O|M|C,"Commands cleared by another initiator"},
+ {0x30,0x00,D|T|W|R|O|M,"Incompatible medium installed"},
+ {0x30,0x01,D|T|W|R|O,"Cannot read medium - unknown format"},
+ {0x30,0x02,D|T|W|R|O,"Cannot read medium - incompatible format"},
+ {0x30,0x03,D|T,"Cleaning cartridge installed"},
+ {0x31,0x00,D|T|W|O,"Medium format corrupted"},
+ {0x31,0x01,D|L|O,"Format command failed"},
+ {0x32,0x00,D|W|O,"No defect spare location available"},
+ {0x32,0x01,D|W|O,"Defect list update failure"},
+ {0x33,0x00,T,"Tape length error"},
+ {0x36,0x00,L,"Ribbon, ink, or toner failure"},
+ {0x37,0x00,D|T|L|W|R|S|O|M|C,"Rounded parameter"},
+ {0x39,0x00,D|T|L|W|R|S|O|M|C,"Saving parameters not supported"},
+ {0x3A,0x00,D|T|L|W|R|S|O|M,"Medium not present"},
+ {0x3B,0x00,T|L,"Sequential positioning error"},
+ {0x3B,0x01,T,"Tape position error at beginning-of-medium"},
+ {0x3B,0x02,T,"Tape position error at end-of-medium"},
+ {0x3B,0x03,L,"Tape or electronic vertical forms unit not ready"},
+ {0x3B,0x04,L,"Slew failure"},
+ {0x3B,0x05,L,"Paper jam"},
+ {0x3B,0x06,L,"Failed to sense top-of-form"},
+ {0x3B,0x07,L,"Failed to sense bottom-of-form"},
+ {0x3B,0x08,T,"Reposition error"},
+ {0x3B,0x09,S,"Read past end of medium"},
+ {0x3B,0x0A,S,"Read past beginning of medium"},
+ {0x3B,0x0B,S,"Position past end of medium"},
+ {0x3B,0x0C,S,"Position past beginning of medium"},
+ {0x3B,0x0D,M,"Medium destination element full"},
+ {0x3B,0x0E,M,"Medium source element empty"},
+ {0x3D,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid bits in identify message"},
+ {0x3E,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit has not self-configured yet"},
+ {0x3F,0x00,D|T|L|P|W|R|S|O|M|C,"Target operating conditions have changed"},
+ {0x3F,0x01,D|T|L|P|W|R|S|O|M|C,"Microcode has been changed"},
+ {0x3F,0x02,D|T|L|P|W|R|S|O|M|C,"Changed operating definition"},
+ {0x3F,0x03,D|T|L|P|W|R|S|O|M|C,"Inquiry data has changed"},
+ {0x43,0x00,D|T|L|P|W|R|S|O|M|C,"Message error"},
+ {0x44,0x00,D|T|L|P|W|R|S|O|M|C,"Internal target failure"},
+ {0x45,0x00,D|T|L|P|W|R|S|O|M|C,"Select or reselect failure"},
+ {0x46,0x00,D|T|L|P|W|R|S|O|M|C,"Unsuccessful soft reset"},
+ {0x47,0x00,D|T|L|P|W|R|S|O|M|C,"Scsi parity error"},
+ {0x48,0x00,D|T|L|P|W|R|S|O|M|C,"Initiator detected error message received"},
+ {0x49,0x00,D|T|L|P|W|R|S|O|M|C,"Invalid message error"},
+ {0x4A,0x00,D|T|L|P|W|R|S|O|M|C,"Command phase error"},
+ {0x4B,0x00,D|T|L|P|W|R|S|O|M|C,"Data phase error"},
+ {0x4C,0x00,D|T|L|P|W|R|S|O|M|C,"Logical unit failed self-configuration"},
+ {0x4E,0x00,D|T|L|P|W|R|S|O|M|C,"Overlapped commands attempted"},
+ {0x50,0x00,T,"Write append error"},
+ {0x50,0x01,T,"Write append position error"},
+ {0x50,0x02,T,"Position error related to timing"},
+ {0x51,0x00,T|O,"Erase failure"},
+ {0x52,0x00,T,"Cartridge fault"},
+ {0x53,0x00,D|T|L|W|R|S|O|M,"Media load or eject failed"},
+ {0x53,0x01,T,"Unload tape failure"},
+ {0x53,0x02,D|T|W|R|O|M,"Medium removal prevented"},
+ {0x54,0x00,P,"Scsi to host system interface failure"},
+ {0x55,0x00,P,"System resource failure"},
+ {0x57,0x00,R,"Unable to recover table-of-contents"},
+ {0x58,0x00,O,"Generation does not exist"},
+ {0x59,0x00,O,"Updated block read"},
+ {0x5A,0x00,D|T|L|P|W|R|S|O|M,"Operator request or state change input (unspecified)"},
+ {0x5A,0x01,D|T|W|R|O|M,"Operator medium removal request"},
+ {0x5A,0x02,D|T|W|O,"Operator selected write protect"},
+ {0x5A,0x03,D|T|W|O,"Operator selected write permit"},
+ {0x5B,0x00,D|T|L|P|W|R|S|O|M,"Log exception"},
+ {0x5B,0x01,D|T|L|P|W|R|S|O|M,"Threshold condition met"},
+ {0x5B,0x02,D|T|L|P|W|R|S|O|M,"Log counter at maximum"},
+ {0x5B,0x03,D|T|L|P|W|R|S|O|M,"Log list codes exhausted"},
+ {0x5C,0x00,D|O,"Rpl status change"},
+ {0x5C,0x01,D|O,"Spindles synchronized"},
+ {0x5C,0x02,D|O,"Spindles not synchronized"},
+ {0x60,0x00,S,"Lamp failure"},
+ {0x61,0x00,S,"Video acquisition error"},
+ {0x61,0x01,S,"Unable to acquire video"},
+ {0x61,0x02,S,"Out of focus"},
+ {0x62,0x00,S,"Scan head positioning error"},
+ {0x63,0x00,R,"End of user area encountered on this track"},
+ {0x64,0x00,R,"Illegal mode for this track"},
+ {0, 0, 0, NULL}
+};
+#endif
+
+#if (CONSTANTS & CONST_SENSE)
+static const char *snstext[] = {
+ "None","Recovered Error","Not Ready","Medium Error","Hardware Error",
+ "Illegal Request","Unit Attention","Data Protect","Blank Check",
+ "Key=9","Copy Aborted","Aborted Command","End-Of-Medium",
+ "Volume Overflow", "Miscompare", "Key=15"};
+#endif
+
+
+/* Print sense information */
+void print_sense(const char * devclass, Scsi_Cmnd * SCpnt)
+{
+ int i, s;
+ int sense_class, valid, code;
+ unsigned char * sense_buffer = SCpnt->sense_buffer;
+ const char * error = NULL;
+
+ sense_class = (sense_buffer[0] >> 4) & 0x07;
+ code = sense_buffer[0] & 0xf;
+ valid = sense_buffer[0] & 0x80;
+
+ if (sense_class == 7) {
+ s = sense_buffer[7] + 8;
+ if(s > sizeof(SCpnt->sense_buffer)) s = sizeof(SCpnt->sense_buffer);
+
+ if (!valid)
+ printk("extra data not valid ");
+
+ if (sense_buffer[2] & 0x80) printk( "FMK ");
+ if (sense_buffer[2] & 0x40) printk( "EOM ");
+ if (sense_buffer[2] & 0x20) printk( "ILI ");
+
+ switch (code) {
+ case 0x0:
+ error = "Current";
+ break;
+ case 0x1:
+ error = "Deferred";
+ break;
+ default:
+ error = "Invalid";
+ }
+
+ printk("%s error ", error);
+
+#if (CONSTANTS & CONST_SENSE)
+ printk( "%s%s: sense key %s\n", devclass,
+ kdevname(SCpnt->request.rq_dev), snstext[sense_buffer[2] & 0x0f]);
+#else
+ printk("%s%s: sns = %2x %2x\n", devclass,
+ kdevname(SCpnt->request.rq_dev), sense_buffer[0], sense_buffer[2]);
+#endif
+
+ /* Check to see if additional sense information is available */
+ if(sense_buffer[7] + 7 < 13 ||
+ (sense_buffer[12] == 0 && sense_buffer[13] == 0)) goto done;
+
+#if (CONSTANTS & CONST_XSENSE)
+ for(i=0; additional[i].text; i++)
+ if(additional[i].code1 == sense_buffer[12] &&
+ additional[i].code2 == sense_buffer[13])
+ printk("Additional sense indicates %s\n", additional[i].text);
+
+ for(i=0; additional2[i].text; i++)
+ if(additional2[i].code1 == sense_buffer[12] &&
+ additional2[i].code2_min >= sense_buffer[13] &&
+ additional2[i].code2_max <= sense_buffer[13]) {
+ printk("Additional sense indicates ");
+ printk(additional2[i].text, sense_buffer[13]);
+ printk("\n");
+ };
+#else
+ printk("ASC=%2x ASCQ=%2x\n", sense_buffer[12], sense_buffer[13]);
+#endif
+ } else {
+
+#if (CONSTANTS & CONST_SENSE)
+ if (sense_buffer[0] < 15)
+ printk("%s%s: old sense key %s\n", devclass,
+ kdevname(SCpnt->request.rq_dev), snstext[sense_buffer[0] & 0x0f]);
+ else
+#endif
+ printk("%s%s: sns = %2x %2x\n", devclass,
+ kdevname(SCpnt->request.rq_dev), sense_buffer[0], sense_buffer[2]);
+
+ printk("Non-extended sense class %d code 0x%0x ", sense_class, code);
+ s = 4;
+ }
+
+ done:
+#if !(CONSTANTS & CONST_SENSE)
+ printk("Raw sense data:");
+ for (i = 0; i < s; ++i)
+ printk("0x%02x ", sense_buffer[i]);
+ printk("\n");
+#endif
+ return;
+}
+
+#if (CONSTANTS & CONST_MSG)
+static const char *one_byte_msgs[] = {
+/* 0x00 */ "Command Complete", NULL, "Save Pointers",
+/* 0x03 */ "Restore Pointers", "Disconnect", "Initiator Error",
+/* 0x06 */ "Abort", "Message Reject", "Nop", "Message Parity Error",
+/* 0x0a */ "Linked Command Complete", "Linked Command Complete w/flag",
+/* 0x0c */ "Bus device reset", "Abort Tag", "Clear Queue",
+/* 0x0f */ "Initiate Recovery", "Release Recovery"
+};
+
+#define NO_ONE_BYTE_MSGS (sizeof(one_byte_msgs) / sizeof (const char *))
+
+static const char *two_byte_msgs[] = {
+/* 0x20 */ "Simple Queue Tag", "Head of Queue Tag", "Ordered Queue Tag"
+/* 0x23 */ "Ignore Wide Residue"
+};
+
+#define NO_TWO_BYTE_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
+
+static const char *extended_msgs[] = {
+/* 0x00 */ "Modify Data Pointer", "Synchronous Data Transfer Request",
+/* 0x02 */ "SCSI-I Extended Identify", "Wide Data Transfer Request"
+};
+
+#define NO_EXTENDED_MSGS (sizeof(two_byte_msgs) / sizeof (const char *))
+#endif /* (CONSTANTS & CONST_MSG) */
+
+int print_msg (const unsigned char *msg) {
+ int len = 0, i;
+ if (msg[0] == EXTENDED_MESSAGE) {
+ len = 3 + msg[1];
+#if (CONSTANTS & CONST_MSG)
+ if (msg[2] < NO_EXTENDED_MSGS)
+ printk ("%s ", extended_msgs[msg[2]]);
+ else
+ printk ("Extended Message, reserved code (0x%02x) ", (int) msg[2]);
+ switch (msg[2]) {
+ case EXTENDED_MODIFY_DATA_POINTER:
+ printk("pointer = %d", (int) (msg[3] << 24) | (msg[4] << 16) |
+ (msg[5] << 8) | msg[6]);
+ break;
+ case EXTENDED_SDTR:
+ printk("period = %d ns, offset = %d", (int) msg[3] * 4, (int)
+ msg[4]);
+ break;
+ case EXTENDED_WDTR:
+ printk("width = 2^%d bytes", msg[3]);
+ break;
+ default:
+ for (i = 2; i < len; ++i)
+ printk("%02x ", msg[i]);
+ }
+#else
+ for (i = 0; i < len; ++i)
+ printk("%02x ", msg[i]);
+#endif
+ /* Identify */
+ } else if (msg[0] & 0x80) {
+#if (CONSTANTS & CONST_MSG)
+ printk("Identify disconnect %sallowed %s %d ",
+ (msg[0] & 0x40) ? "" : "not ",
+ (msg[0] & 0x20) ? "target routine" : "lun",
+ msg[0] & 0x7);
+#else
+ printk("%02x ", msg[0]);
+#endif
+ len = 1;
+ /* Normal One byte */
+ } else if (msg[0] < 0x1f) {
+#if (CONSTANTS & CONST_MSG)
+ if (msg[0] < NO_ONE_BYTE_MSGS)
+ printk(one_byte_msgs[msg[0]]);
+ else
+ printk("reserved (%02x) ", msg[0]);
+#else
+ printk("%02x ", msg[0]);
+#endif
+ len = 1;
+ /* Two byte */
+ } else if (msg[0] <= 0x2f) {
+#if (CONSTANTS & CONST_MSG)
+ if ((msg[0] - 0x20) < NO_TWO_BYTE_MSGS)
+ printk("%s %02x ", two_byte_msgs[msg[0] - 0x20],
+ msg[1]);
+ else
+ printk("reserved two byte (%02x %02x) ",
+ msg[0], msg[1]);
+#else
+ printk("%02x %02x", msg[0], msg[1]);
+#endif
+ len = 2;
+ } else
+#if (CONSTANTS & CONST_MSG)
+ printk(reserved);
+#else
+ printk("%02x ", msg[0]);
+#endif
+ return len;
+}
+
+void print_Scsi_Cmnd (Scsi_Cmnd *cmd) {
+ printk("scsi%d : destination target %d, lun %d\n",
+ cmd->host->host_no,
+ cmd->target,
+ cmd->lun);
+ printk(" command = ");
+ print_command (cmd->cmnd);
+}
+
+#if (CONSTANTS & CONST_HOST)
+static const char * hostbyte_table[]={
+"DID_OK", "DID_NO_CONNECT", "DID_BUS_BUSY", "DID_TIME_OUT", "DID_BAD_TARGET",
+"DID_ABORT", "DID_PARITY", "DID_ERROR", "DID_RESET", "DID_BAD_INTR",NULL};
+
+void print_hostbyte(int scsiresult)
+{ static int maxcode=0;
+ int i;
+
+ if(!maxcode) {
+ for(i=0;hostbyte_table[i];i++) ;
+ maxcode=i-1;
+ }
+ printk("Hostbyte=0x%02x",host_byte(scsiresult));
+ if(host_byte(scsiresult)>maxcode) {
+ printk("is invalid ");
+ return;
+ }
+ printk("(%s) ",hostbyte_table[host_byte(scsiresult)]);
+}
+#else
+void print_hostbyte(int scsiresult)
+{ printk("Hostbyte=0x%02x ",host_byte(scsiresult));
+}
+#endif
+
+#if (CONSTANTS & CONST_DRIVER)
+static const char * driverbyte_table[]={
+"DRIVER_OK", "DRIVER_BUSY", "DRIVER_SOFT", "DRIVER_MEDIA", "DRIVER_ERROR",
+"DRIVER_INVALID", "DRIVER_TIMEOUT", "DRIVER_HARD",NULL };
+
+static const char * driversuggest_table[]={"SUGGEST_OK",
+"SUGGEST_RETRY", "SUGGEST_ABORT", "SUGGEST_REMAP", "SUGGEST_DIE",
+unknown,unknown,unknown, "SUGGEST_SENSE",NULL};
+
+
+void print_driverbyte(int scsiresult)
+{ static int driver_max=0,suggest_max=0;
+ int i,dr=driver_byte(scsiresult)&DRIVER_MASK,
+ su=(driver_byte(scsiresult)&SUGGEST_MASK)>>4;
+
+ if(!driver_max) {
+ for(i=0;driverbyte_table[i];i++) ;
+ driver_max=i;
+ for(i=0;driversuggest_table[i];i++) ;
+ suggest_max=i;
+ }
+ printk("Driverbyte=0x%02x",driver_byte(scsiresult));
+ printk("(%s,%s) ",
+ dr<driver_max ? driverbyte_table[dr]:"invalid",
+ su<suggest_max ? driversuggest_table[su]:"invalid");
+}
+#else
+void print_driverbyte(int scsiresult)
+{ printk("Driverbyte=0x%02x ",driver_byte(scsiresult));
+}
+#endif
+
+/*
+ * Overrides for Emacs so that we almost 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/i386/i386at/gpl/linux/scsi/constants.h b/i386/i386at/gpl/linux/scsi/constants.h
new file mode 100644
index 00000000..e10527ea
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/constants.h
@@ -0,0 +1,6 @@
+#ifndef _CONSTANTS_H
+#define _CONSTANTS_H
+extern int print_msg(unsigned char *);
+extern void print_status(int);
+extern void print_Scsi_Cmnd (Scsi_Cmnd *);
+#endif /* def _CONSTANTS_H */
diff --git a/i386/i386at/gpl/linux/scsi/eata.c b/i386/i386at/gpl/linux/scsi/eata.c
new file mode 100644
index 00000000..29000332
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata.c
@@ -0,0 +1,1099 @@
+/*
+ * eata.c - Low-level driver for EATA/DMA SCSI host adapters.
+ *
+ * 6 Jul 1995 rev. 2.01 for linux 1.3.7
+ * Update required by the new /proc/scsi support.
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * Now DEBUG_RESET is disabled by default.
+ * Register a board even if it does not assert DMA protocol support
+ * (DPT SK2011B does not report correctly the dmasup bit).
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ * New list of Data Out SCSI commands.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ * All external symbols renamed to avoid possible name conflicts.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ *
+ * 24 Jan 1995 rev. 1.13 for linux 1.1.85
+ * Use optimized board configuration, with a measured performance
+ * increase in the range 10%-20% on i/o throughput.
+ *
+ * 16 Jan 1995 rev. 1.12 for linux 1.1.81
+ * Fix mscp structure comments (no functional change).
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 17 Dec 1994 rev. 1.11 for linux 1.1.74
+ * Use the scsicam_bios_param routine. This allows an easy
+ * migration path from disk partition tables created using
+ * different SCSI drivers and non optimal disk geometry.
+ *
+ * 15 Dec 1994 rev. 1.10 for linux 1.1.74
+ * Added support for ISA EATA boards (DPT PM2011, DPT PM2021).
+ * The host->block flag is set for all the detected ISA boards.
+ * The detect routine no longer enforces LEVEL triggering
+ * for EISA boards, it just prints a warning message.
+ *
+ * 30 Nov 1994 rev. 1.09 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
+ *
+ * 18 Nov 1994 rev. 1.08 for linux 1.1.64
+ * Forces sg_tablesize = 64 and can_queue = 64 if these
+ * values are not correctly detected (DPT PM2012).
+ *
+ * 14 Nov 1994 rev. 1.07 for linux 1.1.63 Final BETA release.
+ * 04 Aug 1994 rev. 1.00 for linux 1.1.39 First BETA release.
+ *
+ *
+ * This driver is based on the CAM (Common Access Method Committee)
+ * EATA (Enhanced AT Bus Attachment) rev. 2.0A, using DMA protocol.
+ *
+ * Copyright (C) 1994, 1995 Dario Ballabio (dario@milano.europe.dg.com)
+ *
+ */
+
+/*
+ *
+ * Here is a brief description of the DPT SCSI host adapters.
+ * All these boards provide an EATA/DMA compatible programming interface
+ * and are fully supported by this driver:
+ *
+ * PM2011B/9X - Entry Level ISA
+ * PM2021A/9X - High Performance ISA
+ * PM2012A Old EISA
+ * PM2012B Old EISA
+ * PM2022A/9X - Entry Level EISA
+ * PM2122A/9X - High Performance EISA
+ * PM2322A/9X - Extra High Performance EISA
+ *
+ * The DPT PM2001 provides only the EATA/PIO interface and hence is not
+ * supported by this driver.
+ *
+ * This code has been tested with up to 3 Distributed Processing Technology
+ * PM2122A/9X (DPT SCSI BIOS v002.D1, firmware v05E.0) eisa controllers,
+ * no on board cache and no RAID option.
+ * BIOS must be enabled on the first board and must be disabled for all other
+ * boards.
+ * Support is provided for any number of DPT PM2122 eisa boards.
+ * All boards should be configured at the same IRQ level.
+ * Multiple IRQ configurations are supported too.
+ * Boards can be located in any eisa slot (1-15) and are named EATA0,
+ * EATA1,... in increasing eisa slot number. ISA boards are detected
+ * after the eisa slot probes.
+ *
+ * The IRQ for EISA boards should be _level_ triggered (not _edge_ triggered).
+ * This is a requirement in order to support multiple boards on the same IRQ.
+ *
+ * Other eisa configuration parameters are:
+ *
+ * COMMAND QUEUING : ENABLED
+ * COMMAND TIMEOUT : ENABLED
+ * CACHE : DISABLED
+ *
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
+ */
+
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include "linux/in.h"
+#include "eata.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_eata2x = {
+ PROC_SCSI_EATA2X, 6, "eata2x",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#undef FORCE_CONFIG
+
+#undef DEBUG_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_STATISTICS
+#undef DEBUG_RESET
+
+#define MAX_TARGET 8
+#define MAX_IRQ 16
+#define MAX_BOARDS 18
+#define MAX_MAILBOXES 64
+#define MAX_SGLIST 64
+#define MAX_CMD_PER_LUN 2
+
+#define FALSE 0
+#define TRUE 1
+#define FREE 0
+#define IN_USE 1
+#define LOCKED 2
+#define IN_RESET 3
+#define IGNORE 4
+#define NO_IRQ 0xff
+#define NO_DMA 0xff
+#define MAXLOOP 200000
+
+#define REG_CMD 7
+#define REG_STATUS 7
+#define REG_AUX_STATUS 8
+#define REG_DATA 0
+#define REG_DATA2 1
+#define REG_SEE 6
+#define REG_LOW 2
+#define REG_LM 3
+#define REG_MID 4
+#define REG_MSB 5
+#define REGION_SIZE 9
+#define EISA_RANGE 0xf000
+#define BSY_ASSERTED 0x80
+#define DRQ_ASSERTED 0x08
+#define ABSY_ASSERTED 0x01
+#define IRQ_ASSERTED 0x02
+#define READ_CONFIG_PIO 0xf0
+#define SET_CONFIG_PIO 0xf1
+#define SEND_CP_PIO 0xf2
+#define RECEIVE_SP_PIO 0xf3
+#define TRUNCATE_XFR_PIO 0xf4
+#define RESET_PIO 0xf9
+#define READ_CONFIG_DMA 0xfd
+#define SET_CONFIG_DMA 0xfe
+#define SEND_CP_DMA 0xff
+#define ASOK 0x00
+#define ASST 0x01
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+
+/* "EATA", in Big Endian format */
+#define EATA_SIGNATURE 0x41544145
+
+/* Number of valid bytes in the board config structure for EATA 2.0x */
+#define EATA_2_0A_SIZE 28
+#define EATA_2_0B_SIZE 30
+
+/* Board info structure */
+struct eata_info {
+ ulong data_len; /* Number of valid bytes after this field */
+ ulong sign; /* ASCII "EATA" signature */
+ unchar :4, /* unused low nibble */
+ version:4; /* EATA version, should be 0x1 */
+ unchar ocsena:1, /* Overlap Command Support Enabled */
+ tarsup:1, /* Target Mode Supported */
+ :2,
+ dmasup:1, /* DMA Supported */
+ drqvld:1, /* DRQ Index (DRQX) is valid */
+ ata:1, /* This is an ATA device */
+ haaval:1; /* Host Adapter Address Valid */
+ ushort cp_pad_len; /* Number of pad bytes after cp_len */
+ unchar host_addr[3]; /* Host Adapter SCSI ID for channels 2, 1, 0 */
+ unchar reserved;
+ ulong cp_len; /* Number of valid bytes in cp */
+ ulong sp_len; /* Number of valid bytes in sp */
+ ushort queue_size; /* Max number of cp that can be queued */
+ ushort unused;
+ ushort scatt_size; /* Max number of entries in scatter/gather table */
+ unchar irq:4, /* Interrupt Request assigned to this controller */
+ irq_tr:1, /* 0 for edge triggered, 1 for level triggered */
+ second:1, /* 1 if this is a secondary (not primary) controller */
+ drqx:2; /* DRQ Index (0=DMA0, 1=DMA7, 2=DMA6, 3=DMA5) */
+ unchar sync; /* 1 if scsi target id 7...0 is running sync scsi */
+
+ /* Structure extension defined in EATA 2.0B */
+ unchar isaena:1, /* ISA i/o addressing is disabled/enabled */
+ forcaddr:1, /* Port address has been forced */
+ :6;
+ unchar max_id:5, /* Max number of SCSI target IDs */
+ max_chan:3; /* Max SCSI channel number on this board */
+
+ ushort ipad[249];
+ };
+
+/* Board config structure */
+struct eata_config {
+ ushort len; /* Number of bytes following this field */
+ unchar edis:1, /* Disable EATA interface after config command */
+ ocena:1, /* Overlapped Commands Enabled */
+ mdpena:1, /* Transfer all Modified Data Pointer Messages */
+ tarena:1, /* Target Mode Enabled for this controller */
+ :4;
+ unchar cpad[511];
+ };
+
+/* Returned status packet structure */
+struct mssp {
+ unchar adapter_status:7, /* State related to current command */
+ eoc:1; /* End Of Command (1 = command completed) */
+ unchar target_status; /* SCSI status received after data transfer */
+ unchar unused[2];
+ ulong inv_res_len; /* Number of bytes not transferred */
+ Scsi_Cmnd *SCpnt; /* Address set in cp */
+ char mess[12];
+ };
+
+/* MailBox SCSI Command Packet */
+struct mscp {
+ unchar sreset:1, /* SCSI Bus Reset Signal should be asserted */
+ init:1, /* Re-initialize controller and self test */
+ reqsen:1, /* Transfer Request Sense Data to addr using DMA */
+ sg:1, /* Use Scatter/Gather */
+ :1,
+ interp:1, /* The controller interprets cp, not the target */
+ dout:1, /* Direction of Transfer is Out (Host to Target) */
+ din:1; /* Direction of Transfer is In (Target to Host) */
+ unchar sense_len; /* Request Sense Length */
+ unchar unused[4];
+ unchar phsunit:1, /* Send to Target Physical Unit (bypass RAID) */
+ notused:7;
+ unchar target; /* SCSI Target ID */
+ unchar lun:3, /* LUN */
+ :2,
+ luntar:1, /* This cp is for Target (not LUN) */
+ dispri:1, /* Disconnect Privilege granted */
+ one:1; /* 1 */
+ unchar mess[3]; /* Massage to/from Target */
+ unchar cdb[12]; /* Command Descriptor Block */
+ ulong data_len; /* If sg=0 Data Length, if sg=1 sglist length */
+ Scsi_Cmnd *SCpnt; /* Address to be returned is sp */
+ ulong data_address; /* If sg=0 Data Address, if sg=1 sglist address */
+ ulong sp_addr; /* Address where sp is DMA'ed when cp completes */
+ ulong sense_addr; /* Address where Sense Data is DMA'ed on error */
+
+ struct sg_list {
+ unsigned int address; /* Segment Address */
+ unsigned int num_bytes; /* Segment Length */
+ } sglist[MAX_SGLIST];
+
+ unsigned int index; /* cp index */
+ };
+
+struct hostdata {
+ struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
+ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
+ unsigned int last_cp_used; /* Index of last mailbox used */
+ unsigned int iocount; /* Total i/o done for this board */
+ unsigned int multicount; /* Total ... in second ihdlr loop */
+ int board_number; /* Number of this board */
+ char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
+ int in_reset; /* True if board is doing a reset */
+ int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
+ int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
+ unsigned char subversion; /* Bus type, either ISA or ESA */
+ unsigned char protocol_rev; /* EATA 2.0 rev., 'A' or 'B' or 'C' */
+ struct mssp sp[MAX_MAILBOXES]; /* Returned status for this board */
+ };
+
+static struct Scsi_Host * sh[MAX_BOARDS + 1];
+static const char* driver_name = "EATA";
+static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
+
+#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
+#define BN(board) (HD(board)->board_name)
+
+static void eata2x_interrupt_handler(int, struct pt_regs *);
+static int do_trace = FALSE;
+
+static inline unchar wait_on_busy(ushort iobase) {
+ unsigned int loop = MAXLOOP;
+
+ while (inb(iobase + REG_AUX_STATUS) & ABSY_ASSERTED)
+ if (--loop == 0) return TRUE;
+
+ return FALSE;
+}
+
+static inline unchar do_dma (ushort iobase, unsigned int addr, unchar cmd) {
+
+ if (wait_on_busy(iobase)) return TRUE;
+
+ if (addr) {
+ outb((char) addr, iobase + REG_LOW);
+ outb((char) (addr >> 8), iobase + REG_LM);
+ outb((char) (addr >> 16), iobase + REG_MID);
+ outb((char) (addr >> 24), iobase + REG_MSB);
+ }
+
+ outb(cmd, iobase + REG_CMD);
+ return FALSE;
+}
+
+static inline unchar read_pio (ushort iobase, ushort *start, ushort *end) {
+ unsigned int loop = MAXLOOP;
+ ushort *p;
+
+ for (p = start; p <= end; p++) {
+
+ while (!(inb(iobase + REG_STATUS) & DRQ_ASSERTED))
+ if (--loop == 0) return TRUE;
+
+ loop = MAXLOOP;
+ *p = inw(iobase);
+ }
+
+ return FALSE;
+}
+
+static inline int port_detect(ushort *port_base, unsigned int j,
+ Scsi_Host_Template * tpnt) {
+ unsigned char irq, dma_channel, subversion;
+ unsigned char protocol_rev;
+ struct eata_info info;
+ const char *board_status;
+
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
+ char name[16];
+
+ sprintf(name, "%s%d", driver_name, j);
+
+ if(check_region(*port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03x in use, skipping probe.\n",
+ name, *port_base);
+ return FALSE;
+ }
+
+ if (do_dma(*port_base, 0, READ_CONFIG_PIO)) return FALSE;
+
+ /* Read the info structure */
+ if (read_pio(*port_base, (ushort *)&info, (ushort *)&info.ipad[0]))
+ return FALSE;
+
+ /* Check the controller "EATA" signature */
+ if (info.sign != EATA_SIGNATURE) return FALSE;
+
+ if (ntohl(info.data_len) < EATA_2_0A_SIZE) {
+ printk("%s: config structure size (%ld bytes) too short, detaching.\n",
+ name, ntohl(info.data_len));
+ return FALSE;
+ }
+ else if (ntohl(info.data_len) == EATA_2_0A_SIZE)
+ protocol_rev = 'A';
+ else if (ntohl(info.data_len) == EATA_2_0B_SIZE)
+ protocol_rev = 'B';
+ else
+ protocol_rev = 'C';
+
+ if (protocol_rev != 'A' && info.max_chan > 0)
+ printk("%s: warning, only scsi channel 0 is supported.\n", name);
+
+ irq = info.irq;
+
+ if (*port_base & EISA_RANGE) {
+
+ if (!info.haaval || info.ata || info.drqvld) {
+ printk("%s: unusable EISA board found (%d%d%d), detaching.\n",
+ name, info.haaval, info.ata, info.drqvld);
+ return FALSE;
+ }
+
+ subversion = ESA;
+ dma_channel = NO_DMA;
+ }
+ else {
+
+ if (!info.haaval || info.ata || !info.drqvld) {
+ printk("%s: unusable ISA board found (%d%d%d), detaching.\n",
+ name, info.haaval, info.ata, info.drqvld);
+ return FALSE;
+ }
+
+ subversion = ISA;
+ dma_channel = dma_channel_table[3 - info.drqx];
+ }
+
+ if (!info.dmasup)
+ printk("%s: warning, DMA protocol support not asserted.\n", name);
+
+ if (subversion == ESA && !info.irq_tr)
+ printk("%s: warning, LEVEL triggering is suggested for IRQ %u.\n",
+ name, irq);
+
+ if (info.second)
+ board_status = "Sec.";
+ else
+ board_status = "Prim.";
+
+ /* Board detected, allocate its IRQ if not already done */
+ if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
+ (irq, eata2x_interrupt_handler, SA_INTERRUPT, driver_name))) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
+ return FALSE;
+ }
+
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ free_irq(irq);
+ return FALSE;
+ }
+
+#if defined (FORCE_CONFIG)
+ {
+ struct eata_config config;
+
+ /* Set board configuration */
+ memset((char *)&config, 0, sizeof(struct eata_config));
+ config.len = (ushort) htons((ushort)510);
+ config.ocena = TRUE;
+
+ if (do_dma(*port_base, (unsigned int)&config, SET_CONFIG_DMA)) {
+ printk("%s: busy timeout sending configuration, detaching.\n", name);
+ return FALSE;
+ }
+ }
+#endif
+
+ sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+
+ if (irqlist[irq] == NO_IRQ) free_irq(irq);
+
+ if (subversion == ISA) free_dma(dma_channel);
+
+ return FALSE;
+ }
+
+ sh[j]->io_port = *port_base;
+ sh[j]->n_io_port = REGION_SIZE;
+ sh[j]->dma_channel = dma_channel;
+ sh[j]->irq = irq;
+ sh[j]->sg_tablesize = (ushort) ntohs(info.scatt_size);
+ sh[j]->this_id = (ushort) info.host_addr[3];
+ sh[j]->can_queue = (ushort) ntohs(info.queue_size);
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+
+ /* Register the I/O space that we use */
+ request_region(sh[j]->io_port, REGION_SIZE, driver_name);
+
+ memset(HD(j), 0, sizeof(struct hostdata));
+ HD(j)->subversion = subversion;
+ HD(j)->protocol_rev = protocol_rev;
+ HD(j)->board_number = j;
+ irqlist[irq] = j;
+
+ if (HD(j)->subversion == ESA)
+ sh[j]->unchecked_isa_dma = FALSE;
+ else {
+ sh[j]->wish_block = TRUE;
+ sh[j]->unchecked_isa_dma = TRUE;
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ }
+
+ strcpy(BN(j), name);
+
+ printk("%s: 2.0%c, %s, ID %d, PORT 0x%03x, IRQ %u, DMA %u, SG %d, "\
+ "Mbox %d, CmdLun %d.\n", BN(j), HD(j)->protocol_rev, board_status,
+ sh[j]->this_id, sh[j]->io_port, sh[j]->irq, sh[j]->dma_channel,
+ sh[j]->sg_tablesize, sh[j]->can_queue, sh[j]->cmd_per_lun);
+
+ /* DPT PM2012 does not allow to detect sg_tablesize correctly */
+ if (sh[j]->sg_tablesize > MAX_SGLIST || sh[j]->sg_tablesize < 2) {
+ printk("%s: detect, forcing to use %d SG lists.\n", BN(j), MAX_SGLIST);
+ sh[j]->sg_tablesize = MAX_SGLIST;
+ }
+
+ /* DPT PM2012 does not allow to detect can_queue correctly */
+ if (sh[j]->can_queue > MAX_MAILBOXES || sh[j]->can_queue < 2) {
+ printk("%s: detect, forcing to use %d Mbox.\n", BN(j), MAX_MAILBOXES);
+ sh[j]->can_queue = MAX_MAILBOXES;
+ }
+
+#if defined (DEBUG_DETECT)
+ if (protocol_rev != 'A')
+ printk("%s: EATA 2.0%c, isaena %u, forcaddr %u, max_id %u,"\
+ " max_chan %u.\n", name, protocol_rev, info.isaena,
+ info.forcaddr, info.max_id, info.max_chan);
+
+ printk("%s: Version 0x%x, SYNC 0x%x, infol %ld, cpl %ld spl %ld.\n",
+ name, info.version, info.sync, ntohl(info.data_len),
+ ntohl(info.cp_len), ntohl(info.sp_len));
+#endif
+
+ return TRUE;
+}
+
+int eata2x_detect (Scsi_Host_Template * tpnt) {
+ unsigned int j = 0, k, flags;
+
+ ushort io_port[] = {
+ 0x1c88, 0x2c88, 0x3c88, 0x4c88, 0x5c88, 0x6c88, 0x7c88, 0x8c88,
+ 0x9c88, 0xac88, 0xbc88, 0xcc88, 0xdc88, 0xec88, 0xfc88,
+ 0x1f0, 0x170, 0x330, 0x230, 0x0
+ };
+
+ ushort *port_base = io_port;
+
+ tpnt->proc_dir = &proc_scsi_eata2x;
+
+ save_flags(flags);
+ cli();
+
+ for (k = 0; k < MAX_IRQ; k++) {
+ irqlist[k] = NO_IRQ;
+ calls[k] = 0;
+ }
+
+ for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
+
+ while (*port_base) {
+
+ if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+
+ port_base++;
+ }
+
+ if (j > 0)
+ printk("EATA/DMA 2.0x: Copyright (C) 1994, 1995 Dario Ballabio.\n");
+
+ restore_flags(flags);
+ return j;
+}
+
+static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
+ unsigned int k;
+ struct scatterlist * sgpnt;
+
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+
+ for (k = 0; k < SCpnt->use_sg; k++) {
+ cpp->sglist[k].address = htonl((unsigned int) sgpnt[k].address);
+ cpp->sglist[k].num_bytes = htonl((unsigned int) sgpnt[k].length);
+ }
+
+ cpp->data_address = htonl((unsigned int) cpp->sglist);
+ cpp->data_len = htonl((SCpnt->use_sg * sizeof(struct sg_list)));
+}
+
+int eata2x_queuecommand (Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+ unsigned int i, j, k, flags;
+ struct mscp *cpp;
+ struct mssp *spp;
+
+ static const unsigned char data_out_cmds[] = {
+ 0x0a, 0x2a, 0x15, 0x55, 0x04, 0x07, 0x0b, 0x10, 0x16, 0x18, 0x1d,
+ 0x24, 0x2b, 0x2e, 0x30, 0x31, 0x32, 0x38, 0x39, 0x3a, 0x3b, 0x3d,
+ 0x3f, 0x40, 0x41, 0x4c, 0xaa, 0xae, 0xb0, 0xb1, 0xb2, 0xb6, 0xea
+ };
+
+ save_flags(flags);
+ cli();
+ /* j is the board number */
+ j = ((struct hostdata *) SCpnt->host->hostdata)->board_number;
+
+ if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
+
+ /* i is the mailbox number, look for the first free mailbox
+ starting from last_cp_used */
+ i = HD(j)->last_cp_used + 1;
+
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
+
+ if (i >= sh[j]->can_queue) i = 0;
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ HD(j)->last_cp_used = i;
+ break;
+ }
+ }
+
+ if (k == sh[j]->can_queue) {
+ printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
+
+ if (HD(j)->in_reset)
+ printk("%s: qcomm, already in reset.\n", BN(j));
+ else if (eata2x_reset(SCpnt) == SCSI_RESET_SUCCESS)
+ panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
+
+ SCpnt->result = DID_BUS_BUSY << 16;
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
+ restore_flags(flags);
+ done(SCpnt);
+ return 0;
+ }
+
+ /* Set pointer to control packet structure */
+ cpp = &HD(j)->cp[i];
+
+ memset(cpp, 0, sizeof(struct mscp));
+
+ /* Set pointer to status packet structure */
+ spp = &HD(j)->sp[i];
+
+ memset(spp, 0, sizeof(struct mssp));
+
+ /* The EATA protocol uses Big Endian format, while Intel is Little Endian */
+ cpp->sp_addr = htonl((unsigned int) spp);
+
+ SCpnt->scsi_done = done;
+ cpp->index = i;
+ SCpnt->host_scribble = (unsigned char *) &cpp->index;
+
+ if (do_trace) printk("%s: qcomm, mbox %d, target %d, pid %ld.\n",
+ BN(j), i, SCpnt->target, SCpnt->pid);
+
+ for (k = 0; k < ARRAY_SIZE(data_out_cmds); k++)
+ if (SCpnt->cmnd[0] == data_out_cmds[k]) {
+ cpp->dout = TRUE;
+ break;
+ }
+
+ cpp->din = !cpp->dout;
+ cpp->reqsen = TRUE;
+ cpp->dispri = TRUE;
+ cpp->one = TRUE;
+ cpp->target = SCpnt->target;
+ cpp->lun = SCpnt->lun;
+ cpp->SCpnt = SCpnt;
+ cpp->sense_addr = htonl((unsigned int) SCpnt->sense_buffer);
+ cpp->sense_len = sizeof SCpnt->sense_buffer;
+
+ if (SCpnt->use_sg) {
+ cpp->sg = TRUE;
+ build_sg_list(cpp, SCpnt);
+ }
+ else {
+ cpp->data_address = htonl((unsigned int) SCpnt->request_buffer);
+ cpp->data_len = htonl(SCpnt->request_bufflen);
+ }
+
+ memcpy(cpp->cdb, SCpnt->cmnd, SCpnt->cmd_len);
+
+ /* Send control packet to the board */
+ if (do_dma(sh[j]->io_port, (unsigned int) cpp, SEND_CP_DMA)) {
+ SCpnt->result = DID_ERROR << 16;
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, target %d, pid %ld, adapter busy, DID_ERROR, done.\n",
+ BN(j), SCpnt->target, SCpnt->pid);
+ restore_flags(flags);
+ done(SCpnt);
+ return 0;
+ }
+
+ HD(j)->cp_stat[i] = IN_USE;
+ restore_flags(flags);
+ return 0;
+}
+
+int eata2x_abort (Scsi_Cmnd *SCarg) {
+ unsigned int i, j, flags;
+
+ save_flags(flags);
+ cli();
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL) {
+ printk("%s: abort, target %d, pid %ld inactive.\n",
+ BN(j), SCarg->target, SCarg->pid);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d, pid %ld.\n",
+ BN(j), i, SCarg->target, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ restore_flags(flags);
+ return SCSI_ABORT_SNOOZE;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ restore_flags(flags);
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+ restore_flags(flags);
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+int eata2x_reset (Scsi_Cmnd *SCarg) {
+ unsigned int i, j, flags, time, k, limit = 0;
+ int arg_done = FALSE;
+ Scsi_Cmnd *SCpnt;
+
+ save_flags(flags);
+ cli();
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d, pid %ld.\n",
+ BN(j), SCarg->target, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_RESET_ERROR;
+ }
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_RESET_ERROR;
+ }
+
+ for (k = 0; k < MAX_TARGET; k++) HD(j)->target_reset[k] = TRUE;
+
+ for (k = 0; k < MAX_TARGET; k++) HD(j)->target_time_out[k] = 0;
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ SCpnt = HD(j)->cp[i].SCpnt;
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+
+ if (SCpnt == NULL)
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (do_dma(sh[j]->io_port, 0, RESET_PIO)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_RESET_ERROR;
+ }
+
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined (DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+ sti();
+ time = jiffies;
+ while (jiffies < (time + 100) && limit++ < 100000000);
+ cli();
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ /* Skip mailboxes already set free by interrupt */
+ if (HD(j)->cp_stat[i] != IN_RESET) continue;
+
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ restore_flags(flags);
+ SCpnt->scsi_done(SCpnt);
+ cli();
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+ restore_flags(flags);
+
+ if (arg_done) {
+ printk("%s: reset, exit, success.\n", BN(j));
+ return SCSI_RESET_SUCCESS;
+ }
+ else {
+ printk("%s: reset, exit, wakeup.\n", BN(j));
+ return SCSI_RESET_PUNT;
+ }
+}
+
+static void eata2x_interrupt_handler(int irq, struct pt_regs * regs) {
+ Scsi_Cmnd *SCpnt;
+ unsigned int i, j, k, flags, status, tstatus, loops, total_loops = 0;
+ struct mssp *spp;
+ struct mscp *cpp;
+
+ save_flags(flags);
+ cli();
+
+ if (irqlist[irq] == NO_IRQ) {
+ printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
+ restore_flags(flags);
+ return;
+ }
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n",
+ driver_name, irq, calls[irq]);
+
+ /* Service all the boards configured on this irq */
+ for (j = 0; sh[j] != NULL; j++) {
+
+ if (sh[j]->irq != irq) continue;
+
+ loops = 0;
+
+ /* Loop until all interrupts for a board are serviced */
+ while (inb(sh[j]->io_port + REG_AUX_STATUS) & IRQ_ASSERTED) {
+ total_loops++;
+ loops++;
+
+ if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
+ BN(j), HD(j)->iocount);
+
+ /* Read the status register to clear the interrupt indication */
+ inb(sh[j]->io_port + REG_STATUS);
+
+ /* Service all mailboxes of this board */
+ for (i = 0; i < sh[j]->can_queue; i++) {
+ spp = &HD(j)->sp[i];
+
+ /* Check if this mailbox has completed the operation */
+ if (spp->eoc == FALSE) continue;
+
+ spp->eoc = FALSE;
+
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
+ BN(j), i, HD(j)->iocount);
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n",
+ BN(j), i, HD(j)->iocount);
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
+
+ HD(j)->cp_stat[i] = FREE;
+ cpp = &HD(j)->cp[i];
+ SCpnt = spp->SCpnt;
+
+ if (SCpnt == NULL)
+ panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt != cpp->SCpnt)
+ panic("%s: ihdlr, mbox %d, sp SCpnt %p, cp SCpnt %p.\n",
+ BN(j), i, SCpnt, cpp->SCpnt);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
+ BN(j), i, SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
+ " irq %d.\n", BN(j), i, SCpnt->pid,
+ *(unsigned int *)SCpnt->host_scribble, irq);
+
+ tstatus = status_byte(spp->target_status);
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD
+ && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_reset[SCpnt->target])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ HD(j)->target_reset[SCpnt->target] = FALSE;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d:%d, pid %ld, target_status "\
+ "0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->target, SCpnt->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
+ HD(j)->target_time_out[SCpnt->target] = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+ case 0x02: /* Command Time Out */
+
+ if (HD(j)->target_time_out[SCpnt->target] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_time_out[SCpnt->target]++;
+ }
+
+ break;
+ case 0x03: /* SCSI Bus Reset Received */
+ case 0x04: /* Initial Controller Power-up */
+
+ if (SCpnt->device->type != TYPE_TAPE)
+ status = DID_BUS_BUSY << 16;
+ else
+ status = DID_ERROR << 16;
+
+ for (k = 0; k < MAX_TARGET; k++)
+ HD(j)->target_reset[k] = TRUE;
+
+ break;
+ case 0x07: /* Bus Parity Error */
+ case 0x0c: /* Controller Ram Parity */
+ case 0x05: /* Unexpected Bus Phase */
+ case 0x06: /* Unexpected Bus Free */
+ case 0x08: /* SCSI Hung */
+ case 0x09: /* Unexpected Message Reject */
+ case 0x0a: /* SCSI Bus Reset Stuck */
+ case 0x0b: /* Auto Request-Sense Failed */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+ HD(j)->iocount++;
+
+ if (loops > 1) HD(j)->multicount++;
+
+#if defined (DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace)
+#endif
+ printk("%s: ihdlr, mbox %d, err 0x%x:%x,"\
+ " target %d:%d, pid %ld, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->target, SCpnt->lun, SCpnt->pid, HD(j)->iocount);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ restore_flags(flags);
+ SCpnt->scsi_done(SCpnt);
+ cli();
+
+ } /* Mailbox loop */
+
+ } /* Multiple command loop */
+
+ } /* Boards loop */
+
+ calls[irq]++;
+
+ if (total_loops == 0)
+ printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
+ driver_name, irq, calls[irq]);
+
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n",
+ driver_name, irq, calls[irq]);
+
+#if defined (DEBUG_STATISTICS)
+ if ((calls[irq] % 100000) == 10000)
+ for (j = 0; sh[j] != NULL; j++)
+ printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
+ calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
+#endif
+
+ restore_flags(flags);
+ return;
+}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = EATA;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/eata.h b/i386/i386at/gpl/linux/scsi/eata.h
new file mode 100644
index 00000000..aabcc806
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata.h
@@ -0,0 +1,41 @@
+/*
+ * eata.h - used by the low-level driver for EATA/DMA SCSI host adapters.
+ *
+ */
+#ifndef _EATA_H
+#define _EATA_H
+
+#include <linux/scsicam.h>
+
+int eata2x_detect(Scsi_Host_Template *);
+int eata2x_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int eata2x_abort(Scsi_Cmnd *);
+int eata2x_reset(Scsi_Cmnd *);
+
+#define EATA_VERSION "2.01.00"
+
+
+#define EATA { \
+ NULL, /* Ptr for modules */ \
+ NULL, /* usage count for modules */ \
+ NULL, \
+ NULL, \
+ "EATA/DMA 2.0x rev. " EATA_VERSION " ", \
+ eata2x_detect, \
+ NULL, /* Release */ \
+ NULL, \
+ NULL, \
+ eata2x_queuecommand, \
+ eata2x_abort, \
+ eata2x_reset, \
+ NULL, \
+ scsicam_bios_param, \
+ 0, /* can_queue, reset by detect */ \
+ 7, /* this_id, reset by detect */ \
+ 0, /* sg_tablesize, reset by detect */ \
+ 0, /* cmd_per_lun, reset by detect */ \
+ 0, /* number of boards present */ \
+ 1, /* unchecked isa dma, reset by detect */ \
+ ENABLE_CLUSTERING \
+ }
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/eata_dma.c b/i386/i386at/gpl/linux/scsi/eata_dma.c
new file mode 100644
index 00000000..da9e3daa
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_dma.c
@@ -0,0 +1,1375 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all ISA based EATA-DMA boards *
+ * -supports all EISA based EATA-DMA boards *
+ * -supports all PCI based EATA-DMA boards *
+ * -supports multiple HBAs with & without IRQ sharing *
+ * -supports all SCSI channels on multi channel boards *
+ * -needs identical IDs on all channels of a HBA *
+ * -can be loaded as module *
+ * -displays statistical and hardware information *
+ * in /proc/scsi/eata_dma *
+ * -provides rudimentary latency measurement *
+ * possibilities via /proc/scsi/eata_dma/<hostnum> *
+ * *
+ * (c)1993,94,95 Michael Neuffer *
+ * neuffer@goofy.zdv.uni-mainz.de *
+ * *
+ * 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 kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ * I have to thank DPT for their excellent support. I took *
+ * me almost a year and a stopover at their HQ, on my first *
+ * trip to the USA, to get it, but since then they've been *
+ * very helpful and tried to give me all the infos and *
+ * support I need. *
+ * *
+ * Thanks also to Greg Hosler who did a lot of testing and *
+ * found quite a number of bugs during the development. *
+ ************************************************************
+ * last change: 95/11/29 OS: Linux 1.3.45 *
+ ************************************************************/
+
+/* Look in eata_dma.h for configuration and revision information */
+
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <asm/byteorder.h>
+#include <asm/types.h>
+#include <asm/io.h>
+#include <asm/dma.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "sd.h"
+#include "hosts.h"
+#include <linux/scsicam.h>
+#include "eata_dma.h"
+#include "eata_dma_proc.h"
+
+#include <linux/stat.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_eata_dma = {
+ PROC_SCSI_EATA, 8, "eata_dma",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static u32 ISAbases[] =
+{0x1F0, 0x170, 0x330, 0x230};
+static unchar EISAbases[] =
+{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static uint registered_HBAs = 0;
+static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
+static unchar reg_IRQ[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static unchar reg_IRQL[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static struct eata_sp *status = 0; /* Statuspacket array */
+static void *dma_scratch = 0;
+
+static struct eata_register *fake_int_base;
+static int fake_int_result;
+static int fake_int_happened;
+
+static ulong int_counter = 0;
+static ulong queue_counter = 0;
+
+void eata_scsi_done (Scsi_Cmnd * scmd)
+{
+ scmd->request.rq_status = RQ_SCSI_DONE;
+
+ if (scmd->request.sem != NULL)
+ up(scmd->request.sem);
+
+ return;
+}
+
+void eata_fake_int_handler(s32 irq, struct pt_regs * regs)
+{
+ fake_int_result = inb((ulong)fake_int_base + HA_RSTATUS);
+ fake_int_happened = TRUE;
+ DBG(DBG_INTR3, printk("eata_fake_int_handler called irq%d base %p"
+ " res %#x\n", irq, fake_int_base, fake_int_result));
+ return;
+}
+
+#ifdef MACH
+#include "eata_dma_proc.src"
+#else
+#include "eata_dma_proc.c"
+#endif
+
+#ifdef MODULE
+int eata_release(struct Scsi_Host *sh)
+{
+ uint i;
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq);
+ else reg_IRQ[sh->irq]--;
+
+ scsi_init_free((void *)status, 512);
+ scsi_init_free((void *)dma_scratch, 512);
+ for (i = 0; i < sh->can_queue; i++){ /* Free all SG arrays */
+ if(SD(sh)->ccb[i].sg_list != NULL)
+ scsi_init_free((void *) SD(sh)->ccb[i].sg_list,
+ sh->sg_tablesize * sizeof(struct eata_sg_list));
+ }
+
+ if (SD(sh)->channel == 0) {
+ if (sh->dma_channel != BUSMASTER) free_dma(sh->dma_channel);
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
+#endif
+
+
+void eata_int_handler(int irq, struct pt_regs * regs)
+{
+ uint i, result = 0;
+ uint hba_stat, scsi_stat, eata_stat;
+ Scsi_Cmnd *cmd;
+ struct eata_ccb *cp;
+ struct eata_sp *sp;
+ uint base;
+ ulong flags;
+ uint x;
+ struct Scsi_Host *sh;
+
+ save_flags(flags);
+ cli();
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->next) {
+ if (sh->irq != irq)
+ continue;
+
+ while(inb((uint)sh->base + HA_RAUXSTAT) & HA_AIRQ) {
+
+ int_counter++;
+
+ sp = &SD(sh)->sp;
+ cp = sp->ccb;
+
+ if(cp == NULL) {
+ eata_stat = inb((uint)sh->base + HA_RSTATUS);
+ printk("eata_dma: int_handler, Spurious IRQ %d "
+ "received. CCB pointer not set.\n", irq);
+ break;
+ }
+
+ cmd = cp->cmd;
+ base = (uint) cmd->host->base;
+ hba_stat = sp->hba_stat;
+
+ scsi_stat = (sp->scsi_stat >> 1) & 0x1f;
+
+ if (sp->EOC == FALSE) {
+ eata_stat = inb(base + HA_RSTATUS);
+ printk("eata_dma: int_handler, board: %x cmd %lx returned "
+ "unfinished.\nEATA: %x HBA: %x SCSI: %x spadr %lx "
+ "spadrirq %lx, irq%d\n", base, (long)cp, eata_stat,
+ hba_stat, scsi_stat,(long)&status, (long)&status[irq],
+ irq);
+ DBG(DBG_DELAY, DEL2(800));
+ break;
+ }
+
+ if (cp->status == LOCKED) {
+ cp->status = FREE;
+ eata_stat = inb(base + HA_RSTATUS);
+ printk("eata_dma: int_handler, freeing locked queueslot\n");
+ DBG(DBG_INTR && DBG_DELAY, DEL2(800));
+ break;
+ }
+
+ eata_stat = inb(base + HA_RSTATUS);
+ DBG(DBG_INTR, printk("IRQ %d received, base %#.4x, pid %ld, "
+ "target: %x, lun: %x, ea_s: %#.2x, hba_s: "
+ "%#.2x \n", irq, base, cmd->pid, cmd->target,
+ cmd->lun, eata_stat, hba_stat));
+
+ switch (hba_stat) {
+ case HA_NO_ERROR: /* NO Error */
+ if (scsi_stat == CONDITION_GOOD
+ && cmd->device->type == TYPE_DISK
+ && (HD(cmd)->t_state[cp->cp_channel][cp->cp_id] == RESET))
+ result = DID_BUS_BUSY << 16;
+ else if (scsi_stat == GOOD) {
+ HD(cmd)->t_state[cp->cp_channel][cp->cp_id] = OK;
+ if(HD(cmd)->do_latency == TRUE && cp->timestamp) {
+ uint time;
+ time = jiffies - cp->timestamp;
+ if((cp->rw_latency) == TRUE) { /* was WRITE */
+ if(HD(cmd)->writes_lat[cp->sizeindex][1] > time)
+ HD(cmd)->writes_lat[cp->sizeindex][1] = time;
+ if(HD(cmd)->writes_lat[cp->sizeindex][2] < time)
+ HD(cmd)->writes_lat[cp->sizeindex][2] = time;
+ HD(cmd)->writes_lat[cp->sizeindex][3] += time;
+ HD(cmd)->writes_lat[cp->sizeindex][0]++;
+ } else {
+ if(HD(cmd)->reads_lat[cp->sizeindex][1] > time)
+ HD(cmd)->reads_lat[cp->sizeindex][1] = time;
+ if(HD(cmd)->reads_lat[cp->sizeindex][2] < time)
+ HD(cmd)->reads_lat[cp->sizeindex][2] = time;
+ HD(cmd)->reads_lat[cp->sizeindex][3] += time;
+ HD(cmd)->reads_lat[cp->sizeindex][0]++;
+ }
+ }
+ }
+ else if (scsi_stat == CHECK_CONDITION
+ && cmd->device->type == TYPE_DISK
+ && (cmd->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ result = DID_BUS_BUSY << 16;
+ else
+ result = DID_OK << 16;
+ HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] = OK;
+ break;
+ case HA_ERR_SEL_TO: /* Selection Timeout */
+ result = DID_BAD_TARGET << 16;
+ break;
+ case HA_ERR_CMD_TO: /* Command Timeout */
+ if (HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id] > 1)
+ result = DID_ERROR << 16;
+ else {
+ result = DID_TIME_OUT << 16;
+ HD(cmd)->t_timeout[cp->cp_channel][cp->cp_id]++;
+ }
+ break;
+ case HA_ERR_RESET: /* SCSI Bus Reset Received */
+ case HA_INIT_POWERUP: /* Initial Controller Power-up */
+ if (cmd->device->type != TYPE_TAPE)
+ result = DID_BUS_BUSY << 16;
+ else
+ result = DID_ERROR << 16;
+
+ for (i = 0; i < MAXTARGET; i++)
+ HD(cmd)->t_state[cp->cp_channel][i] = RESET;
+ break;
+ case HA_UNX_BUSPHASE: /* Unexpected Bus Phase */
+ case HA_UNX_BUS_FREE: /* Unexpected Bus Free */
+ case HA_BUS_PARITY: /* Bus Parity Error */
+ case HA_SCSI_HUNG: /* SCSI Hung */
+ case HA_UNX_MSGRJCT: /* Unexpected Message Reject */
+ case HA_RESET_STUCK: /* SCSI Bus Reset Stuck */
+ case HA_RSENSE_FAIL: /* Auto Request-Sense Failed */
+ case HA_PARITY_ERR: /* Controller Ram Parity */
+ default:
+ result = DID_ERROR << 16;
+ break;
+ }
+ cmd->result = result | (scsi_stat << 1);
+
+#if DBG_INTR2
+ if (scsi_stat || result || hba_stat || eata_stat != 0x50
+ || cmd->scsi_done == NULL || cmd->device->id == 7)
+ printk("HBA: %d, channel %d, id: %d, lun %d, pid %ld:\n"
+ "eata_stat %#x, hba_stat %#.2x, scsi_stat %#.2x, "
+ "sense_key: %#x, result: %#.8x\n", x,
+ cmd->device->channel, cmd->device->id, cmd->device->lun,
+ cmd->pid, eata_stat, hba_stat, scsi_stat,
+ cmd->sense_buffer[2] & 0xf, cmd->result);
+ DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+#endif
+
+ cp->status = FREE; /* now we can release the slot */
+ cmd->scsi_done(cmd);
+ }
+ }
+ restore_flags(flags);
+
+ return;
+}
+
+inline int eata_send_command(u32 addr, u32 base, u8 command)
+{
+ long loop = R_LIMIT;
+
+ while (inb(base + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0)
+ return(FALSE);
+
+ /* And now the address in nice little byte chunks */
+ outb( addr & 0x000000ff, base + HA_WDMAADDR);
+ outb((addr & 0x0000ff00) >> 8, base + HA_WDMAADDR + 1);
+ outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 2);
+ outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR + 3);
+ outb(command, base + HA_WCOMMAND);
+ return(TRUE);
+}
+
+#if 0
+inline int eata_send_immediate(u32 addr, u32 base, u8 cmnd, u8 cmnd2, u8 id,
+ u8 lun)
+{
+ if(addr){
+ outb( addr & 0x000000ff, base + HA_WDMAADDR);
+ outb((addr & 0x0000ff00) >> 8, base + HA_WDMAADDR + 1);
+ outb((addr & 0x00ff0000) >> 16, base + HA_WDMAADDR + 2);
+ outb((addr & 0xff000000) >> 24, base + HA_WDMAADDR + 3);
+ } else {
+ outb(id, base + HA_WSUBCODE);
+ outb(lun, base + HA_WSUBLUN);
+ }
+
+ outb(cmnd2, base + HA_WCOMMAND2);
+ outb(cmnd, base + HA_WCOMMAND);
+ return(TRUE);
+}
+#endif
+
+int eata_queue(Scsi_Cmnd * cmd, void (* done) (Scsi_Cmnd *))
+{
+ unsigned int i, x, y;
+ u32 flags;
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *cp;
+ struct scatterlist *sl;
+
+ save_flags(flags);
+ cli();
+
+ queue_counter++;
+
+ hd = HD(cmd);
+ sh = cmd->host;
+
+ /* check for free slot */
+ for (y = hd->last_ccb + 1, x = 0; x < sh->can_queue; x++, y++) {
+ if (y >= sh->can_queue)
+ y = 0;
+ if (hd->ccb[y].status == FREE)
+ break;
+ }
+
+ hd->last_ccb = y;
+
+ if (x >= sh->can_queue) {
+ uint z;
+
+ printk(KERN_EMERG "eata_dma: run out of queue slots cmdno:%ld"
+ " intrno: %ld, can_queue: %d, x: %d, y: %d\n",
+ queue_counter, int_counter, sh->can_queue, x, y);
+ printk(KERN_EMERG "Status of queueslots:");
+ for(z = 0; z < sh->can_queue; z +=2) {
+ switch(hd->ccb[z].status) {
+ case FREE:
+ printk(KERN_EMERG "Slot %2d is FREE \t", z);
+ break;
+ case USED:
+ printk(KERN_EMERG "Slot %2d is USED \t", z);
+ break;
+ case LOCKED:
+ printk(KERN_EMERG "Slot %2d is LOCKED\t", z);
+ break;
+ default:
+ printk(KERN_EMERG "Slot %2d is UNKNOWN\t", z);
+ }
+ panic("\nSystem halted.\n");
+ }
+ }
+ cp = &hd->ccb[y];
+
+ memset(cp, 0, sizeof(struct eata_ccb) - sizeof(struct eata_sg_list *));
+
+ cp->status = USED; /* claim free slot */
+
+ DBG(DBG_QUEUE, printk("eata_queue pid %ld, target: %x, lun: %x, y %d\n",
+ cmd->pid, cmd->target, cmd->lun, y));
+ DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
+
+ if(hd->do_latency == TRUE) {
+ int x, z;
+ short *sho;
+ long *lon;
+ x = 0; /* just to keep GCC quiet */
+ if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 ||
+ cmd->cmnd[0] == WRITE_12 || cmd->cmnd[0] == READ_6 ||
+ cmd->cmnd[0] == READ_10 || cmd->cmnd[0] == READ_12) {
+
+ cp->timestamp = jiffies; /* For latency measurements */
+ switch(cmd->cmnd[0]) {
+ case WRITE_6:
+ case READ_6:
+ x = cmd->cmnd[4]/2;
+ break;
+ case WRITE_10:
+ case READ_10:
+ sho = (short *) &cmd->cmnd[7];
+ x = ntohs(*sho)/2;
+ break;
+ case WRITE_12:
+ case READ_12:
+ lon = (long *) &cmd->cmnd[6];
+ x = ntohl(*lon)/2;
+ break;
+ }
+
+ for(z = 0; (x > (1 << z)) && (z <= 11); z++)
+ /* nothing */;
+ cp->sizeindex = z;
+ if (cmd->cmnd[0] == WRITE_6 || cmd->cmnd[0] == WRITE_10 ||
+ cmd->cmnd[0] == WRITE_12){
+ cp->rw_latency = TRUE;
+ }
+ }
+ }
+ cmd->scsi_done = (void *)done;
+
+ switch (cmd->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea: /* alternate number for WRITE LONG */
+ cp->DataOut = TRUE; /* Output mode */
+ break;
+ case TEST_UNIT_READY:
+ default:
+ cp->DataIn = TRUE; /* Input mode */
+ }
+
+ /* FIXME: This will will have to be changed once the midlevel driver
+ * allows different HBA IDs on every channel.
+ */
+ if (cmd->target == sh->this_id)
+ cp->Interpret = TRUE; /* Interpret command */
+
+ if (cmd->use_sg) {
+ cp->scatter = TRUE; /* SG mode */
+ if (cp->sg_list == NULL) {
+ cp->sg_list = kmalloc(sh->sg_tablesize * sizeof(struct eata_sg_list),
+ GFP_ATOMIC | GFP_DMA);
+ }
+ if (cp->sg_list == NULL)
+ panic("eata_dma: Run out of DMA memory for SG lists !\n");
+ cp->cp_dataDMA = htonl(virt_to_bus(cp->sg_list));
+
+ cp->cp_datalen = htonl(cmd->use_sg * sizeof(struct eata_sg_list));
+ sl=(struct scatterlist *)cmd->request_buffer;
+ for(i = 0; i < cmd->use_sg; i++, sl++){
+ cp->sg_list[i].data = htonl(virt_to_bus(sl->address));
+ cp->sg_list[i].len = htonl((u32) sl->length);
+ }
+ } else {
+ cp->scatter = FALSE;
+ cp->cp_datalen = htonl(cmd->request_bufflen);
+ cp->cp_dataDMA = htonl(virt_to_bus(cmd->request_buffer));
+ }
+
+ cp->Auto_Req_Sen = TRUE;
+ cp->cp_reqDMA = htonl(virt_to_bus(cmd->sense_buffer));
+ cp->reqlen = sizeof(cmd->sense_buffer);
+
+ cp->cp_id = cmd->target;
+ cp->cp_channel = cmd->channel;
+ cp->cp_lun = cmd->lun;
+ cp->cp_dispri = TRUE;
+ cp->cp_identify = TRUE;
+ memcpy(cp->cp_cdb, cmd->cmnd, cmd->cmd_len);
+
+ cp->cp_statDMA = htonl(virt_to_bus(&(hd->sp)));
+
+ cp->cp_viraddr = cp; /* This will be passed thru, so we don't need to
+ * convert it */
+ cp->cmd = cmd;
+ cmd->host_scribble = (char *)&hd->ccb[y];
+
+ if(eata_send_command((u32) cp, (u32) sh->base, EATA_CMD_DMA_SEND_CP) == FALSE) {
+ cmd->result = DID_BUS_BUSY << 16;
+ DBG(DBG_QUEUE && DBG_ABNORM,
+ printk("eata_queue target %d, pid %ld, HBA busy, "
+ "returning DID_BUS_BUSY\n",cmd->target, cmd->pid));
+ done(cmd);
+ cp->status = FREE;
+ restore_flags(flags);
+ return(0);
+ }
+ DBG(DBG_QUEUE, printk("Queued base %#.4x pid: %ld target: %x lun: %x "
+ "slot %d irq %d\n", (s32)sh->base, cmd->pid,
+ cmd->target, cmd->lun, y, sh->irq));
+ DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
+ restore_flags(flags);
+ return(0);
+}
+
+
+int eata_abort(Scsi_Cmnd * cmd)
+{
+ ulong loop = R_LIMIT;
+ ulong flags;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_abort called pid: %ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY) {
+ if (--loop == 0) {
+ printk("eata_dma: abort, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_ERROR);
+ }
+ }
+ if (CD(cmd)->status == RESET) {
+ restore_flags(flags);
+ printk("eata_dma: abort, command reset error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == LOCKED) {
+ restore_flags(flags);
+ DBG(DBG_ABNORM, printk("eata_dma: abort, queue slot locked.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_BUSY); /* SNOOZE */
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ restore_flags(flags);
+ panic("eata_dma: abort: invalid slot status\n");
+}
+
+int eata_reset(Scsi_Cmnd * cmd)
+{
+ ushort x, z;
+ ulong time, limit = 0;
+ ulong loop = R_LIMIT;
+ ulong flags;
+ unchar success = FALSE;
+ Scsi_Cmnd *sp;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_reset called pid:%ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+
+ if (HD(cmd)->state == RESET) {
+ printk("eata_reset: exit, already in reset.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_ERROR);
+ }
+
+ while (inb((u32)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk("eata_reset: exit, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_ERROR);
+ }
+
+ for (x = 0; x < MAXCHANNEL; x++) {
+ for (z = 0; z < MAXTARGET; z++) {
+ HD(cmd)->t_state[x][z] = RESET;
+ HD(cmd)->t_timeout[x][z] = NO_TIMEOUT;
+ }
+ }
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ if (HD(cmd)->ccb[x].status == LOCKED) {
+ HD(cmd)->ccb[x].status = FREE;
+ printk("eata_reset: locked slot %d forced free.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ continue;
+ }
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+ printk("eata_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ if (sp == NULL)
+ panic("eata_reset: slot %d, sp==NULL.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ if (sp == cmd)
+ success = TRUE;
+ }
+
+ /* hard reset the HBA */
+ inb((u32) (cmd->host->base) + HA_RSTATUS); /* This might cause trouble */
+ eata_send_command(0, (u32) cmd->host->base, EATA_CMD_RESET);
+
+ DBG(DBG_ABNORM, printk("eata_reset: board reset done, enabling interrupts.\n"));
+ HD(cmd)->state = RESET;
+
+ restore_flags(flags);
+
+ time = jiffies;
+ while (jiffies < (time + (3 * HZ)) || limit++ < 10000000)
+ /* As time goes by... */;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_reset: interrupts disabled, loops %ld.\n",
+ limit));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(cmd)->ccb[x].status = LOCKED;
+
+ printk("eata_reset: slot %d locked, DID_RESET, pid %ld done.\n",
+ x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ restore_flags(flags);
+ sp->scsi_done(sp);
+ cli();
+ }
+
+ HD(cmd)->state = FALSE;
+ restore_flags(flags);
+
+ if (success) {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, success.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_SUCCESS);
+ } else {
+ DBG(DBG_ABNORM, printk("eata_reset: exit, wakeup.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_PUNT);
+ }
+}
+
+char * get_board_data(u32 base, u32 irq, u32 id)
+{
+ struct eata_ccb *cp;
+ struct eata_sp *sp;
+ static char *buff;
+ ulong i;
+ ulong limit = 0;
+
+ cp = (struct eata_ccb *) scsi_init_malloc(sizeof(struct eata_ccb),
+ GFP_ATOMIC | GFP_DMA);
+ sp = (struct eata_sp *) scsi_init_malloc(sizeof(struct eata_sp),
+ GFP_ATOMIC | GFP_DMA);
+
+ buff = dma_scratch;
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+ memset(sp, 0, sizeof(struct eata_sp));
+ memset(buff, 0, 256);
+
+ cp->DataIn = TRUE;
+ cp->Interpret = TRUE; /* Interpret command */
+ cp->cp_dispri = TRUE;
+ cp->cp_identify = TRUE;
+
+ cp->cp_datalen = htonl(56);
+ cp->cp_dataDMA = htonl(virt_to_bus(buff));
+ cp->cp_statDMA = htonl(virt_to_bus(sp));
+ cp->cp_viraddr = cp;
+
+ cp->cp_id = id;
+ cp->cp_lun = 0;
+
+ cp->cp_cdb[0] = INQUIRY;
+ cp->cp_cdb[1] = 0;
+ cp->cp_cdb[2] = 0;
+ cp->cp_cdb[3] = 0;
+ cp->cp_cdb[4] = 56;
+ cp->cp_cdb[5] = 0;
+
+ fake_int_base = (struct eata_register *) base;
+ fake_int_result = FALSE;
+ fake_int_happened = FALSE;
+
+ eata_send_command((u32) cp, (u32) base, EATA_CMD_DMA_SEND_CP);
+
+ i = jiffies + (3 * HZ);
+ while (fake_int_happened == FALSE && jiffies <= i)
+ barrier();
+
+ DBG(DBG_INTR3, printk("fake_int_result: %#x hbastat %#x scsistat %#x,"
+ " buff %p sp %p\n",
+ fake_int_result, (u32) (sp->hba_stat /*& 0x7f*/),
+ (u32) sp->scsi_stat, buff, sp));
+
+ scsi_init_free((void *)cp, sizeof(struct eata_ccb));
+ scsi_init_free((void *)sp, sizeof(struct eata_sp));
+
+ if ((fake_int_result & HA_SERROR) || jiffies > i){
+ /* hard reset the HBA */
+ inb((u32) (base) + HA_RSTATUS);
+ eata_send_command(0, base, EATA_CMD_RESET);
+ i = jiffies;
+ while (jiffies < (i + (3 * HZ)) && limit++ < 10000000)
+ barrier();
+ return (NULL);
+ } else
+ return (buff);
+}
+
+int check_blink_state(long base)
+{
+ ushort loops = 10;
+ u32 blinkindicator;
+ u32 state = 0x12345678;
+ u32 oldstate = 0;
+
+ blinkindicator = htonl(0x54504442);
+ while ((loops--) && (state != oldstate)) {
+ oldstate = state;
+ state = inl((uint) base + 1);
+ }
+
+ DBG(DBG_BLINK, printk("Did Blink check. Status: %d\n",
+ (state == oldstate) && (state == blinkindicator)));
+
+ if ((state == oldstate) && (state == blinkindicator))
+ return(TRUE);
+ else
+ return (FALSE);
+}
+
+int get_conf_PIO(u32 base, struct get_conf *buf)
+{
+ ulong loop = R_LIMIT;
+ u16 *p;
+
+ if(check_region(base, 9))
+ return (FALSE);
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return (FALSE);
+
+ DBG(DBG_PIO && DBG_PROBE,
+ printk("Issuing PIO READ CONFIG to HBA at %#x\n", base));
+ eata_send_command(0, base, EATA_CMD_PIO_READ_CONFIG);
+
+ loop = R_LIMIT;
+ for (p = (u16 *) buf;
+ (long)p <= ((long)buf + (sizeof(struct get_conf) / 2)); p++) {
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ return (FALSE);
+
+ loop = R_LIMIT;
+ *p = inw(base + HA_RDATA);
+ }
+
+ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { /* Error ? */
+ if (htonl(EATA_SIGNATURE) == buf->signature) {
+ DBG(DBG_PIO&&DBG_PROBE, printk("EATA Controller found at %x "
+ "EATA Level: %x\n", (uint) base,
+ (uint) (buf->version)));
+
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+ return (TRUE);
+ }
+ } else {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during transfer "
+ "for HBA at %lx\n", (long)base));
+ }
+ return (FALSE);
+}
+
+void print_config(struct get_conf *gc)
+{
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d DMAS:%d\n",
+ (u32) ntohl(gc->len), gc->version,
+ gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support,
+ gc->DMA_support);
+ printk("DMAV:%d HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n",
+ gc->DMA_valid, gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2],
+ gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d DMAC:%d FORCADR:%d SG_64K:%d SG_UAE:%d MID:%d "
+ "MCH:%d MLUN:%d\n",
+ gc->IRQ, gc->IRQ_TR, (8 - gc->DMA_channel) & 7, gc->FORCADR,
+ gc->SG_64K, gc->SG_UAE, gc->MAX_ID, gc->MAX_CHAN, gc->MAX_LUN);
+ printk("RIDQ:%d PCI:%d EISA:%d\n",
+ gc->ID_qest, gc->is_PCI, gc->is_EISA);
+ DBG(DPT_DEBUG, DELAY(14));
+}
+
+short register_HBA(u32 base, struct get_conf *gc, Scsi_Host_Template * tpnt,
+ u8 bustype)
+{
+ ulong size = 0;
+ unchar dma_channel = 0;
+ char *buff = 0;
+ unchar bugs = 0;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+ int x;
+
+
+ DBG(DBG_REGISTER, print_config(gc));
+
+ if (gc->DMA_support == FALSE) {
+ printk("The EATA HBA at %#.4x does not support DMA.\n"
+ "Please use the EATA-PIO driver.\n", base);
+ return (FALSE);
+ }
+ if(gc->HAA_valid == FALSE || ntohl(gc->len) < 0x22)
+ gc->MAX_CHAN = 0;
+
+ if (reg_IRQ[gc->IRQ] == FALSE) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, (void *) eata_fake_int_handler, SA_INTERRUPT,
+ "eata_dma")){
+ reg_IRQ[gc->IRQ]++;
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.", gc->IRQ);
+ return (FALSE);
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ] == TRUE) {
+ printk("Can't support more than one HBA on this IRQ,\n"
+ " if the IRQ is edge triggered. Sorry.\n");
+ return (FALSE);
+ } else
+ reg_IRQ[gc->IRQ]++;
+ }
+
+ /* if gc->DMA_valid it must be an ISA HBA and we have to register it */
+ dma_channel = BUSMASTER;
+ if (gc->DMA_valid) {
+ if (request_dma(dma_channel = (8 - gc->DMA_channel) & 7, "eata_dma")) {
+ printk("Unable to allocate DMA channel %d for ISA HBA at %#.4x.\n",
+ dma_channel, base);
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+ }
+
+#if !(NEWSTUFF)
+ if (bustype != IS_EISA && bustype != IS_ISA)
+#endif
+ buff = get_board_data(base, gc->IRQ, gc->scsi_id[3]);
+
+ if (buff == NULL) {
+#if !(NEWSTUFF)
+ if (bustype == IS_EISA || bustype == IS_ISA) {
+ bugs = bugs || BROKEN_INQUIRY;
+ } else {
+#endif
+ if (gc->DMA_support == FALSE)
+ printk("HBA at %#.4x doesn't support DMA. Sorry\n", base);
+ else
+ printk("HBA at %#.4x does not react on INQUIRY. Sorry.\n",
+ base);
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+#if !(NEWSTUFF)
+ }
+#endif
+ }
+
+ if (gc->DMA_support == FALSE && buff != NULL)
+ printk("HBA %.12sat %#.4x doesn't set the DMA_support flag correctly.\n",
+ &buff[16], base);
+
+ request_region(base, 9, "eata_dma"); /* We already checked the
+ * availability, so this
+ * should not fail.
+ */
+
+ if(ntohs(gc->queuesiz) == 0) {
+ gc->queuesiz = ntohs(64);
+ printk("Warning: Queue size has to be corrected. Assuming 64 queueslots\n"
+ " This might be a PM2012B with a defective Firmware\n");
+ }
+
+ size = sizeof(hostdata) + ((sizeof(struct eata_ccb) + sizeof(long))
+ * ntohs(gc->queuesiz));
+
+ DBG(DBG_REGISTER, printk("scsi_register size: %ld\n", size));
+
+ sh = scsi_register(tpnt, size);
+
+ if(sh == NULL) {
+ if (gc->DMA_valid)
+ free_dma(dma_channel);
+
+ reg_IRQ[gc->IRQ]--;
+ if (reg_IRQ[gc->IRQ] == 0)
+ free_irq(gc->IRQ);
+ if (gc->IRQ_TR == FALSE)
+ reg_IRQL[gc->IRQ] = FALSE;
+ return (FALSE);
+ }
+
+ hd = SD(sh);
+
+ memset(hd->ccb, 0, sizeof(struct eata_ccb) * ntohs(gc->queuesiz));
+ memset(hd->reads, 0, sizeof(u32) * 26);
+
+ hd->broken_INQUIRY = (bugs & BROKEN_INQUIRY);
+
+ if(hd->broken_INQUIRY == TRUE) {
+ strcpy(SD(sh)->vendor, "DPT");
+ strcpy(SD(sh)->name, "??????????");
+ strcpy(SD(sh)->revision, "???.?");
+ } else {
+ strncpy(SD(sh)->vendor, &buff[8], 8);
+ SD(sh)->vendor[8] = 0;
+ strncpy(SD(sh)->name, &buff[16], 17);
+ SD(sh)->name[17] = 0;
+ SD(sh)->revision[0] = buff[32];
+ SD(sh)->revision[1] = buff[33];
+ SD(sh)->revision[2] = buff[34];
+ SD(sh)->revision[3] = '.';
+ SD(sh)->revision[4] = buff[35];
+ SD(sh)->revision[5] = 0;
+ }
+
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ SD(sh)->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ SD(sh)->EATA_revision = 'b';
+ break;
+ case 0x22:
+ SD(sh)->EATA_revision = 'c';
+ break;
+ case 0x24:
+ SD(sh)->EATA_revision = 'z';
+ default:
+ SD(sh)->EATA_revision = '?';
+ }
+
+ if(ntohl(gc->len) >= 0x22) {
+ if (gc->is_PCI == TRUE)
+ hd->bustype = IS_PCI;
+ else if (gc->is_EISA == TRUE)
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ } else if(hd->broken_INQUIRY == FALSE) {
+ if (buff[21] == '4')
+ hd->bustype = IS_PCI;
+ else if (buff[21] == '2')
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ } else
+ hd->bustype = bustype;
+
+ if(ntohl(gc->len) >= 0x22) {
+ sh->max_id = gc->MAX_ID + 1;
+ sh->max_lun = gc->MAX_LUN + 1;
+ } else {
+ sh->max_id = 8;
+ sh->max_lun = 8;
+ }
+
+ hd->channel = gc->MAX_CHAN;
+ sh->max_channel = gc->MAX_CHAN;
+ sh->unique_id = base;
+ sh->base = (char *) base;
+ sh->io_port = base;
+ sh->n_io_port = 9;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = dma_channel;
+
+ /* FIXME:
+ * SCSI midlevel code should support different HBA ids on every channel
+ */
+ sh->this_id = gc->scsi_id[3];
+ sh->can_queue = ntohs(gc->queuesiz);
+
+ if (gc->OCS_enabled == TRUE) {
+ if(hd->bustype != IS_ISA)
+ sh->cmd_per_lun = sh->can_queue/C_P_L_DIV;
+ else
+ sh->cmd_per_lun = 8; /* We artificially limit this to conserve
+ * memory, which would be needed for ISA
+ * bounce buffers */
+ } else
+ sh->cmd_per_lun = 1;
+
+ /* FIXME:
+ * SG should be allocated more dynamically
+ */
+ /*
+ * If we are using a ISA board, we can't use extended SG,
+ * because we would need exessive amounts of memory for
+ * bounce buffers.
+ */
+ if (gc->SG_64K == TRUE && ntohs(gc->SGsiz) == 64 && hd->bustype != IS_ISA){
+ sh->sg_tablesize = SG_SIZE_BIG;
+ sh->use_clustering = FALSE;
+ } else {
+ sh->sg_tablesize = ntohs(gc->SGsiz);
+ sh->use_clustering = TRUE;
+ if (sh->sg_tablesize > SG_SIZE || sh->sg_tablesize == 0) {
+ sh->sg_tablesize = SG_SIZE;
+ if (ntohs(gc->SGsiz) == 0)
+ printk("Warning: SG size had to be corrected.\n"
+ "This might be a PM2012 with a defective Firmware\n");
+ }
+ }
+
+ if (gc->SECOND)
+ hd->primary = FALSE;
+ else
+ hd->primary = TRUE;
+
+ sh->wish_block = FALSE;
+
+ if (hd->bustype != IS_ISA) {
+ sh->unchecked_isa_dma = FALSE;
+ } else {
+ sh->unchecked_isa_dma = TRUE; /* We're doing ISA DMA */
+ }
+
+ for(x = 0; x <= 11; x++){ /* Initialize min. latency */
+ hd->writes_lat[x][1] = 0xffffffff;
+ hd->reads_lat[x][1] = 0xffffffff;
+ }
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if(hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+
+ return (TRUE);
+}
+
+
+void find_EISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ u32 base;
+ int i;
+
+#if CHECKPAL
+ u8 pal1, pal2, pal3;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i] == TRUE) { /* Still a possibility ? */
+
+ base = 0x1c88 + (i * 0x1000);
+#if CHECKPAL
+ pal1 = inb((u16)base - 8);
+ pal2 = inb((u16)base - 7);
+ pal3 = inb((u16)base - 6);
+
+ if (((pal1 == DPT_ID1) && (pal2 == DPT_ID2)) ||
+ ((pal1 == NEC_ID1) && (pal2 == NEC_ID2) && (pal3 == NEC_ID3))||
+ ((pal1 == ATT_ID1) && (pal2 == ATT_ID2) && (pal3 == ATT_ID3))){
+ DBG(DBG_PROBE, printk("EISA EATA id tags found: %x %x %x \n",
+ (int)pal1, (int)pal2, (int)pal3));
+#endif
+ if (get_conf_PIO(base, buf) == TRUE) {
+ if (buf->IRQ) {
+ DBG(DBG_EISA, printk("Registering EISA HBA\n"));
+ register_HBA(base, buf, tpnt, IS_EISA);
+ } else
+ printk("eata_dma: No valid IRQ. HBA removed from list\n");
+ } else {
+ if (check_blink_state(base))
+ printk("HBA is in BLINK state. Consult your HBAs "
+ "Manual to correct this.\n");
+ }
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#if CHECKPAL
+ }
+#endif
+ }
+ }
+ return;
+}
+
+void find_ISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ int i;
+
+ for (i = 0; i < MAXISA; i++) {
+ if (ISAbases[i]) {
+ if (get_conf_PIO(ISAbases[i],buf) == TRUE){
+ DBG(DBG_ISA, printk("Registering ISA HBA\n"));
+ register_HBA(ISAbases[i], buf, tpnt, IS_ISA);
+ } else {
+ if (check_blink_state(ISAbases[i]))
+ printk("HBA is in BLINK state. Consult your HBAs "
+ "Manual to correct this.\n");
+ }
+ ISAbases[i] = 0;
+ }
+ }
+ return;
+}
+
+void find_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+
+#ifndef CONFIG_PCI
+ printk("eata_dma: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+#else
+
+ u8 pci_bus, pci_device_fn;
+ static s16 pci_index = 0; /* Device index to PCI BIOS calls */
+ u32 base = 0;
+ u16 com_adr;
+ u16 rev_device;
+ u32 error, i, x;
+ u8 pal1, pal2, pal3;
+
+ if (pcibios_present()) {
+ for (i = 0; i <= MAXPCI; ++i, ++pci_index) {
+ if (pcibios_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT,
+ pci_index, &pci_bus, &pci_device_fn))
+ break;
+ DBG(DBG_PROBE && DBG_PCI,
+ printk("eata_dma: find_PCI, HBA at bus %d, device %d,"
+ " function %d, index %d\n", (s32)pci_bus,
+ (s32)((pci_device_fn & 0xf8) >> 3),
+ (s32)(pci_device_fn & 7), pci_index));
+
+ if (!(error = pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_CLASS_DEVICE, &rev_device))) {
+ if (rev_device == PCI_CLASS_STORAGE_SCSI) {
+ if (!(error = pcibios_read_config_word(pci_bus,
+ pci_device_fn, PCI_COMMAND,
+ (u16 *) & com_adr))) {
+ if (!((com_adr & PCI_COMMAND_IO) &&
+ (com_adr & PCI_COMMAND_MASTER))) {
+ printk("eata_dma: find_PCI, HBA has IO or BUSMASTER mode disabled\n");
+ continue;
+ }
+ } else
+ printk("eata_dma: find_PCI, error %x while reading "
+ "PCI_COMMAND\n", error);
+ } else
+ printk("eata_dma: find_PCI, DEVICECLASSID %x didn't match\n",
+ rev_device);
+ } else {
+ printk("eata_dma: find_PCI, error %x while reading PCI_CLASS_BASE\n",
+ error);
+ continue;
+ }
+
+ if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, (int *) &base))){
+
+ /* Check if the address is valid */
+ if (base & 0x01) {
+ base &= 0xfffffffe;
+ /* EISA tag there ? */
+ pal1 = inb(base);
+ pal2 = inb(base + 1);
+ pal3 = inb(base + 2);
+ if (((pal1 == DPT_ID1) && (pal2 == DPT_ID2)) ||
+ ((pal1 == NEC_ID1) && (pal2 == NEC_ID2) &&
+ (pal3 == NEC_ID3)) ||
+ ((pal1 == ATT_ID1) && (pal2 == ATT_ID2) &&
+ (pal3 == ATT_ID3)))
+ base += 0x08;
+ else
+ base += 0x10; /* Now, THIS is the real address */
+
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_conf_PIO(base, buf) == TRUE) {
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+ DBG(DBG_PCI, printk("Registering PCI HBA\n"));
+ register_HBA(base, buf, tpnt, IS_PCI);
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88)
+ EISAbases[(base >> 12) & 0x0f] = 0;
+ continue; /* break; */
+ } else if (check_blink_state(base) == TRUE) {
+ printk("eata_dma: HBA is in BLINK state.\n"
+ "Consult your HBAs Manual to correct this.\n");
+ }
+ }
+ }
+ } else {
+ printk("eata_dma: error %x while reading "
+ "PCI_BASE_ADDRESS_0\n", error);
+ }
+ }
+ } else {
+ printk("eata_dma: No BIOS32 extensions present. This driver release "
+ "still depends on it.\n"
+ " Skipping scan for PCI HBAs. \n");
+ }
+#endif /* #ifndef CONFIG_PCI */
+ return;
+}
+
+int eata_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ int i;
+
+ DBG((DBG_PROBE && DBG_DELAY) || DPT_DEBUG,
+ printk("Using lots of delays to let you read the debugging output\n"));
+
+ tpnt->proc_dir = &proc_scsi_eata_dma;
+
+ status = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+ dma_scratch = scsi_init_malloc(512, GFP_ATOMIC | GFP_DMA);
+
+ if(status == NULL || dma_scratch == NULL) {
+ printk("eata_dma: can't allocate enough memory to probe for hosts !\n");
+ return(0);
+ }
+
+ find_PCI(&gc, tpnt);
+
+ find_EISA(&gc, tpnt);
+
+ find_ISA(&gc, tpnt);
+
+ for (i = 0; i <= MAXIRQ; i++) { /* Now that we know what we have, we */
+ if (reg_IRQ[i]){ /* exchange the interrupt handler which */
+ free_irq(i); /* we used for probing with the real one */
+ request_irq(i, (void *)(eata_int_handler), SA_INTERRUPT, "eata_dma");
+ }
+ }
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) driver version: %d.%d%s\n"
+ "developed in co-operation with DPT\n"
+ "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n",
+ VER_MAJOR, VER_MINOR, VER_SUB);
+ printk("Registered HBAs:");
+ printk("\nHBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: DMA: Ch: "
+ "ID: Pr: QS: SG: CPL:\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4x %2d",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (u32) HBA_ptr->base, HBA_ptr->irq);
+ if(HBA_ptr->dma_channel != BUSMASTER)
+ printk(" %2x ", HBA_ptr->dma_channel);
+ else
+ printk(" %s", "BMST");
+ printk(" %d %d %c %2d %2d %2d\n", SD(HBA_ptr)->channel,
+ HBA_ptr->this_id, (SD(HBA_ptr)->primary == TRUE)?'Y':'N',
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize, HBA_ptr->cmd_per_lun);
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ } else {
+ scsi_init_free((void *)status, 512);
+ }
+
+ scsi_init_free((void *)dma_scratch, 512);
+
+ DBG(DPT_DEBUG, DELAY(12));
+
+ return(registered_HBAs);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_DMA;
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost 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
+ * tab-width: 8
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/scsi/eata_dma.h b/i386/i386at/gpl/linux/scsi/eata_dma.h
new file mode 100644
index 00000000..41504673
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_dma.h
@@ -0,0 +1,119 @@
+/********************************************************
+* Header file for eata_dma.c Linux EATA-DMA SCSI driver *
+* (c) 1993,94,95 Michael Neuffer *
+*********************************************************
+* last change: 95/07/18 *
+********************************************************/
+
+
+#ifndef _EATA_DMA_H
+#define _EATA_DMA_H
+
+#ifndef HOSTS_C
+
+#include "eata_generic.h"
+
+
+#define VER_MAJOR 2
+#define VER_MINOR 5
+#define VER_SUB "8a"
+
+
+/************************************************************************
+ * Here you can switch parts of the code on and of *
+ ************************************************************************/
+
+#define CHECKPAL 0 /* EISA pal checking on/off */
+#define NEWSTUFF 0 /* Some changes for ISA/EISA boards */
+
+/************************************************************************
+ * Debug options. *
+ * Enable DEBUG and whichever options you require. *
+ ************************************************************************/
+#define DEBUG_EATA 1 /* Enable debug code. */
+#define DPT_DEBUG 0 /* Bobs special */
+#define DBG_DELAY 0 /* Build in delays so debug messages can be
+ * be read before they vanish of the top of
+ * the screen! */
+#define DBG_PROBE 0 /* Debug probe routines. */
+#define DBG_PCI 0 /* Trace PCI routines */
+#define DBG_EISA 0 /* Trace EISA routines */
+#define DBG_ISA 0 /* Trace ISA routines */
+#define DBG_BLINK 0 /* Trace Blink check */
+#define DBG_PIO 0 /* Trace get_config_PIO */
+#define DBG_COM 0 /* Trace command call */
+#define DBG_QUEUE 0 /* Trace command queueing. */
+#define DBG_QUEUE2 0 /* Trace command queueing SG. */
+#define DBG_INTR 0 /* Trace interrupt service routine. */
+#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_INTR3 0 /* Trace get_board_data interrupts. */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
+#define DBG_PROC_WRITE 0
+#define DBG_REGISTER 0 /* */
+#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort)*/
+
+#if DEBUG_EATA
+#define DBG(x, y) if ((x)) {y;}
+#else
+#define DBG(x, y)
+#endif
+
+#endif /* !HOSTS_C */
+
+int eata_detect(Scsi_Host_Template *);
+const char *eata_info(struct Scsi_Host *);
+int eata_command(Scsi_Cmnd *);
+int eata_queue(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int eata_abort(Scsi_Cmnd *);
+int eata_reset(Scsi_Cmnd *);
+int eata_proc_info(char *, char **, off_t, int, int, int);
+#ifdef MODULE
+int eata_release(struct Scsi_Host *);
+#else
+#define eata_release NULL
+#endif
+
+#include <linux/scsicam.h>
+
+#define EATA_DMA { \
+ NULL, NULL, \
+ NULL, /* proc_dir_entry */ \
+ eata_proc_info, /* procinfo */ \
+ "EATA (Extended Attachment) HBA driver", \
+ eata_detect, \
+ eata_release, \
+ NULL, NULL, \
+ eata_queue, \
+ eata_abort, \
+ eata_reset, \
+ NULL, /* Slave attach */ \
+ scsicam_bios_param, \
+ 0, /* Canqueue */ \
+ 0, /* this_id */ \
+ 0, /* sg_tablesize */ \
+ 0, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 1, /* True if ISA */ \
+ ENABLE_CLUSTERING }
+
+
+#endif /* _EATA_DMA_H */
+
+/*
+ * Overrides for Emacs so that we almost 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/i386/i386at/gpl/linux/scsi/eata_dma_proc.h b/i386/i386at/gpl/linux/scsi/eata_dma_proc.h
new file mode 100644
index 00000000..d49f348e
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_dma_proc.h
@@ -0,0 +1,260 @@
+
+struct lun_map {
+ __u8 id:5,
+ chan:3;
+ __u8 lun;
+};
+
+typedef struct emul_pp {
+ __u8 p_code:6,
+ null:1,
+ p_save:1;
+ __u8 p_length;
+ __u16 cylinder;
+ __u8 heads;
+ __u8 sectors;
+ __u8 null2;
+ __u8 s_lunmap:4,
+ ems:1;
+ __u16 drive_type; /* In Little Endian ! */
+ struct lun_map lunmap[4];
+}emulpp;
+
+
+/* Log Sense pages */
+
+typedef struct log_sheader {
+ __u8 page_code,
+ reserved;
+ __u16 length;
+}logsh;
+
+
+/* Log Sense Statistics */
+
+typedef struct read_command_statistics {
+ __u16 code; /* 0x01 */
+ __u8 flags;
+ __u8 length; /* 0x24 */
+ __u32 h_commands,
+ uncached,
+ la_cmds,
+ la_blks,
+ la_hits,
+ missed,
+ hits,
+ seq_la_blks,
+ seq_la_hits;
+}r_cmd_stat;
+
+typedef struct write_command_statistics {
+ __u16 code; /* 0x03 */
+ __u8 flags;
+ __u8 length; /* 0x28 */
+ __u32 h_commands,
+ uncached,
+ thru,
+ bypass,
+ soft_err,
+ hits,
+ b_idle,
+ b_activ,
+ b_blks,
+ b_blks_clean;
+}w_cmd_stat;
+
+typedef struct host_command_statistics {
+ __u16 code; /* 0x02, 0x04 */
+ __u8 flags;
+ __u8 length; /* 0x30 */
+ __u32 sizes[12];
+}hst_cmd_stat;
+
+typedef struct physical_command_statistics {
+ __u16 code; /* 0x06, 0x07 */
+ __u8 flags;
+ __u8 length; /* 0x34 */
+ __u32 sizes[13];
+}phy_cmd_stat;
+
+typedef struct misc_device_statistics {
+ __u16 code; /* 0x05 */
+ __u8 flags;
+ __u8 length; /* 0x10 */
+ __u32 disconnect,
+ pass_thru,
+ sg_commands,
+ stripe_boundary_crosses;
+}msc_stats;
+
+/* Configuration Pages */
+
+typedef struct controller_configuration {
+ __u16 code; /* 0x01 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 intt:1,
+ sec:1,
+ csh:1,
+ key:1,
+ tmr:1,
+ srs:1,
+ nvr:1;
+ __u8 interrupt;
+}coco;
+
+typedef struct controller_hardware_errors {
+ __u16 code; /* 0x02 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 unused:1,
+ per:1;
+ __u8 interrupt;
+}coher;
+
+typedef struct memory_map {
+ __u16 code; /* 0x03, 0x04 */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u32 memory_map;
+}mema;
+
+typedef struct scsi_transfer {
+ __u16 code; /* 0x05 */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 offset,
+ period;
+ __u16 speed;
+}scsitrans;
+
+typedef struct scsi_modes {
+ __u16 code; /* 0x06 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 que:1,
+ cdis:1,
+ wtru:1,
+ dasd:1,
+ ncr:1,
+ awre:1;
+ __u8 reserved;
+}scsimod;
+
+typedef struct host_bus {
+ __u16 code; /* 0x07 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 speed:6,
+ pci:1,
+ eisa:1;
+ __u8 reserved;
+}hobu;
+
+typedef struct scsi_bus {
+ __u16 code; /* 0x08 */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 speed:4,
+ res:1,
+ ext:1,
+ wide:1,
+ dif:1;
+ __u8 busnum;
+}scbu;
+
+typedef struct board_type {
+ __u16 code; /* 0x09 */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 unused:1,
+ cmi:1,
+ dmi:1,
+ cm4k:1,
+ cm4:1,
+ dm4k:1,
+ dm4:1,
+ hba:1;
+ __u8 cpu_type,
+ cpu_speed;
+ __u8 sx1:1,
+ sx2:1,
+ unused2:4,
+ alrm:1,
+ srom:1;
+}boty;
+
+typedef struct memory_config {
+ __u16 code; /* 0x0a */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 banksize[4];
+}memco;
+
+typedef struct firmware_info {
+ __u16 code; /* 0x0b */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 dnld:1,
+ bs528:1,
+ fmt:1,
+ fw528:1;
+ __u8 unused1,
+ fw_type,
+ unused;
+}firm;
+
+typedef struct subsystem_info {
+ __u16 code; /* 0x0c */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 shlf:1,
+ swap:1,
+ noss:1;
+ __u8 reserved;
+}subinf;
+
+typedef struct per_channel_info {
+ __u16 code; /* 0x0d */
+ __u8 flags;
+ __u8 length; /* 0x02 */
+ __u8 channel;
+ __u8 shlf:1,
+ swap:1,
+ noss:1,
+ srs:1,
+ que:1,
+ ext:1,
+ wide:1,
+ diff:1;
+}pcinf;
+
+typedef struct array_limits {
+ __u16 code; /* 0x0e */
+ __u8 flags;
+ __u8 length; /* 0x04 */
+ __u8 max_groups,
+ raid0_drv,
+ raid35_drv,
+ unused;
+}arrlim;
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/eata_dma_proc.src b/i386/i386at/gpl/linux/scsi/eata_dma_proc.src
new file mode 100644
index 00000000..b4936a67
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_dma_proc.src
@@ -0,0 +1,488 @@
+
+void swap_statistics(u8 *p)
+{
+ u32 y;
+ u32 *lp, h_lp;
+ u16 *sp, h_sp;
+ u8 *bp;
+
+ lp = (u32 *)p;
+ sp = ((short *)lp) + 1; /* Convert Header */
+ h_sp = *sp = ntohs(*sp);
+ lp++;
+
+ do {
+ sp = (u16 *)lp; /* Convert SubHeader */
+ *sp = ntohs(*sp);
+ bp = (u8 *) lp;
+ y = *(bp + 3);
+ lp++;
+ for (h_lp = (u32)lp; (u32)lp < h_lp + ((u32)*(bp + 3)); lp++)
+ *lp = ntohl(*lp);
+ }while ((u32)lp < ((u32)p) + 4 + h_sp);
+
+}
+
+/*
+ * eata_set_info
+ * buffer : pointer to the data that has been written to the hostfile
+ * length : number of bytes written to the hostfile
+ * HBA_ptr: pointer to the Scsi_Host struct
+ */
+int eata_set_info(char *buffer, int length, struct Scsi_Host *HBA_ptr)
+{
+ int orig_length = length;
+
+ if (length >= 8 && strncmp(buffer, "eata_dma", 8) == 0) {
+ buffer += 9;
+ length -= 9;
+ if(length >= 8 && strncmp(buffer, "latency", 7) == 0) {
+ SD(HBA_ptr)->do_latency = TRUE;
+ return(orig_length);
+ }
+
+ if(length >=10 && strncmp(buffer, "nolatency", 9) == 0) {
+ SD(HBA_ptr)->do_latency = FALSE;
+ return(orig_length);
+ }
+
+ printk("Unknown command:%s length: %d\n", buffer, length);
+ } else
+ printk("Wrong Signature:%10s\n", buffer);
+
+ return(-EINVAL);
+}
+
+/*
+ * eata_proc_info
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is beeing written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int eata_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+
+ Scsi_Device *scd, SDev;
+ struct Scsi_Host *HBA_ptr;
+ Scsi_Cmnd scmd;
+ char cmnd[10];
+ static u8 buff[512];
+ static u8 buff2[512];
+ hst_cmd_stat *rhcs, *whcs;
+ coco *cc;
+ scsitrans *st;
+ scsimod *sm;
+ hobu *hb;
+ scbu *sb;
+ boty *bt;
+ memco *mc;
+ firm *fm;
+ subinf *si;
+ pcinf *pi;
+ arrlim *al;
+ int i, x;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+ scd = NULL;
+
+ HBA_ptr = first_HBA;
+ for (i = 1; i <= registered_HBAs; i++) {
+ if (HBA_ptr->host_no == hostno)
+ break;
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+
+ if(inout == TRUE) /* Has data been writen to the file ? */
+ return(eata_set_info(buffer, length, HBA_ptr));
+
+ if (offset == 0)
+ memset(buff, 0, sizeof(buff));
+
+ cc = (coco *) (buff + 0x148);
+ st = (scsitrans *)(buff + 0x164);
+ sm = (scsimod *) (buff + 0x16c);
+ hb = (hobu *) (buff + 0x172);
+ sb = (scbu *) (buff + 0x178);
+ bt = (boty *) (buff + 0x17e);
+ mc = (memco *) (buff + 0x186);
+ fm = (firm *) (buff + 0x18e);
+ si = (subinf *) (buff + 0x196);
+ pi = (pcinf *) (buff + 0x19c);
+ al = (arrlim *) (buff + 0x1a2);
+
+ size = sprintf(buffer+len, "EATA (Extended Attachment) driver version: "
+ "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
+ len += size; pos = begin + len;
+ size = sprintf(buffer + len, "queued commands: %10ld\n"
+ "processed interrupts:%10ld\n", queue_counter, int_counter);
+ len += size; pos = begin + len;
+
+ size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Firmware revision: v%s\n",
+ SD(HBA_ptr)->revision);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Hardware Configuration:\n");
+ len += size;
+ pos = begin + len;
+
+ if(SD(HBA_ptr)->broken_INQUIRY == TRUE) {
+ if (HBA_ptr->dma_channel == BUSMASTER)
+ size = sprintf(buffer + len, "DMA: BUSMASTER\n");
+ else
+ size = sprintf(buffer + len, "DMA: %d\n", HBA_ptr->dma_channel);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, "Host Bus: EISA\n");
+ len += size;
+ pos = begin + len;
+
+ } else {
+ memset(&SDev, 0, sizeof(Scsi_Device));
+ memset(&scmd, 0, sizeof(Scsi_Cmnd));
+
+ SDev.host = HBA_ptr;
+ SDev.id = HBA_ptr->this_id;
+ SDev.lun = 0;
+ SDev.channel = 0;
+
+ cmnd[0] = LOG_SENSE;
+ cmnd[1] = 0;
+ cmnd[2] = 0x33 + (3<<6);
+ cmnd[3] = 0;
+ cmnd[4] = 0;
+ cmnd[5] = 0;
+ cmnd[6] = 0;
+ cmnd[7] = 0x00;
+ cmnd[8] = 0x66;
+ cmnd[9] = 0;
+
+ scmd.cmd_len = 10;
+
+ scmd.host = HBA_ptr;
+ scmd.device = &SDev;
+ scmd.target = HBA_ptr->this_id;
+ scmd.lun = 0;
+ scmd.channel = 0;
+ scmd.use_sg = 0;
+
+ /*
+ * Do the command and wait for it to finish.
+ */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scmd.request.rq_status = RQ_SCSI_BUSY;
+ scmd.request.sem = &sem;
+ scsi_do_cmd (&scmd, cmnd, buff + 0x144, 0x66,
+ eata_scsi_done, 1 * HZ, 1);
+ down(&sem);
+ }
+
+ size = sprintf(buffer + len, "IRQ: %2d, %s triggered\n", cc->interrupt,
+ (cc->intt == TRUE)?"level":"edge");
+ len += size;
+ pos = begin + len;
+ if (HBA_ptr->dma_channel == 0xff)
+ size = sprintf(buffer + len, "DMA: BUSMASTER\n");
+ else
+ size = sprintf(buffer + len, "DMA: %d\n", HBA_ptr->dma_channel);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "CPU: MC680%02d %dMHz\n", bt->cpu_type,
+ bt->cpu_speed);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Host Bus: %s\n",
+ (SD(HBA_ptr)->bustype == IS_PCI)?"PCI ":
+ (SD(HBA_ptr)->bustype == IS_EISA)?"EISA":"ISA ");
+
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SCSI Bus:%s%s Speed: %sMB/sec. %s\n",
+ (sb->wide == TRUE)?" WIDE":"",
+ (sb->dif == TRUE)?" DIFFERENTIAL":"",
+ (sb->speed == 0)?"5":(sb->speed == 1)?"10":"20",
+ (sb->ext == TRUE)?"With external cable detection":"");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SCSI channel expansion Module: %s present\n",
+ (bt->sx1 == TRUE)?"SX1 (one channel)":
+ ((bt->sx2 == TRUE)?"SX2 (two channels)":"not"));
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SmartRAID hardware: %spresent.\n",
+ (cc->srs == TRUE)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Type: %s\n",
+ ((cc->key == TRUE)?((bt->dmi == TRUE)?"integrated"
+ :((bt->dm4 == TRUE)?"DM401X"
+ :(bt->dm4k == TRUE)?"DM4000"
+ :"-"))
+ :"-"));
+ len += size;
+ pos = begin + len;
+
+ size = sprintf(buffer + len, " Max array groups: %d\n",
+ (al->code == 0x0e)?al->max_groups:7);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Max drives per RAID 0 array: %d\n",
+ (al->code == 0x0e)?al->raid0_drv:7);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Max drives per RAID 3/5 array: %d\n",
+ (al->code == 0x0e)?al->raid35_drv:7);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Cache Module: %spresent.\n",
+ (cc->csh)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, " Type: %s\n",
+ ((cc->csh == TRUE)?((bt->cmi == TRUE)?"integrated"
+ :((bt->cm4 == TRUE)?"CM401X"
+ :((bt->cm4k == TRUE)?"CM4000"
+ :"-")))
+ :"-"));
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 3; x++) {
+ size = sprintf(buffer + len, " Bank%d: %dMB with%s ECC\n",x,
+ mc->banksize[x] & 0x7f,
+ (mc->banksize[x] & 0x80)?"":"out");
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer + len, "Timer Mod.: %spresent\n",
+ (cc->tmr == TRUE)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "NVRAM : %spresent\n",
+ (cc->nvr == TRUE)?"":"not ");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "SmartROM : %sabled\n",
+ (bt->srom == TRUE)?"dis":"en");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Alarm : %s\n",
+ (bt->alrm == TRUE)?"on":"off");
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ cmnd[0] = LOG_SENSE;
+ cmnd[1] = 0;
+ cmnd[2] = 0x32 + (3<<6);
+ cmnd[3] = 0;
+ cmnd[4] = 0;
+ cmnd[5] = 0;
+ cmnd[6] = 0;
+ cmnd[7] = 0x01;
+ cmnd[8] = 0x44;
+ cmnd[9] = 0;
+
+ scmd.cmd_len = 10;
+
+ /*
+ * Do the command and wait for it to finish.
+ */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ scmd.request.rq_status = RQ_SCSI_BUSY;
+ scmd.request.sem = &sem;
+ scsi_do_cmd (&scmd, cmnd, buff2, 0x144,
+ eata_scsi_done, 1 * HZ, 1);
+ down(&sem);
+ }
+
+ swap_statistics(buff2);
+ rhcs = (hst_cmd_stat *)(buff2 + 0x2c);
+ whcs = (hst_cmd_stat *)(buff2 + 0x8c);
+
+ for (x = 0; x <= 11; x++) {
+ SD(HBA_ptr)->reads[x] += rhcs->sizes[x];
+ SD(HBA_ptr)->writes[x] += whcs->sizes[x];
+ SD(HBA_ptr)->reads[12] += rhcs->sizes[x];
+ SD(HBA_ptr)->writes[12] += whcs->sizes[x];
+ }
+ size = sprintf(buffer + len, "Host<->Disk command statistics:\n"
+ " Reads: Writes:\n");
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 10; x++) {
+ size = sprintf(buffer+len,"%5dk:%12u %12u\n", 1 << x,
+ SD(HBA_ptr)->reads[x],
+ SD(HBA_ptr)->writes[x]);
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer+len,">1024k:%12u %12u\n",
+ SD(HBA_ptr)->reads[11],
+ SD(HBA_ptr)->writes[11]);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer+len,"Sum :%12u %12u\n",
+ SD(HBA_ptr)->reads[12],
+ SD(HBA_ptr)->writes[12]);
+ len += size;
+ pos = begin + len;
+ }
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ if(SD(HBA_ptr)->do_latency == TRUE) {
+ size = sprintf(buffer + len, "Host Latency Command Statistics:\n"
+ "Current timer resolution: 10ms\n"
+ " Reads: Min:(ms) Max:(ms) Ave:(ms)\n");
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 10; x++) {
+ size = sprintf(buffer+len,"%5dk:%12u %12u %12u %12u\n",
+ 1 << x,
+ SD(HBA_ptr)->reads_lat[x][0],
+ (SD(HBA_ptr)->reads_lat[x][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->reads_lat[x][1] * 10),
+ SD(HBA_ptr)->reads_lat[x][2] * 10,
+ SD(HBA_ptr)->reads_lat[x][3] * 10 /
+ ((SD(HBA_ptr)->reads_lat[x][0])
+ ? SD(HBA_ptr)->reads_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer+len,">1024k:%12u %12u %12u %12u\n",
+ SD(HBA_ptr)->reads_lat[11][0],
+ (SD(HBA_ptr)->reads_lat[11][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->reads_lat[11][1] * 10),
+ SD(HBA_ptr)->reads_lat[11][2] * 10,
+ SD(HBA_ptr)->reads_lat[11][3] * 10 /
+ ((SD(HBA_ptr)->reads_lat[x][0])
+ ? SD(HBA_ptr)->reads_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ size = sprintf(buffer + len,
+ " Writes: Min:(ms) Max:(ms) Ave:(ms)\n");
+ len += size;
+ pos = begin + len;
+ for (x = 0; x <= 10; x++) {
+ size = sprintf(buffer+len,"%5dk:%12u %12u %12u %12u\n",
+ 1 << x,
+ SD(HBA_ptr)->writes_lat[x][0],
+ (SD(HBA_ptr)->writes_lat[x][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->writes_lat[x][1] * 10),
+ SD(HBA_ptr)->writes_lat[x][2] * 10,
+ SD(HBA_ptr)->writes_lat[x][3] * 10 /
+ ((SD(HBA_ptr)->writes_lat[x][0])
+ ? SD(HBA_ptr)->writes_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+ }
+ size = sprintf(buffer+len,">1024k:%12u %12u %12u %12u\n",
+ SD(HBA_ptr)->writes_lat[11][0],
+ (SD(HBA_ptr)->writes_lat[11][1] == 0xffffffff)
+ ? 0:(SD(HBA_ptr)->writes_lat[x][1] * 10),
+ SD(HBA_ptr)->writes_lat[11][2] * 10,
+ SD(HBA_ptr)->writes_lat[11][3] * 10/
+ ((SD(HBA_ptr)->writes_lat[x][0])
+ ? SD(HBA_ptr)->writes_lat[x][0]:1));
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+
+#if 0
+ scd = scsi_devices;
+
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+#endif
+
+ stop_output:
+ DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
+
+ return (len);
+}
+
+/*
+ * 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
+ * tab-width: 8
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/scsi/eata_generic.h b/i386/i386at/gpl/linux/scsi/eata_generic.h
new file mode 100644
index 00000000..4d9fc497
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_generic.h
@@ -0,0 +1,397 @@
+/********************************************************
+* Header file for eata_dma.c and eata_pio.c *
+* Linux EATA SCSI drivers *
+* (c) 1993,94,95 Michael Neuffer *
+*********************************************************
+* last change: 95/11/07 *
+********************************************************/
+
+
+#ifndef _EATA_GENERIC_H
+#define _EATA_GENERIC_H
+
+
+
+/*********************************************
+ * Misc. definitions *
+ *********************************************/
+
+#ifndef TRUE
+#define TRUE 1
+#endif
+#ifndef FALSE
+#define FALSE 0
+#endif
+
+#define min(a,b) ((a<b)?(a):(b))
+
+#define R_LIMIT 0x20000
+
+#define MAXISA 4
+#define MAXEISA 16
+#define MAXPCI 16
+#define MAXIRQ 16
+#define MAXTARGET 16
+#define MAXCHANNEL 3
+
+#define IS_ISA 'I'
+#define IS_EISA 'E'
+#define IS_PCI 'P'
+
+#define BROKEN_INQUIRY 1
+
+#define BUSMASTER 0xff
+#define PIO 0xfe
+
+#define EATA_SIGNATURE 0x45415441 /* BIG ENDIAN coded "EATA" sig. */
+
+#define DPT_ID1 0x12
+#define DPT_ID2 0x14
+
+#define ATT_ID1 0x06
+#define ATT_ID2 0x94
+#define ATT_ID3 0x0
+
+#define NEC_ID1 0x38
+#define NEC_ID2 0xa3
+#define NEC_ID3 0x82
+
+
+#define EATA_CP_SIZE 44
+
+#define MAX_PCI_DEVICES 32 /* Maximum # Of Devices Per Bus */
+#define MAX_METHOD_2 16 /* Max Devices For Method 2 */
+#define MAX_PCI_BUS 16 /* Maximum # Of Busses Allowed */
+
+#define SG_SIZE 64
+#define SG_SIZE_BIG 509 /* max. 509 elements, one 4k page */
+
+#define C_P_L_DIV 2 /* 1 <= C_P_L_DIV <= 8
+ * You can use this parameter to fine-tune
+ * the driver. Depending on the number of
+ * devices and their speed and ability to queue
+ * commands, you will get the best results with a
+ * value
+ * ~= numdevices-(devices_unable_to_queue_commands/2)
+ * The reason for this is that the disk driver
+ * tends to flood the queue, so that other
+ * drivers have problems to queue commands
+ * themselves. This can for example result in
+ * the effect that the tape stops during disk
+ * accesses.
+ */
+
+#define FREE 0
+#define OK 0
+#define NO_TIMEOUT 0
+#define USED 1
+#define TIMEOUT 2
+#define RESET 4
+#define LOCKED 8
+
+#define HD(cmd) ((hostdata *)&(cmd->host->hostdata))
+#define CD(cmd) ((struct eata_ccb *)(cmd->host_scribble))
+#define SD(host) ((hostdata *)&(host->hostdata))
+
+#define DELAY(x) { __u32 i; i = jiffies + (x * HZ); while (jiffies < i) barrier(); }
+#define DEL2(x) { __u32 i; for (i = 0; i < 0xffff * x; i++); }
+
+/***********************************************
+ * EATA Command & Register definitions *
+ ***********************************************/
+#define PCI_REG_DPTconfig 0x40
+#define PCI_REG_PumpModeAddress 0x44
+#define PCI_REG_PumpModeData 0x48
+#define PCI_REG_ConfigParam1 0x50
+#define PCI_REG_ConfigParam2 0x54
+
+
+#define EATA_CMD_PIO_SETUPTEST 0xc6
+#define EATA_CMD_PIO_READ_CONFIG 0xf0
+#define EATA_CMD_PIO_SET_CONFIG 0xf1
+#define EATA_CMD_PIO_SEND_CP 0xf2
+#define EATA_CMD_PIO_RECEIVE_SP 0xf3
+#define EATA_CMD_PIO_TRUNC 0xf4
+
+#define EATA_CMD_RESET 0xf9
+#define EATA_CMD_IMMEDIATE 0xfa
+
+#define EATA_CMD_DMA_READ_CONFIG 0xfd
+#define EATA_CMD_DMA_SET_CONFIG 0xfe
+#define EATA_CMD_DMA_SEND_CP 0xff
+
+#define ECS_EMULATE_SENSE 0xd4
+
+#define EATA_GENERIC_ABORT 0x00
+#define EATA_SPECIFIC_RESET 0x01
+#define EATA_BUS_RESET 0x02
+#define EATA_SPECIFIC_ABORT 0x03
+#define EATA_QUIET_INTR 0x04
+#define EATA_COLD_BOOT_HBA 0x06 /* Only as a last resort */
+#define EATA_FORCE_IO 0x07
+
+
+#define HA_WCOMMAND 0x07 /* command register offset */
+#define HA_WCOMMAND2 0x06 /* immediate command offset */
+#define HA_WSUBCODE 0x05
+#define HA_WSUBLUN 0x04
+#define HA_WDMAADDR 0x02 /* DMA address LSB offset */
+#define HA_RAUXSTAT 0x08 /* aux status register offset*/
+#define HA_RSTATUS 0x07 /* status register offset */
+#define HA_RDATA 0x00 /* data register (16bit) */
+
+#define HA_ABUSY 0x01 /* aux busy bit */
+#define HA_AIRQ 0x02 /* aux IRQ pending bit */
+#define HA_SERROR 0x01 /* pr. command ended in error*/
+#define HA_SMORE 0x02 /* more data soon to come */
+#define HA_SCORR 0x04 /* data corrected */
+#define HA_SDRQ 0x08 /* data request active */
+#define HA_SSC 0x10 /* seek complete */
+#define HA_SFAULT 0x20 /* write fault */
+#define HA_SREADY 0x40 /* drive ready */
+#define HA_SBUSY 0x80 /* drive busy */
+#define HA_SDRDY HA_SSC+HA_SREADY+HA_SDRQ
+
+/**********************************************
+ * Message definitions *
+ **********************************************/
+
+#define HA_NO_ERROR 0x00 /* No Error */
+#define HA_ERR_SEL_TO 0x01 /* Selection Timeout */
+#define HA_ERR_CMD_TO 0x02 /* Command Timeout */
+#define HA_ERR_RESET 0x03 /* SCSI Bus Reset Received */
+#define HA_INIT_POWERUP 0x04 /* Initial Controller Power-up */
+#define HA_UNX_BUSPHASE 0x05 /* Unexpected Bus Phase */
+#define HA_UNX_BUS_FREE 0x06 /* Unexpected Bus Free */
+#define HA_BUS_PARITY 0x07 /* Bus Parity Error */
+#define HA_SCSI_HUNG 0x08 /* SCSI Hung */
+#define HA_UNX_MSGRJCT 0x09 /* Unexpected Message Rejected */
+#define HA_RESET_STUCK 0x0a /* SCSI Bus Reset Stuck */
+#define HA_RSENSE_FAIL 0x0b /* Auto Request-Sense Failed */
+#define HA_PARITY_ERR 0x0c /* Controller Ram Parity Error */
+#define HA_CP_ABORT_NA 0x0d /* Abort Message sent to non-active cmd */
+#define HA_CP_ABORTED 0x0e /* Abort Message sent to active cmd */
+#define HA_CP_RESET_NA 0x0f /* Reset Message sent to non-active cmd */
+#define HA_CP_RESET 0x10 /* Reset Message sent to active cmd */
+#define HA_ECC_ERR 0x11 /* Controller Ram ECC Error */
+#define HA_PCI_PARITY 0x12 /* PCI Parity Error */
+#define HA_PCI_MABORT 0x13 /* PCI Master Abort */
+#define HA_PCI_TABORT 0x14 /* PCI Target Abort */
+#define HA_PCI_STABORT 0x15 /* PCI Signaled Target Abort */
+
+/**********************************************
+ * Other definitions *
+ **********************************************/
+
+struct reg_bit { /* reading this one will clear the interrupt */
+ __u8 error:1; /* previous command ended in an error */
+ __u8 more:1; /* more DATA coming soon, poll BSY & DRQ (PIO) */
+ __u8 corr:1; /* data read was successfully corrected with ECC*/
+ __u8 drq:1; /* data request active */
+ __u8 sc:1; /* seek complete */
+ __u8 fault:1; /* write fault */
+ __u8 ready:1; /* drive ready */
+ __u8 busy:1; /* controller busy */
+};
+
+struct reg_abit { /* reading this won't clear the interrupt */
+ __u8 abusy:1; /* auxiliary busy */
+ __u8 irq:1; /* set when drive interrupt is asserted */
+ __u8 dummy:6;
+};
+
+struct eata_register { /* EATA register set */
+ __u8 data_reg[2]; /* R, couldn't figure this one out */
+ __u8 cp_addr[4]; /* W, CP address register */
+ union {
+ __u8 command; /* W, command code: [read|set] conf, send CP*/
+ struct reg_bit status; /* R, see register_bit1 */
+ __u8 statusbyte;
+ } ovr;
+ struct reg_abit aux_stat; /* R, see register_bit2 */
+};
+
+struct get_conf { /* Read Configuration Array */
+ __u32 len; /* Should return 0x22, 0x24, etc */
+ __u32 signature; /* Signature MUST be "EATA" */
+ __u8 version2:4,
+ version:4; /* EATA Version level */
+ __u8 OCS_enabled:1, /* Overlap Command Support enabled */
+ TAR_support:1, /* SCSI Target Mode supported */
+ TRNXFR:1, /* Truncate Transfer Cmd not necessary *
+ * Only used in PIO Mode */
+ MORE_support:1, /* MORE supported (only PIO Mode) */
+ DMA_support:1, /* DMA supported Driver uses only *
+ * this mode */
+ DMA_valid:1, /* DRQ value in Byte 30 is valid */
+ ATA:1, /* ATA device connected (not supported) */
+ HAA_valid:1; /* Hostadapter Address is valid */
+
+ __u16 cppadlen; /* Number of pad bytes send after CD data *
+ * set to zero for DMA commands */
+ __u8 scsi_id[4]; /* SCSI ID of controller 2-0 Byte 0 res. *
+ * if not, zero is returned */
+ __u32 cplen; /* CP length: number of valid cp bytes */
+ __u32 splen; /* Number of bytes returned after *
+ * Receive SP command */
+ __u16 queuesiz; /* max number of queueable CPs */
+ __u16 dummy;
+ __u16 SGsiz; /* max number of SG table entries */
+ __u8 IRQ:4, /* IRQ used this HA */
+ IRQ_TR:1, /* IRQ Trigger: 0=edge, 1=level */
+ SECOND:1, /* This is a secondary controller */
+ DMA_channel:2; /* DRQ index, DRQ is 2comp of DRQX */
+ __u8 sync; /* device at ID 7 tru 0 is running in *
+ * synchronous mode, this will disappear */
+ __u8 DSBLE:1, /* ISA i/o addressing is disabled */
+ FORCADR:1, /* i/o address has been forced */
+ SG_64K:1,
+ SG_UAE:1,
+ :4;
+ __u8 MAX_ID:5, /* Max number of SCSI target IDs */
+ MAX_CHAN:3; /* Number of SCSI busses on HBA */
+ __u8 MAX_LUN; /* Max number of LUNs */
+ __u8 :3,
+ AUTOTRM:1,
+ M1_inst:1,
+ ID_qest:1, /* Raidnum ID is questionable */
+ is_PCI:1, /* HBA is PCI */
+ is_EISA:1; /* HBA is EISA */
+ __u8 unused[478];
+};
+
+struct eata_sg_list
+{
+ __u32 data;
+ __u32 len;
+};
+
+struct eata_ccb { /* Send Command Packet structure */
+
+ __u8 SCSI_Reset:1, /* Cause a SCSI Bus reset on the cmd */
+ HBA_Init:1, /* Cause Controller to reinitialize */
+ Auto_Req_Sen:1, /* Do Auto Request Sense on errors */
+ scatter:1, /* Data Ptr points to a SG Packet */
+ Resrvd:1, /* RFU */
+ Interpret:1, /* Interpret the SCSI cdb of own use */
+ DataOut:1, /* Data Out phase with command */
+ DataIn:1; /* Data In phase with command */
+ __u8 reqlen; /* Request Sense Length *
+ * Valid if Auto_Req_Sen=1 */
+ __u8 unused[3];
+ __u8 FWNEST:1, /* send cmd to phys RAID component */
+ unused2:7;
+ __u8 Phsunit:1, /* physical unit on mirrored pair */
+ I_AT:1, /* inhibit address translation */
+ I_HBA_C:1, /* HBA inhibit caching */
+ unused3:5;
+
+ __u8 cp_id:5, /* SCSI Device ID of target */
+ cp_channel:3; /* SCSI Channel # of HBA */
+ __u8 cp_lun:3,
+ :2,
+ cp_luntar:1, /* CP is for target ROUTINE */
+ cp_dispri:1, /* Grant disconnect privilege */
+ cp_identify:1; /* Always TRUE */
+ __u8 cp_msg1; /* Message bytes 0-3 */
+ __u8 cp_msg2;
+ __u8 cp_msg3;
+ __u8 cp_cdb[12]; /* Command Descriptor Block */
+ __u32 cp_datalen; /* Data Transfer Length *
+ * If scatter=1 len of sg package */
+ void *cp_viraddr; /* address of this ccb */
+ __u32 cp_dataDMA; /* Data Address, if scatter=1 *
+ * address of scatter packet */
+ __u32 cp_statDMA; /* address for Status Packet */
+ __u32 cp_reqDMA; /* Request Sense Address, used if *
+ * CP command ends with error */
+ /* Additional CP info begins here */
+ __u32 timestamp; /* Needed to measure command latency */
+ __u32 timeout;
+ __u8 sizeindex;
+ __u8 rw_latency;
+ __u8 retries;
+ __u8 status; /* status of this queueslot */
+ Scsi_Cmnd *cmd; /* address of cmd */
+ struct eata_sg_list *sg_list;
+};
+
+
+struct eata_sp {
+ __u8 hba_stat:7, /* HBA status */
+ EOC:1; /* True if command finished */
+ __u8 scsi_stat; /* Target SCSI status */
+ __u8 reserved[2];
+ __u32 residue_len; /* Number of bytes not transferred */
+ struct eata_ccb *ccb; /* Address set in COMMAND PACKET */
+ __u8 msg[12];
+};
+
+typedef struct hstd {
+ __u8 vendor[9];
+ __u8 name[18];
+ __u8 revision[6];
+ __u8 EATA_revision;
+ __u8 bustype; /* bustype of HBA */
+ __u8 channel; /* # of avail. scsi channels */
+ __u8 state; /* state of HBA */
+ __u8 primary; /* true if primary */
+ __u8 broken_INQUIRY:1; /* This is an EISA HBA with *
+ * broken INQUIRY */
+ __u8 do_latency; /* Latency measurement flag */
+ __u32 reads[13];
+ __u32 writes[13];
+ __u32 reads_lat[12][4];
+ __u32 writes_lat[12][4];
+ /* state of Target (RESET,..) */
+ __u8 t_state[MAXCHANNEL][MAXTARGET];
+ /* timeouts on target */
+ __u32 t_timeout[MAXCHANNEL][MAXTARGET];
+ __u32 last_ccb; /* Last used ccb */
+ __u32 cplen; /* size of CP in words */
+ __u16 cppadlen; /* pad length of cp in words */
+ __u8 hostid; /* SCSI ID of HBA */
+ __u8 devflags; /* bits set for detected devices */
+ __u8 moresupport; /* HBA supports MORE flag */
+ struct Scsi_Host *next;
+ struct Scsi_Host *prev;
+ struct eata_sp sp; /* status packet */
+ struct eata_ccb ccb[0]; /* ccb array begins here */
+}hostdata;
+
+/* structure for max. 2 emulated drives */
+struct drive_geom_emul {
+ __u8 trans; /* translation flag 1=transl */
+ __u8 channel; /* SCSI channel number */
+ __u8 HBA; /* HBA number (prim/sec) */
+ __u8 id; /* drive id */
+ __u8 lun; /* drive lun */
+ __u32 heads; /* number of heads */
+ __u32 sectors; /* number of sectors */
+ __u32 cylinder; /* number of cylinders */
+};
+
+struct geom_emul {
+ __u8 bios_drives; /* number of emulated drives */
+ struct drive_geom_emul drv[2]; /* drive structures */
+};
+
+#endif /* _EATA_GENERIC_H */
+
+/*
+ * Overrides for Emacs so that we almost 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
+ * tab-width: 8
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/scsi/eata_pio.c b/i386/i386at/gpl/linux/scsi/eata_pio.c
new file mode 100644
index 00000000..95248ae7
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_pio.c
@@ -0,0 +1,1051 @@
+/************************************************************
+ * *
+ * Linux EATA SCSI PIO driver *
+ * *
+ * based on the CAM document CAM/89-004 rev. 2.0c, *
+ * DPT's driver kit, some internal documents and source, *
+ * and several other Linux scsi drivers and kernel docs. *
+ * *
+ * The driver currently: *
+ * -supports all EATA-PIO boards *
+ * -only supports DASD devices *
+ * *
+ * (c)1993,94,95 Michael Neuffer, Alfred Arnold *
+ * neuffer@goofy.zdv.uni-mainz.de *
+ * a.arnold@kfa-juelich.de *
+ * *
+ * 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 kernel; if not, write to *
+ * the Free Software Foundation, Inc., 675 Mass Ave, *
+ * Cambridge, MA 02139, USA. *
+ * *
+ ************************************************************
+ * last change: 95/08/04 OS: Linux 1.3.15 *
+ ************************************************************/
+
+/* Look in eata_pio.h for configuration information */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/malloc.h>
+#include <linux/in.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+#include "eata_pio.h"
+#include "eata_dma_proc.h"
+#include "scsi.h"
+#include "sd.h"
+
+#include <linux/stat.h>
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_eata_pio = {
+ PROC_SCSI_EATA_PIO, 9, "eata_pio",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static uint ISAbases[MAXISA] =
+{0x1F0, 0x170, 0x330, 0x230};
+static uint ISAirqs[MAXISA] =
+{14,12,15,11};
+static unchar EISAbases[] =
+{1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1};
+static uint registered_HBAs = 0;
+static struct Scsi_Host *last_HBA = NULL;
+static struct Scsi_Host *first_HBA = NULL;
+static unchar reg_IRQ[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+static unchar reg_IRQL[] =
+{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
+
+static ulong int_counter = 0;
+static ulong queue_counter = 0;
+
+void hprint(const char *str)
+{
+ char *hptr =(char *) 0x000b0000;
+ char *hptr2=(char *) 0x000b00a0;
+ char *hptr3=(char *) 0x000b0f00;
+ int z;
+
+ memmove(hptr,hptr2,24*80*2);
+ for (z=0; z<strlen(str); z++)
+ hptr3[z*2]=str[z];
+ for (; z<80; z++)
+ hptr3[z*2]=' ';
+}
+
+#ifdef MACH
+#include "eata_pio_proc.src"
+#else
+#include "eata_pio_proc.c"
+#endif
+
+#ifdef MODULE
+int eata_pio_release(struct Scsi_Host *sh)
+{
+ if (sh->irq && reg_IRQ[sh->irq] == 1) free_irq(sh->irq);
+ else reg_IRQ[sh->irq]--;
+ if (SD(sh)->channel == 0) {
+ if (sh->io_port && sh->n_io_port)
+ release_region(sh->io_port, sh->n_io_port);
+ }
+ return(TRUE);
+}
+#endif
+
+void IncStat(Scsi_Pointer *SCp, uint Increment)
+{
+ SCp->ptr+=Increment;
+ if ((SCp->this_residual-=Increment)==0)
+ {
+ if ((--SCp->buffers_residual)==0) SCp->Status=FALSE;
+ else
+ {
+ SCp->buffer++;
+ SCp->ptr=SCp->buffer->address;
+ SCp->this_residual=SCp->buffer->length;
+ }
+ }
+}
+
+void eata_pio_int_handler(int irq, struct pt_regs * regs)
+{
+ uint eata_stat = 0xfffff;
+ Scsi_Cmnd *cmd;
+ hostdata *hd;
+ struct eata_ccb *cp;
+ uint base;
+ ulong flags;
+ uint x,z;
+ struct Scsi_Host *sh;
+ ushort zwickel=0;
+ unchar stat,odd;
+
+ save_flags(flags);
+ cli();
+
+ for (x = 1, sh = first_HBA; x <= registered_HBAs; x++, sh = SD(sh)->prev) {
+ if (sh->irq != irq)
+ continue;
+ if (inb((uint)sh->base + HA_RSTATUS) & HA_SBUSY)
+ continue;
+
+ int_counter++;
+
+ hd=SD(sh);
+
+ cp = &hd->ccb[0];
+ cmd = cp->cmd;
+ base = (uint) cmd->host->base;
+
+ do
+ {
+ stat=inb(base+HA_RSTATUS);
+ if (stat&HA_SDRQ)
+ if (cp->DataIn)
+ {
+ z=256; odd=FALSE;
+ while ((cmd->SCp.Status)&&((z>0)||(odd)))
+ {
+ if (odd)
+ {
+ *(cmd->SCp.ptr)=zwickel>>8;
+ IncStat(&cmd->SCp,1);
+ odd=FALSE;
+ }
+ x=min(z,cmd->SCp.this_residual/2);
+ insw(base+HA_RDATA,cmd->SCp.ptr,x);
+ z-=x;
+ IncStat(&cmd->SCp,2*x);
+ if ((z>0)&&(cmd->SCp.this_residual==1))
+ {
+ zwickel=inw(base+HA_RDATA);
+ *(cmd->SCp.ptr)=zwickel&0xff;
+ IncStat(&cmd->SCp,1); z--;
+ odd=TRUE;
+ }
+ }
+ while (z>0) {
+ zwickel=inw(base+HA_RDATA);
+ z--;
+ }
+ }
+ else /* cp->DataOut */
+ {
+ odd=FALSE; z=256;
+ while ((cmd->SCp.Status)&&((z>0)||(odd)))
+ {
+ if (odd)
+ {
+ zwickel+=*(cmd->SCp.ptr)<<8;
+ IncStat(&cmd->SCp,1);
+ outw(zwickel,base+HA_RDATA);
+ z--;
+ odd=FALSE;
+ }
+ x=min(z,cmd->SCp.this_residual/2);
+ outsw(base+HA_RDATA,cmd->SCp.ptr,x);
+ z-=x;
+ IncStat(&cmd->SCp,2*x);
+ if ((z>0)&&(cmd->SCp.this_residual==1))
+ {
+ zwickel=*(cmd->SCp.ptr);
+ zwickel&=0xff;
+ IncStat(&cmd->SCp,1);
+ odd=TRUE;
+ }
+ }
+ while (z>0||odd) {
+ outw(zwickel,base+HA_RDATA);
+ z--;
+ odd=FALSE;
+ }
+ }
+ }
+ while ((stat&HA_SDRQ)||((stat&HA_SMORE)&&hd->moresupport));
+
+ /* terminate handler if HBA goes busy again, i.e. transfers
+ * more data */
+
+ if (stat&HA_SBUSY) break;
+
+ /* OK, this is quite stupid, but I haven't found any correct
+ * way to get HBA&SCSI status so far */
+
+ if (!(inb(base+HA_RSTATUS)&HA_SERROR))
+ {
+ cmd->result=(DID_OK<<16);
+ hd->devflags|=(1<<cp->cp_id);
+ }
+ else if (hd->devflags&1<<cp->cp_id)
+ cmd->result=(DID_OK<<16)+0x02;
+ else cmd->result=(DID_NO_CONNECT<<16);
+
+ if (cp->status == LOCKED) {
+ cp->status = FREE;
+ eata_stat = inb(base + HA_RSTATUS);
+ printk("eata_pio: int_handler, freeing locked queueslot\n");
+ DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+ restore_flags(flags);
+ return;
+ }
+
+#if DBG_INTR2
+ if (stat != 0x50)
+ printk("stat: %#.2x, result: %#.8x\n", stat, cmd->result);
+ DBG(DBG_INTR&&DBG_DELAY,DEL2(800));
+#endif
+
+ cp->status = FREE; /* now we can release the slot */
+
+ restore_flags(flags);
+ cmd->scsi_done(cmd);
+ save_flags(flags);
+ cli();
+ }
+ restore_flags(flags);
+
+ return;
+}
+
+inline uint eata_pio_send_command(uint base, unchar command)
+{
+ uint loop = R_LIMIT;
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return(TRUE);
+
+ outb(command, base + HA_WCOMMAND);
+ return(FALSE);
+}
+
+int eata_pio_queue(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ uint x, y;
+ long flags;
+ uint base;
+
+ hostdata *hd;
+ struct Scsi_Host *sh;
+ struct eata_ccb *cp;
+
+ save_flags(flags);
+ cli();
+
+ queue_counter++;
+
+ hd = HD(cmd);
+ sh = cmd->host;
+ base = (uint) sh->base;
+
+ /* use only slot 0, as 2001 can handle only one cmd at a time */
+
+ y = x = 0;
+
+ if (hd->ccb[y].status!=FREE) {
+
+ DBG(DBG_QUEUE, printk("can_queue %d, x %d, y %d\n",sh->can_queue,x,y));
+#if DEBUG_EATA
+ panic("eata_pio: run out of queue slots cmdno:%ld intrno: %ld\n",
+ queue_counter, int_counter);
+#else
+ panic("eata_pio: run out of queue slots....\n");
+#endif
+ }
+
+ cp = &hd->ccb[y];
+
+ memset(cp, 0, sizeof(struct eata_ccb));
+ memset(cmd->sense_buffer, 0, sizeof(cmd->sense_buffer));
+
+ cp->status = USED; /* claim free slot */
+
+ DBG(DBG_QUEUE, printk("eata_pio_queue pid %ld, target: %x, lun: %x, y %d\n",
+ cmd->pid, cmd->target, cmd->lun, y));
+ DBG(DBG_QUEUE && DBG_DELAY, DEL2(250));
+
+ cmd->scsi_done = (void *)done;
+
+ switch (cmd->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case UPDATE_BLOCK: case WRITE_LONG: case WRITE_SAME:
+ case SEARCH_HIGH_12: case SEARCH_EQUAL_12: case SEARCH_LOW_12:
+ case WRITE_12: case WRITE_VERIFY_12: case SET_WINDOW:
+ case MEDIUM_SCAN: case SEND_VOLUME_TAG:
+ case 0xea: /* alternate number for WRITE LONG */
+ cp->DataOut = TRUE; /* Output mode */
+ break;
+ case TEST_UNIT_READY:
+ default:
+ cp->DataIn = TRUE; /* Input mode */
+ }
+
+ cp->Interpret = (cmd->target == hd->hostid);
+ cp->cp_datalen = htonl((ulong)cmd->request_bufflen);
+ cp->Auto_Req_Sen = FALSE;
+ cp->cp_reqDMA = htonl(0);
+ cp->reqlen = 0;
+
+ cp->cp_id = cmd->target;
+ cp->cp_lun = cmd->lun;
+ cp->cp_dispri = FALSE;
+ cp->cp_identify = TRUE;
+ memcpy(cp->cp_cdb, cmd->cmnd, COMMAND_SIZE(*cmd->cmnd));
+
+ cp->cp_statDMA = htonl(0);
+
+ cp->cp_viraddr = cp;
+ cp->cmd = cmd;
+ cmd->host_scribble = (char *)&hd->ccb[y];
+
+ if (cmd->use_sg == 0)
+ {
+ cmd->SCp.buffers_residual=1;
+ cmd->SCp.ptr = cmd->request_buffer;
+ cmd->SCp.this_residual = cmd->request_bufflen;
+ cmd->SCp.buffer = NULL;
+ } else {
+ cmd->SCp.buffer = cmd->request_buffer;
+ cmd->SCp.buffers_residual = cmd->use_sg;
+ cmd->SCp.ptr = cmd->SCp.buffer->address;
+ cmd->SCp.this_residual = cmd->SCp.buffer->length;
+ }
+ cmd->SCp.Status = (cmd->SCp.this_residual != 0); /* TRUE as long as bytes
+ * are to transfer */
+
+ if (eata_pio_send_command(base, EATA_CMD_PIO_SEND_CP))
+ {
+ cmd->result = DID_BUS_BUSY << 16;
+ printk("eata_pio_queue target %d, pid %ld, HBA busy, returning "
+ "DID_BUS_BUSY, done.\n", cmd->target, cmd->pid);
+ done(cmd);
+ cp->status = FREE;
+ restore_flags(flags);
+ return (0);
+ }
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ));
+ outsw(base + HA_RDATA, cp, hd->cplen);
+ outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
+ for (x = 0; x < hd->cppadlen; x++) outw(0, base + HA_RDATA);
+
+ DBG(DBG_QUEUE,printk("Queued base %#.4lx pid: %ld target: %x lun: %x "
+ "slot %d irq %d\n", (long)sh->base, cmd->pid,
+ cmd->target, cmd->lun, y, sh->irq));
+ DBG(DBG_QUEUE && DBG_DELAY, DEL2(200));
+
+ restore_flags(flags);
+ return (0);
+}
+
+int eata_pio_abort(Scsi_Cmnd * cmd)
+{
+ ulong flags;
+ uint loop = R_LIMIT;
+
+ save_flags(flags);
+ cli();
+
+ DBG(DBG_ABNORM, printk("eata_pio_abort called pid: %ld target: %x lun: %x"
+ " reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+
+ while (inb((uint)(cmd->host->base) + HA_RAUXSTAT) & HA_ABUSY)
+ if (--loop == 0) {
+ printk("eata_pio: abort, timeout error.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == FREE) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_NOT_RUNNING\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ if (CD(cmd)->status == USED) {
+ DBG(DBG_ABNORM, printk("Returning: SCSI_ABORT_BUSY\n"));
+ restore_flags(flags);
+ return (SCSI_ABORT_BUSY); /* SNOOZE */
+ }
+ if (CD(cmd)->status == RESET) {
+ restore_flags(flags);
+ printk("eata_pio: abort, command reset error.\n");
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_ERROR);
+ }
+ if (CD(cmd)->status == LOCKED) {
+ restore_flags(flags);
+ DBG(DBG_ABNORM, printk("eata_pio: abort, queue slot locked.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_ABORT_NOT_RUNNING);
+ }
+ restore_flags(flags);
+ panic("eata_pio: abort: invalid slot status\n");
+}
+
+int eata_pio_reset(Scsi_Cmnd * cmd)
+{
+ uint x, z, time, limit = 0;
+ ulong flags;
+ unchar success = FALSE;
+ Scsi_Cmnd *sp;
+
+ save_flags(flags);
+ cli();
+ hprint("reset");
+ DBG(DBG_ABNORM, printk("eata_pio_reset called pid:%ld target: %x lun: %x "
+ "reason %x\n", cmd->pid, cmd->target, cmd->lun,
+ cmd->abort_reason));
+
+ if (HD(cmd)->state == RESET) {
+ printk("eata_pio_reset: exit, already in reset.\n");
+ restore_flags(flags);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_ERROR);
+ }
+
+ for (z = 0; z < MAXTARGET; z++) {
+ HD(cmd)->t_state[0][z] = RESET;
+ HD(cmd)->t_timeout[0][z] = NO_TIMEOUT;
+ }
+
+ /* force all slots to be free */
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ if (HD(cmd)->ccb[x].status == FREE)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ HD(cmd)->ccb[x].status = RESET;
+ printk("eata_pio_reset: slot %d in reset, pid %ld.\n", x, sp->pid);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ if (sp == NULL)
+ panic("eata_pio_reset: slot %d, sp==NULL.\n", x);
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ }
+
+ /* hard reset the HBA */
+ outb((uint) cmd->host->base+HA_WCOMMAND, EATA_CMD_RESET);
+
+ DBG(DBG_ABNORM, printk("eata_pio_reset: board reset done.\n"));
+ HD(cmd)->state = RESET;
+
+ time = jiffies;
+ while (jiffies < (time + (3 * HZ)) && limit++ < 10000000);
+
+ DBG(DBG_ABNORM, printk("eata_pio_reset: interrupts disabled, loops %d.\n", limit));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+
+ for (x = 0; x < cmd->host->can_queue; x++) {
+
+ /* Skip slots already set free by interrupt */
+ if (HD(cmd)->ccb[x].status != RESET)
+ continue;
+
+ sp = HD(cmd)->ccb[x].cmd;
+ sp->result = DID_RESET << 16;
+
+ /* This mailbox is terminated */
+ printk("eata_pio_reset: resetted ccb %d.\n",x);
+ HD(cmd)->ccb[x].status = FREE;
+
+ restore_flags(flags);
+ sp->scsi_done(sp);
+ cli();
+ }
+
+ HD(cmd)->state = FALSE;
+ restore_flags(flags);
+
+ if (success) { /* hmmm... */
+ DBG(DBG_ABNORM, printk("eata_pio_reset: exit, success.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_SUCCESS);
+ } else {
+ DBG(DBG_ABNORM, printk("eata_pio_reset: exit, wakeup.\n"));
+ DBG(DBG_ABNORM && DBG_DELAY, DEL2(500));
+ return (SCSI_RESET_PUNT);
+ }
+}
+
+char * get_pio_board_data(ulong base, uint irq, uint id, ulong cplen, ushort cppadlen)
+{
+ struct eata_ccb cp;
+ static char buff[256];
+ int z;
+
+ memset(&cp, 0, sizeof(struct eata_ccb));
+ memset(buff, 0, sizeof(buff));
+
+ cp.DataIn = TRUE;
+ cp.Interpret = TRUE; /* Interpret command */
+
+ cp.cp_datalen = htonl(254);
+ cp.cp_dataDMA = htonl(0);
+
+ cp.cp_id = id;
+ cp.cp_lun = 0;
+
+ cp.cp_cdb[0] = INQUIRY;
+ cp.cp_cdb[1] = 0;
+ cp.cp_cdb[2] = 0;
+ cp.cp_cdb[3] = 0;
+ cp.cp_cdb[4] = 254;
+ cp.cp_cdb[5] = 0;
+
+ if (eata_pio_send_command((uint) base, EATA_CMD_PIO_SEND_CP))
+ return (NULL);
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ));
+ outsw(base + HA_RDATA, &cp, cplen);
+ outb(EATA_CMD_PIO_TRUNC, base + HA_WCOMMAND);
+ for (z = 0; z < cppadlen; z++) outw(0, base + HA_RDATA);
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY);
+ if (inb(base + HA_RSTATUS) & HA_SERROR)
+ return (NULL);
+ else if (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ return (NULL);
+ else
+ {
+ insw(base+HA_RDATA, &buff, 127);
+ while (inb(base + HA_RSTATUS)&HA_SDRQ) inw(base + HA_RDATA);
+ return (buff);
+ }
+}
+
+int get_pio_conf_PIO(u32 base, struct get_conf *buf)
+{
+ ulong loop = R_LIMIT;
+ int z;
+ ushort *p;
+
+ if(check_region(base, 9))
+ return (FALSE);
+
+ memset(buf, 0, sizeof(struct get_conf));
+
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ if (--loop == 0)
+ return (FALSE);
+
+ DBG(DBG_PIO && DBG_PROBE,
+ printk("Issuing PIO READ CONFIG to HBA at %#x\n", base));
+ eata_pio_send_command(base, EATA_CMD_PIO_READ_CONFIG);
+
+ loop = R_LIMIT;
+ for (p = (ushort *) buf;
+ (long)p <= ((long)buf + (sizeof(struct get_conf) / 2)); p++) {
+ while (!(inb(base + HA_RSTATUS) & HA_SDRQ))
+ if (--loop == 0)
+ return (FALSE);
+
+ loop = R_LIMIT;
+ *p = inw(base + HA_RDATA);
+ }
+ if (!(inb(base + HA_RSTATUS) & HA_SERROR)) { /* Error ? */
+ if (htonl(EATA_SIGNATURE) == buf->signature) {
+ DBG(DBG_PIO&&DBG_PROBE, printk("EATA Controller found at %#4x "
+ "EATA Level: %x\n", base,
+ (uint) (buf->version)));
+
+ while (inb(base + HA_RSTATUS) & HA_SDRQ)
+ inw(base + HA_RDATA);
+ if(ALLOW_DMA_BOARDS == FALSE) {
+ for (z = 0; z < MAXISA; z++)
+ if (base == ISAbases[z]) {
+ buf->IRQ = ISAirqs[z];
+ break;
+ }
+ }
+ return (TRUE);
+ }
+ } else {
+ DBG(DBG_PROBE, printk("eata_dma: get_conf_PIO, error during transfer "
+ "for HBA at %x\n", base));
+ }
+ return (FALSE);
+}
+
+void print_pio_config(struct get_conf *gc)
+{
+ printk("Please check values: (read config data)\n");
+ printk("LEN: %d ver:%d OCS:%d TAR:%d TRNXFR:%d MORES:%d\n",
+ (uint) ntohl(gc->len), gc->version,
+ gc->OCS_enabled, gc->TAR_support, gc->TRNXFR, gc->MORE_support);
+ printk("HAAV:%d SCSIID0:%d ID1:%d ID2:%d QUEUE:%d SG:%d SEC:%d\n",
+ gc->HAA_valid, gc->scsi_id[3], gc->scsi_id[2],
+ gc->scsi_id[1], ntohs(gc->queuesiz), ntohs(gc->SGsiz), gc->SECOND);
+ printk("IRQ:%d IRQT:%d FORCADR:%d MCH:%d RIDQ:%d\n",
+ gc->IRQ, gc->IRQ_TR, gc->FORCADR,
+ gc->MAX_CHAN, gc->ID_qest);
+ DBG(DPT_DEBUG, DELAY(14));
+}
+
+static uint print_selftest(uint base)
+{
+ unchar buffer[512];
+#ifdef VERBOSE_SETUP
+ int z;
+#endif
+
+ printk("eata_pio: executing controller self test & setup...\n");
+ while (inb(base + HA_RSTATUS) & HA_SBUSY);
+ outb(EATA_CMD_PIO_SETUPTEST, base + HA_WCOMMAND);
+ do {
+ while (inb(base + HA_RSTATUS) & HA_SBUSY)
+ /* nothing */ ;
+ if (inb(base + HA_RSTATUS) & HA_SDRQ)
+ {
+ insw(base + HA_RDATA, &buffer, 256);
+#ifdef VERBOSE_SETUP
+ /* no beeps please... */
+ for (z = 0; z < 511 && buffer[z]; z++)
+ if (buffer[z] != 7) printk("%c", buffer[z]);
+#endif
+ }
+ } while (inb(base+HA_RSTATUS) & (HA_SBUSY|HA_SDRQ));
+
+ return (!(inb(base+HA_RSTATUS) & HA_SERROR));
+}
+
+int register_pio_HBA(long base, struct get_conf *gc, Scsi_Host_Template * tpnt)
+{
+ ulong size = 0;
+ char *buff;
+ ulong cplen;
+ ushort cppadlen;
+ struct Scsi_Host *sh;
+ hostdata *hd;
+
+ DBG(DBG_REGISTER, print_pio_config(gc));
+
+ if (gc->DMA_support == TRUE) {
+ printk("HBA at %#.4lx supports DMA. Please use EATA-DMA driver.\n",base);
+ if(ALLOW_DMA_BOARDS == FALSE)
+ return (FALSE);
+ }
+
+ if ((buff = get_pio_board_data((uint)base, gc->IRQ, gc->scsi_id[3],
+ cplen =(htonl(gc->cplen )+1)/2,
+ cppadlen=(htons(gc->cppadlen)+1)/2)) == NULL)
+ {
+ printk("HBA at %#lx didn't react on INQUIRY. Sorry.\n", (ulong) base);
+ return (FALSE);
+ }
+
+ if (print_selftest(base) == FALSE && ALLOW_DMA_BOARDS == FALSE)
+ {
+ printk("HBA at %#lx failed while performing self test & setup.\n",
+ (ulong) base);
+ return (FALSE);
+ }
+
+ if (!reg_IRQ[gc->IRQ]) { /* Interrupt already registered ? */
+ if (!request_irq(gc->IRQ, eata_pio_int_handler, SA_INTERRUPT,
+ "EATA-PIO")){
+ reg_IRQ[gc->IRQ]++;
+ if (!gc->IRQ_TR)
+ reg_IRQL[gc->IRQ] = TRUE; /* IRQ is edge triggered */
+ } else {
+ printk("Couldn't allocate IRQ %d, Sorry.", gc->IRQ);
+ return (FALSE);
+ }
+ } else { /* More than one HBA on this IRQ */
+ if (reg_IRQL[gc->IRQ] == TRUE) {
+ printk("Can't support more than one HBA on this IRQ,\n"
+ " if the IRQ is edge triggered. Sorry.\n");
+ return (FALSE);
+ } else
+ reg_IRQ[gc->IRQ]++;
+ }
+
+ request_region(base, 8, "eata_pio");
+
+ size = sizeof(hostdata) + (sizeof(struct eata_ccb) * ntohs(gc->queuesiz));
+
+ sh = scsi_register(tpnt, size);
+ hd = SD(sh);
+
+ memset(hd->ccb, 0, (sizeof(struct eata_ccb) * ntohs(gc->queuesiz)));
+ memset(hd->reads, 0, sizeof(ulong) * 26);
+
+ strncpy(SD(sh)->vendor, &buff[8], 8);
+ SD(sh)->vendor[8] = 0;
+ strncpy(SD(sh)->name, &buff[16], 17);
+ SD(sh)->name[17] = 0;
+ SD(sh)->revision[0] = buff[32];
+ SD(sh)->revision[1] = buff[33];
+ SD(sh)->revision[2] = buff[34];
+ SD(sh)->revision[3] = '.';
+ SD(sh)->revision[4] = buff[35];
+ SD(sh)->revision[5] = 0;
+
+ switch (ntohl(gc->len)) {
+ case 0x1c:
+ SD(sh)->EATA_revision = 'a';
+ break;
+ case 0x1e:
+ SD(sh)->EATA_revision = 'b';
+ break;
+ case 0x22:
+ SD(sh)->EATA_revision = 'c';
+ break;
+ case 0x24:
+ SD(sh)->EATA_revision = 'z';
+ default:
+ SD(sh)->EATA_revision = '?';
+ }
+
+ if(ntohl(gc->len) >= 0x22) {
+ if (gc->is_PCI == TRUE)
+ hd->bustype = IS_PCI;
+ else if (gc->is_EISA == TRUE)
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ } else {
+ if (buff[21] == '4')
+ hd->bustype = IS_PCI;
+ else if (buff[21] == '2')
+ hd->bustype = IS_EISA;
+ else
+ hd->bustype = IS_ISA;
+ }
+
+ SD(sh)->cplen=cplen;
+ SD(sh)->cppadlen=cppadlen;
+ SD(sh)->hostid=gc->scsi_id[3];
+ SD(sh)->devflags=1<<gc->scsi_id[3];
+ SD(sh)->moresupport=gc->MORE_support;
+ sh->unique_id = base;
+ sh->base = (char *) base;
+ sh->io_port = base;
+ sh->n_io_port = 8;
+ sh->irq = gc->IRQ;
+ sh->dma_channel = PIO;
+ sh->this_id = gc->scsi_id[3];
+ sh->can_queue = 1;
+ sh->cmd_per_lun = 1;
+ sh->sg_tablesize = SG_ALL;
+
+ hd->channel = 0;
+
+ sh->max_id = 8;
+ sh->max_lun = 8;
+
+ if (gc->SECOND)
+ hd->primary = FALSE;
+ else
+ hd->primary = TRUE;
+
+ sh->unchecked_isa_dma = FALSE; /* We can only do PIO */
+
+ hd->next = NULL; /* build a linked list of all HBAs */
+ hd->prev = last_HBA;
+ if(hd->prev != NULL)
+ SD(hd->prev)->next = sh;
+ last_HBA = sh;
+ if (first_HBA == NULL)
+ first_HBA = sh;
+ registered_HBAs++;
+ return (1);
+}
+
+void find_pio_ISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ int i;
+
+ for (i = 0; i < MAXISA; i++) {
+ if (ISAbases[i]) {
+ if (get_pio_conf_PIO(ISAbases[i], buf) == TRUE){
+ register_pio_HBA(ISAbases[i], buf, tpnt);
+ }
+ ISAbases[i] = 0;
+ }
+ }
+ return;
+}
+
+void find_pio_EISA(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+ u32 base;
+ int i;
+
+#if CHECKPAL
+ u8 pal1, pal2, pal3;
+#endif
+
+ for (i = 0; i < MAXEISA; i++) {
+ if (EISAbases[i] == TRUE) { /* Still a possibility ? */
+
+ base = 0x1c88 + (i * 0x1000);
+#if CHECKPAL
+ pal1 = inb((u16)base - 8);
+ pal2 = inb((u16)base - 7);
+ pal3 = inb((u16)base - 6);
+
+ if (((pal1 == 0x12) && (pal2 == 0x14)) ||
+ ((pal1 == 0x38) && (pal2 == 0xa3) && (pal3 == 0x82)) ||
+ ((pal1 == 0x06) && (pal2 == 0x94) && (pal3 == 0x24))) {
+ DBG(DBG_PROBE, printk("EISA EATA id tags found: %x %x %x \n",
+ (int)pal1, (int)pal2, (int)pal3));
+#endif
+ if (get_pio_conf_PIO(base, buf) == TRUE) {
+ DBG(DBG_PROBE && DBG_EISA, print_pio_config(buf));
+ if (buf->IRQ) {
+ register_pio_HBA(base, buf, tpnt);
+ } else
+ printk("eata_dma: No valid IRQ. HBA removed from list\n");
+ }
+ /* Nothing found here so we take it from the list */
+ EISAbases[i] = 0;
+#if CHECKPAL
+ }
+#endif
+ }
+ }
+ return;
+}
+
+void find_pio_PCI(struct get_conf *buf, Scsi_Host_Template * tpnt)
+{
+
+#ifndef CONFIG_PCI
+ printk("eata_pio: kernel PCI support not enabled. Skipping scan for PCI HBAs.\n");
+#else
+
+ u8 pci_bus, pci_device_fn;
+ static s16 pci_index = 0; /* Device index to PCI BIOS calls */
+ u32 base = 0;
+ u16 com_adr;
+ u16 rev_device;
+ u32 error, i, x;
+
+ if (pcibios_present()) {
+ for (i = 0; i <= MAXPCI; ++i, ++pci_index) {
+ if (pcibios_find_device(PCI_VENDOR_ID_DPT, PCI_DEVICE_ID_DPT,
+ pci_index, &pci_bus, &pci_device_fn))
+ break;
+ DBG(DBG_PROBE && DBG_PCI,
+ printk("eata_pio: HBA at bus %d, device %d,"
+ " function %d, index %d\n", (s32)pci_bus,
+ (s32)((pci_device_fn & 0xf8) >> 3),
+ (s32)(pci_device_fn & 7), pci_index));
+
+ if (!(error = pcibios_read_config_word(pci_bus, pci_device_fn,
+ PCI_CLASS_DEVICE, &rev_device))) {
+ if (rev_device == PCI_CLASS_STORAGE_SCSI) {
+ if (!(error = pcibios_read_config_word(pci_bus,
+ pci_device_fn, PCI_COMMAND,
+ (u16 *) & com_adr))) {
+ if (!((com_adr & PCI_COMMAND_IO) &&
+ (com_adr & PCI_COMMAND_MASTER))) {
+ printk("HBA has IO or BUSMASTER mode disabled\n");
+ continue;
+ }
+ } else
+ printk("eata_pio: error %x while reading "
+ "PCI_COMMAND\n", error);
+ } else
+ printk("DEVICECLASSID %x didn't match\n", rev_device);
+ } else {
+ printk("eata_pio: error %x while reading PCI_CLASS_BASE\n",
+ error);
+ continue;
+ }
+
+ if (!(error = pcibios_read_config_dword(pci_bus, pci_device_fn,
+ PCI_BASE_ADDRESS_0, (int *) &base))){
+
+ /* Check if the address is valid */
+ if (base & 0x01) {
+ base &= 0xfffffffe;
+ /* EISA tag there ? */
+ if ((inb(base) == 0x12) && (inb(base + 1) == 0x14))
+ continue; /* Jep, it's forced, so move on */
+ base += 0x10; /* Now, THIS is the real address */
+ if (base != 0x1f8) {
+ /* We didn't find it in the primary search */
+ if (get_pio_conf_PIO(base, buf) == TRUE) {
+ if (buf->FORCADR) /* If the address is forced */
+ continue; /* we'll find it later */
+
+ /* OK. We made it till here, so we can go now
+ * and register it. We only have to check and
+ * eventually remove it from the EISA and ISA list
+ */
+
+ register_pio_HBA(base, buf, tpnt);
+
+ if (base < 0x1000) {
+ for (x = 0; x < MAXISA; ++x) {
+ if (ISAbases[x] == base) {
+ ISAbases[x] = 0;
+ break;
+ }
+ }
+ } else if ((base & 0x0fff) == 0x0c88) {
+ x = (base >> 12) & 0x0f;
+ EISAbases[x] = 0;
+ }
+ continue; /* break; */
+ }
+ }
+ }
+ } else
+ printk("eata_pio: error %x while reading "
+ "PCI_BASE_ADDRESS_0\n", error);
+ }
+ } else
+ printk("eata_pio: No BIOS32 extensions present. This driver release "
+ "still depends on it.\n"
+ " Skipping scan for PCI HBAs.\n");
+#endif /* #ifndef CONFIG_PCI */
+ return;
+}
+
+
+int eata_pio_detect(Scsi_Host_Template * tpnt)
+{
+ struct Scsi_Host *HBA_ptr;
+ struct get_conf gc;
+ int i;
+
+ DBG((DBG_PROBE && DBG_DELAY) || DPT_DEBUG,
+ printk("Using lots of delays to let you read the debugging output\n"));
+
+ tpnt->proc_dir = &proc_scsi_eata_pio;
+
+ find_pio_PCI(&gc, tpnt);
+
+ find_pio_EISA(&gc, tpnt);
+
+ find_pio_ISA(&gc, tpnt);
+
+ for (i = 0; i <= MAXIRQ; i++)
+ if (reg_IRQ[i])
+ request_irq(i, eata_pio_int_handler, SA_INTERRUPT, "EATA-PIO");
+
+ HBA_ptr = first_HBA;
+
+ if (registered_HBAs != 0) {
+ printk("EATA (Extended Attachment) PIO driver version: %d.%d%s\n"
+ "(c) 1993-95 Michael Neuffer, neuffer@goofy.zdv.uni-mainz.de\n"
+ " Alfred Arnold, a.arnold@kfa-juelich.de\n"
+ "This release only supports DASD devices (harddisks)\n",
+ VER_MAJOR, VER_MINOR, VER_SUB);
+
+ printk("Registered HBAs:\n");
+ printk("HBA no. Boardtype: Revis: EATA: Bus: BaseIO: IRQ: Ch: ID: Pr:"
+ " QS: SG: CPL:\n");
+ for (i = 1; i <= registered_HBAs; i++) {
+ printk("scsi%-2d: %.10s v%s 2.0%c %s %#.4x %2d %d %d %c"
+ " %2d %2d %2d\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name, SD(HBA_ptr)->revision,
+ SD(HBA_ptr)->EATA_revision, (SD(HBA_ptr)->bustype == 'P')?
+ "PCI ":(SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ",
+ (uint) HBA_ptr->base, HBA_ptr->irq, SD(HBA_ptr)->channel,
+ HBA_ptr->this_id, (SD(HBA_ptr)->primary == TRUE)?'Y':'N',
+ HBA_ptr->can_queue, HBA_ptr->sg_tablesize,
+ HBA_ptr->cmd_per_lun);
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+ }
+ DBG(DPT_DEBUG,DELAY(12));
+
+ return (registered_HBAs);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = EATA_PIO;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost 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/i386/i386at/gpl/linux/scsi/eata_pio.h b/i386/i386at/gpl/linux/scsi/eata_pio.h
new file mode 100644
index 00000000..8a626e0b
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_pio.h
@@ -0,0 +1,116 @@
+/********************************************************
+* Header file for eata_pio.c Linux EATA-PIO SCSI driver *
+* (c) 1993,94,95 Michael Neuffer *
+*********************************************************
+* last change: 95/06/21 *
+********************************************************/
+
+
+#ifndef _EATA_PIO_H
+#define _EATA_PIO_H
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include <linux/scsicam.h>
+
+#ifndef HOSTS_C
+#include "eata_generic.h"
+
+#define VER_MAJOR 0
+#define VER_MINOR 0
+#define VER_SUB "1b"
+
+/************************************************************************
+ * Here you can switch parts of the code on and of *
+ ************************************************************************/
+
+#define VERBOSE_SETUP /* show startup screen of 2001 */
+#define ALLOW_DMA_BOARDS 1
+
+/************************************************************************
+ * Debug options. *
+ * Enable DEBUG and whichever options you require. *
+ ************************************************************************/
+#define DEBUG_EATA 1 /* Enable debug code. */
+#define DPT_DEBUG 0 /* Bobs special */
+#define DBG_DELAY 0 /* Build in delays so debug messages can be
+ * be read before they vanish of the top of
+ * the screen!
+ */
+#define DBG_PROBE 0 /* Debug probe routines. */
+#define DBG_ISA 0 /* Trace ISA routines */
+#define DBG_EISA 0 /* Trace EISA routines */
+#define DBG_PCI 0 /* Trace PCI routines */
+#define DBG_PIO 0 /* Trace get_config_PIO */
+#define DBG_COM 0 /* Trace command call */
+#define DBG_QUEUE 0 /* Trace command queueing. */
+#define DBG_INTR 0 /* Trace interrupt service routine. */
+#define DBG_INTR2 0 /* Trace interrupt service routine. */
+#define DBG_PROC 0 /* Debug proc-fs related statistics */
+#define DBG_PROC_WRITE 0
+#define DBG_REGISTER 0 /* */
+#define DBG_ABNORM 1 /* Debug abnormal actions (reset, abort) */
+
+#if DEBUG_EATA
+#define DBG(x, y) if ((x)) {y;}
+#else
+#define DBG(x, y)
+#endif
+
+#endif /* !HOSTS_C */
+
+int eata_pio_detect(Scsi_Host_Template *);
+const char *eata_pio_info(struct Scsi_Host *);
+int eata_pio_command(Scsi_Cmnd *);
+int eata_pio_queue(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int eata_pio_abort(Scsi_Cmnd *);
+int eata_pio_reset(Scsi_Cmnd *);
+int eata_pio_proc_info(char *, char **, off_t, int, int, int);
+#ifdef MODULE
+int eata_pio_release(struct Scsi_Host *);
+#else
+#define eata_pio_release NULL
+#endif
+
+
+#define EATA_PIO { \
+ NULL, NULL, \
+ NULL, /* proc_dir_entry */ \
+ eata_pio_proc_info, /* procinfo */ \
+ "EATA (Extended Attachment) PIO driver", \
+ eata_pio_detect, \
+ eata_pio_release, \
+ NULL, NULL, \
+ eata_pio_queue, \
+ eata_pio_abort, \
+ eata_pio_reset, \
+ NULL, /* Slave attach */ \
+ scsicam_bios_param, \
+ 0, /* Canqueue */ \
+ 0, /* this_id */ \
+ 0, /* sg_tablesize */ \
+ 0, /* cmd_per_lun */ \
+ 0, /* present */ \
+ 1, /* True if ISA */ \
+ ENABLE_CLUSTERING }
+
+#endif /* _EATA_PIO_H */
+
+/*
+ * Overrides for Emacs so that we almost 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
+ * tab-width: 8
+ * End:
+ */
diff --git a/i386/i386at/gpl/linux/scsi/eata_pio_proc.src b/i386/i386at/gpl/linux/scsi/eata_pio_proc.src
new file mode 100644
index 00000000..b5480091
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/eata_pio_proc.src
@@ -0,0 +1,150 @@
+
+#define MAX_SCSI_DEVICE_CODE 10
+const char *const pio_scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
+{
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications "
+};
+
+/*
+ * eata_set_info
+ * buffer : pointer to the data that has been written to the hostfile
+ * length : number of bytes written to the hostfile
+ * HBA_ptr: pointer to the Scsi_Host struct
+ */
+int eata_pio_set_info(char *buffer, int length, struct Scsi_Host *HBA_ptr)
+{
+ DBG(DBG_PROC_WRITE, printk("%s\n", buffer));
+ return(-ENOSYS); /* Currently this is a no-op */
+}
+
+/*
+ * eata_proc_info
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is beeing written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int eata_pio_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+
+ Scsi_Device *scd;
+ struct Scsi_Host *HBA_ptr;
+ static u8 buff[512];
+ int i;
+ int size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ HBA_ptr = first_HBA;
+ for (i = 1; i <= registered_HBAs; i++) {
+ if (HBA_ptr->host_no == hostno)
+ break;
+ HBA_ptr = SD(HBA_ptr)->next;
+ }
+
+ if(inout == TRUE) /* Has data been writen to the file ? */
+ return(eata_pio_set_info(buffer, length, HBA_ptr));
+
+ if (offset == 0)
+ memset(buff, 0, sizeof(buff));
+
+ size = sprintf(buffer+len, "EATA (Extended Attachment) PIO driver version: "
+ "%d.%d%s\n",VER_MAJOR, VER_MINOR, VER_SUB);
+ len += size; pos = begin + len;
+ size = sprintf(buffer + len, "queued commands: %10ld\n"
+ "processed interrupts:%10ld\n", queue_counter, int_counter);
+ len += size; pos = begin + len;
+
+ size = sprintf(buffer + len, "\nscsi%-2d: HBA %.10s\n",
+ HBA_ptr->host_no, SD(HBA_ptr)->name);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Firmware revision: v%s\n",
+ SD(HBA_ptr)->revision);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "IO: PIO\n");
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Base IO : %#.4x\n", (u32) HBA_ptr->base);
+ len += size;
+ pos = begin + len;
+ size = sprintf(buffer + len, "Host Bus: %s\n",
+ (SD(HBA_ptr)->bustype == 'P')?"PCI ":
+ (SD(HBA_ptr)->bustype == 'E')?"EISA":"ISA ");
+
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+
+ scd = scsi_devices;
+
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+
+ stop_output:
+ DBG(DBG_PROC, printk("2pos: %ld offset: %ld len: %d\n", pos, offset, len));
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ DBG(DBG_PROC, printk("3pos: %ld offset: %ld len: %d\n", pos, offset, len));
+
+ return (len);
+}
+
+/*
+ * 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
+ * tab-width: 8
+ * End:
+ */
+
diff --git a/i386/i386at/gpl/linux/scsi/fdomain.c b/i386/i386at/gpl/linux/scsi/fdomain.c
new file mode 100644
index 00000000..54507046
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/fdomain.c
@@ -0,0 +1,2016 @@
+/* fdomain.c -- Future Domain TMC-16x0 SCSI driver
+ * Created: Sun May 3 18:53:19 1992 by faith@cs.unc.edu
+ * Revised: Thu Oct 12 15:59:37 1995 by r.faith@ieee.org
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
+ *
+ * $Id: fdomain.c,v 1.1.1.1 1997/02/25 21:27:49 thomas Exp $
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ **************************************************************************
+
+ DESCRIPTION:
+
+ This is the Linux low-level SCSI driver for Future Domain TMC-1660/1680
+ TMC-1650/1670, and TMC-3260 SCSI host adapters. The 1650 and 1670 have a
+ 25-pin external connector, whereas the 1660 and 1680 have a SCSI-2 50-pin
+ high-density external connector. The 1670 and 1680 have floppy disk
+ controllers built in. The TMC-3260 is a PCI bus card.
+
+ Future Domain's older boards are based on the TMC-1800 chip, and this
+ driver was originally written for a TMC-1680 board with the TMC-1800 chip.
+ More recently, boards are being produced with the TMC-18C50 and TMC-18C30
+ chips. The latest and greatest board may not work with this driver. If
+ you have to patch this driver so that it will recognize your board's BIOS
+ signature, then the driver may fail to function after the board is
+ detected.
+
+ The following BIOS versions are supported: 2.0, 3.0, 3.2, 3.4, and 3.5.
+ The following chips are supported: TMC-1800, TMC-18C50, TMC-18C30.
+ Reports suggest that the driver will also work with the 36C70 chip and
+ with the Quantum ISA-200S and ISA-250MG SCSI adapters.
+
+ Please note that the drive ordering that Future Domain implemented in BIOS
+ versions 3.4 and 3.5 is the opposite of the order (currently) used by the
+ rest of the SCSI industry. If you have BIOS version 3.4 or 3.5, and have
+ more then one drive, then the drive ordering will be the reverse of that
+ which you see under DOS. For example, under DOS SCSI ID 0 will be D: and
+ SCSI ID 1 will be C: (the boot device). Under Linux, SCSI ID 0 will be
+ /dev/sda and SCSI ID 1 will be /dev/sdb. The Linux ordering is consistent
+ with that provided by all the other SCSI drivers for Linux. If you want
+ this changed, send me patches that are protected by #ifdefs.
+
+ If you have a TMC-8xx or TMC-9xx board, then this is not the driver for
+ your board. Please refer to the Seagate driver for more information and
+ possible support.
+
+
+
+ REFERENCES USED:
+
+ "TMC-1800 SCSI Chip Specification (FDC-1800T)", Future Domain Corporation,
+ 1990.
+
+ "Technical Reference Manual: 18C50 SCSI Host Adapter Chip", Future Domain
+ Corporation, January 1992.
+
+ "LXT SCSI Products: Specifications and OEM Technical Manual (Revision
+ B/September 1991)", Maxtor Corporation, 1991.
+
+ "7213S product Manual (Revision P3)", Maxtor Corporation, 1992.
+
+ "Draft Proposed American National Standard: Small Computer System
+ Interface - 2 (SCSI-2)", Global Engineering Documents. (X3T9.2/86-109,
+ revision 10h, October 17, 1991)
+
+ Private communications, Drew Eckhardt (drew@cs.colorado.edu) and Eric
+ Youngdale (ericy@cais.com), 1992.
+
+ Private communication, Tuong Le (Future Domain Engineering department),
+ 1994. (Disk geometry computations for Future Domain BIOS version 3.4, and
+ TMC-18C30 detection.)
+
+ Hogan, Thom. The Programmer's PC Sourcebook. Microsoft Press, 1988. Page
+ 60 (2.39: Disk Partition Table Layout).
+
+ "18C30 Technical Reference Manual", Future Domain Corporation, 1993, page
+ 6-1.
+
+
+
+ NOTES ON REFERENCES:
+
+ The Maxtor manuals were free. Maxtor telephone technical support is
+ great!
+
+ The Future Domain manuals were $25 and $35. They document the chip, not
+ the TMC-16x0 boards, so some information I had to guess at. In 1992,
+ Future Domain sold DOS BIOS source for $250 and the UN*X driver source was
+ $750, but these required a non-disclosure agreement, so even if I could
+ have afforded them, they would *not* have been useful for writing this
+ publically distributable driver. Future Domain technical support has
+ provided some information on the phone and have sent a few useful FAXs.
+ They have been much more helpful since they started to recognize that the
+ word "Linux" refers to an operating system :-).
+
+
+
+ ALPHA TESTERS:
+
+ There are many other alpha testers that come and go as the driver
+ develops. The people listed here were most helpful in times of greatest
+ need (mostly early on -- I've probably left out a few worthy people in
+ more recent times):
+
+ Todd Carrico (todd@wutc.wustl.edu), Dan Poirier (poirier@cs.unc.edu ), Ken
+ Corey (kenc@sol.acs.unt.edu), C. de Bruin (bruin@bruin@sterbbs.nl), Sakari
+ Aaltonen (sakaria@vipunen.hit.fi), John Rice (rice@xanth.cs.odu.edu), Brad
+ Yearwood (brad@optilink.com), and Ray Toy (toy@soho.crd.ge.com).
+
+ Special thanks to Tien-Wan Yang (twyang@cs.uh.edu), who graciously lent me
+ his 18C50-based card for debugging. He is the sole reason that this
+ driver works with the 18C50 chip.
+
+ Thanks to Dave Newman (dnewman@crl.com) for providing initial patches for
+ the version 3.4 BIOS.
+
+ Thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for providing
+ patches that support the TMC-3260, a PCI bus card with the 36C70 chip.
+ The 36C70 chip appears to be "completely compatible" with the 18C30 chip.
+
+ Thanks to Eric Kasten (tigger@petroglyph.cl.msu.edu) for providing the
+ patch for the version 3.5 BIOS.
+
+ Thanks for Stephen Henson (shenson@nyx10.cs.du.edu) for providing the
+ patch for the Quantum ISA-200S SCSI adapter.
+
+ Thanks to Adam Bowen for the signature to the 1610M/MER/MEX scsi cards, to
+ Martin Andrews (andrewm@ccfadm.eeg.ccf.org) for the signature to some
+ random TMC-1680 repackaged by IBM; and to Mintak Ng (mintak@panix.com) for
+ the version 3.61 BIOS siganture.
+
+ Thanks for Mark Singer (elf@netcom.com) and Richard Simpson
+ (rsimpson@ewrcsdra.demon.co.uk) for more Quantum signatures and detective
+ work on the Quantum RAM layout.
+
+ Special thanks to James T. McKinley (mckinley@msupa.pa.msu.edu) for
+ providing patches for proper PCI BIOS32-mediated detection of the TMC-3260
+ card (a PCI bus card with the 36C70 chip). Please send James PCI-related
+ bug reports.
+
+ Thanks to Tom Cavin (tec@usa1.com) for preliminary command-line option
+ patches.
+
+ All of the alpha testers deserve much thanks.
+
+
+
+ NOTES ON USER DEFINABLE OPTIONS:
+
+ DEBUG: This turns on the printing of various debug information.
+
+ ENABLE_PARITY: This turns on SCSI parity checking. With the current
+ driver, all attached devices must support SCSI parity. If none of your
+ devices support parity, then you can probably get the driver to work by
+ turning this option off. I have no way of testing this, however.
+
+ FIFO_COUNT: The host adapter has an 8K cache (host adapters based on the
+ 18C30 chip have a 2k cache). When this many 512 byte blocks are filled by
+ the SCSI device, an interrupt will be raised. Therefore, this could be as
+ low as 0, or as high as 16. Note, however, that values which are too high
+ or too low seem to prevent any interrupts from occurring, and thereby lock
+ up the machine. I have found that 2 is a good number, but throughput may
+ be increased by changing this value to values which are close to 2.
+ Please let me know if you try any different values.
+
+ DO_DETECT: This activates some old scan code which was needed before the
+ high level drivers got fixed. If you are having trouble with the driver,
+ turning this on should not hurt, and might help. Please let me know if
+ this is the case, since this code will be removed from future drivers.
+
+ RESELECTION: This is no longer an option, since I gave up trying to
+ implement it in version 4.x of this driver. It did not improve
+ performance at all and made the driver unstable (because I never found one
+ of the two race conditions which were introduced by the multiple
+ outstanding command code). The instability seems a very high price to pay
+ just so that you don't have to wait for the tape to rewind. If you want
+ this feature implemented, send me patches. I'll be happy to send a copy
+ of my (broken) driver to anyone who would like to see a copy.
+
+ **************************************************************************/
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "fdomain.h"
+#include <asm/system.h>
+#include <linux/errno.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/bios32.h>
+#include <linux/pci.h>
+#include <linux/stat.h>
+
+#include <linux/config.h> /* for CONFIG_PCI */
+
+struct proc_dir_entry proc_scsi_fdomain = {
+ PROC_SCSI_FDOMAIN, 7, "fdomain",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define VERSION "$Revision: 1.1.1.1 $"
+
+/* START OF USER DEFINABLE OPTIONS */
+
+#define DEBUG 1 /* Enable debugging output */
+#define ENABLE_PARITY 1 /* Enable SCSI Parity */
+#define FIFO_COUNT 2 /* Number of 512 byte blocks before INTR */
+#define DO_DETECT 0 /* Do device detection here (see scsi.c) */
+
+/* END OF USER DEFINABLE OPTIONS */
+
+#if DEBUG
+#define EVERY_ACCESS 0 /* Write a line on every scsi access */
+#define ERRORS_ONLY 1 /* Only write a line if there is an error */
+#define DEBUG_DETECT 0 /* Debug fdomain_16x0_detect() */
+#define DEBUG_MESSAGES 1 /* Debug MESSAGE IN phase */
+#define DEBUG_ABORT 1 /* Debug abort() routine */
+#define DEBUG_RESET 1 /* Debug reset() routine */
+#define DEBUG_RACE 1 /* Debug interrupt-driven race condition */
+#else
+#define EVERY_ACCESS 0 /* LEAVE THESE ALONE--CHANGE THE ONES ABOVE */
+#define ERRORS_ONLY 0
+#define DEBUG_DETECT 0
+#define DEBUG_MESSAGES 0
+#define DEBUG_ABORT 0
+#define DEBUG_RESET 0
+#define DEBUG_RACE 0
+#endif
+
+/* Errors are reported on the line, so we don't need to report them again */
+#if EVERY_ACCESS
+#undef ERRORS_ONLY
+#define ERRORS_ONLY 0
+#endif
+
+#if ENABLE_PARITY
+#define PARITY_MASK 0x08
+#else
+#define PARITY_MASK 0x00
+#endif
+
+enum chip_type {
+ unknown = 0x00,
+ tmc1800 = 0x01,
+ tmc18c50 = 0x02,
+ tmc18c30 = 0x03,
+};
+
+enum {
+ in_arbitration = 0x02,
+ in_selection = 0x04,
+ in_other = 0x08,
+ disconnect = 0x10,
+ aborted = 0x20,
+ sent_ident = 0x40,
+};
+
+enum in_port_type {
+ Read_SCSI_Data = 0,
+ SCSI_Status = 1,
+ TMC_Status = 2,
+ FIFO_Status = 3, /* tmc18c50/tmc18c30 only */
+ Interrupt_Cond = 4, /* tmc18c50/tmc18c30 only */
+ LSB_ID_Code = 5,
+ MSB_ID_Code = 6,
+ Read_Loopback = 7,
+ SCSI_Data_NoACK = 8,
+ Interrupt_Status = 9,
+ Configuration1 = 10,
+ Configuration2 = 11, /* tmc18c50/tmc18c30 only */
+ Read_FIFO = 12,
+ FIFO_Data_Count = 14
+};
+
+enum out_port_type {
+ Write_SCSI_Data = 0,
+ SCSI_Cntl = 1,
+ Interrupt_Cntl = 2,
+ SCSI_Mode_Cntl = 3,
+ TMC_Cntl = 4,
+ Memory_Cntl = 5, /* tmc18c50/tmc18c30 only */
+ Write_Loopback = 7,
+ IO_Control = 11, /* tmc18c30 only */
+ Write_FIFO = 12
+};
+
+static int port_base = 0;
+static void *bios_base = NULL;
+static int bios_major = 0;
+static int bios_minor = 0;
+static int PCI_bus = 0;
+static int Quantum = 0; /* Quantum board variant */
+static int interrupt_level = 0;
+static volatile int in_command = 0;
+static Scsi_Cmnd *current_SC = NULL;
+static enum chip_type chip = unknown;
+static int adapter_mask = 0;
+static int this_id = 0;
+static int setup_called = 0;
+
+#if DEBUG_RACE
+static volatile int in_interrupt_flag = 0;
+#endif
+
+static int SCSI_Mode_Cntl_port;
+static int FIFO_Data_Count_port;
+static int Interrupt_Cntl_port;
+static int Interrupt_Status_port;
+static int Read_FIFO_port;
+static int Read_SCSI_Data_port;
+static int SCSI_Cntl_port;
+static int SCSI_Data_NoACK_port;
+static int SCSI_Status_port;
+static int TMC_Cntl_port;
+static int TMC_Status_port;
+static int Write_FIFO_port;
+static int Write_SCSI_Data_port;
+
+static int FIFO_Size = 0x2000; /* 8k FIFO for
+ pre-tmc18c30 chips */
+
+extern void fdomain_16x0_intr( int irq, struct pt_regs * regs );
+
+static void *addresses[] = {
+ (void *)0xc8000,
+ (void *)0xca000,
+ (void *)0xce000,
+ (void *)0xde000,
+ (void *)0xd0000, /* Extra addresses for PCI boards */
+ (void *)0xe0000,
+};
+#define ADDRESS_COUNT (sizeof( addresses ) / sizeof( unsigned ))
+
+static unsigned short ports[] = { 0x140, 0x150, 0x160, 0x170 };
+#define PORT_COUNT (sizeof( ports ) / sizeof( unsigned short ))
+
+static unsigned short ints[] = { 3, 5, 10, 11, 12, 14, 15, 0 };
+
+/*
+
+ READ THIS BEFORE YOU ADD A SIGNATURE!
+
+ READING THIS SHORT NOTE CAN SAVE YOU LOTS OF TIME!
+
+ READ EVERY WORD, ESPECIALLY THE WORD *NOT*
+
+ This driver works *ONLY* for Future Domain cards using the TMC-1800,
+ TMC-18C50, or TMC-18C30 chip. This includes models TMC-1650, 1660, 1670,
+ and 1680.
+
+ The following BIOS signature signatures are for boards which do *NOT*
+ work with this driver (these TMC-8xx and TMC-9xx boards may work with the
+ Seagate driver):
+
+ FUTURE DOMAIN CORP. (C) 1986-1988 V4.0I 03/16/88
+ FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89
+ FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89
+ FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90
+ FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90
+ FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90
+ FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92
+
+*/
+
+struct signature {
+ const char *signature;
+ int sig_offset;
+ int sig_length;
+ int major_bios_version;
+ int minor_bios_version;
+ int flag; /* 1 == PCI_bus, 2 == ISA_200S, 3 == ISA_250MG, 4 == ISA_200S */
+} signatures[] = {
+ /* 1 2 3 4 5 6 */
+ /* 123456789012345678901234567890123456789012345678901234567890 */
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 5, 50, 2, 0, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V1.07/28/89", 5, 50, 2, 0, 0 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.07/28/89", 72, 50, 2, 0, 2 },
+ { "FUTURE DOMAIN CORP. (C) 1986-1990 1800-V2.0", 73, 43, 2, 0, 3 },
+ { "FUTURE DOMAIN CORP. (C) 1991 1800-V2.0.", 72, 39, 2, 0, 4 },
+ { "FUTURE DOMAIN CORP. (C) 1992 V3.00.004/02/92", 5, 44, 3, 0, 0 },
+ { "FUTURE DOMAIN TMC-18XX (C) 1993 V3.203/12/93", 5, 44, 3, 2, 0 },
+ { "IBM F1 P2 BIOS v1.0104/29/93", 5, 28, 3, -1, 0 },
+ { "Future Domain Corp. V1.0008/18/93", 5, 33, 3, 4, 0 },
+ { "Future Domain Corp. V1.0008/18/93", 26, 33, 3, 4, 1 },
+ /* This next signature may not be a 3.5 bios */
+ { "Future Domain Corp. V2.0108/18/93", 5, 33, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.5008/18/93", 5, 34, 3, 5, 0 },
+ { "FUTURE DOMAIN 18c30/18c50/1800 (C) 1994 V3.5", 5, 44, 3, 5, 0 },
+ { "FUTURE DOMAIN CORP. V3.6008/18/93", 5, 34, 3, 6, 0 },
+ { "FUTURE DOMAIN CORP. V3.6108/18/93", 5, 34, 3, 6, 0 },
+ { "FUTURE DOMAIN TMC-18XX", 5, 22, -1, -1, 0 },
+
+ /* READ NOTICE ABOVE *BEFORE* YOU WASTE YOUR TIME ADDING A SIGNATURE
+ Also, fix the disk geometry code for your signature and send your
+ changes for faith@cs.unc.edu. Above all, do *NOT* change any old
+ signatures!
+
+ Note that the last line will match a "generic" 18XX bios. Because
+ Future Domain has changed the host SCSI ID and/or the location of the
+ geometry information in the on-board RAM area for each of the first
+ three BIOS's, it is still important to enter a fully qualified
+ signature in the table for any new BIOS's (after the host SCSI ID and
+ geometry location are verified). */
+};
+
+#define SIGNATURE_COUNT (sizeof( signatures ) / sizeof( struct signature ))
+
+static void print_banner( struct Scsi_Host *shpnt )
+{
+ if (!shpnt) return; /* This won't ever happen */
+
+ if (bios_major < 0 && bios_minor < 0) {
+ printk( "scsi%d <fdomain>: No BIOS; using scsi id %d\n",
+ shpnt->host_no, shpnt->this_id );
+ } else {
+ printk( "scsi%d <fdomain>: BIOS version ", shpnt->host_no );
+
+ if (bios_major >= 0) printk( "%d.", bios_major );
+ else printk( "?." );
+
+ if (bios_minor >= 0) printk( "%d", bios_minor );
+ else printk( "?." );
+
+ printk( " at 0x%x using scsi id %d\n",
+ (unsigned)bios_base, shpnt->this_id );
+ }
+
+ /* If this driver works for later FD PCI
+ boards, we will have to modify banner
+ for additional PCI cards, but for now if
+ it's PCI it's a TMC-3260 - JTM */
+ printk( "scsi%d <fdomain>: %s chip at 0x%x irq ",
+ shpnt->host_no,
+ chip == tmc1800 ? "TMC-1800"
+ : (chip == tmc18c50 ? "TMC-18C50"
+ : (chip == tmc18c30 ?
+ (PCI_bus ? "TMC-36C70 (PCI bus)" : "TMC-18C30")
+ : "Unknown")),
+ port_base );
+
+ if (interrupt_level) printk( "%d", interrupt_level );
+ else printk( "<none>" );
+
+ printk( "\n" );
+}
+
+void fdomain_setup( char *str, int *ints )
+{
+ if (setup_called++ || ints[0] < 2 || ints[0] > 3) {
+ printk( "fdomain: usage: fdomain=<PORT_BASE>,<IRQ>[,<ADAPTER_ID>]\n" );
+ printk( "fdomain: bad LILO parameters?\n" );
+ }
+
+ port_base = ints[0] >= 1 ? ints[1] : 0;
+ interrupt_level = ints[0] >= 2 ? ints[2] : 0;
+ this_id = ints[0] >= 3 ? ints[3] : 0;
+
+ bios_major = bios_minor = -1; /* Use geometry for BIOS version >= 3.4 */
+}
+
+
+static void do_pause( unsigned amount ) /* Pause for amount*10 milliseconds */
+{
+ unsigned long the_time = jiffies + amount; /* 0.01 seconds per jiffy */
+
+ while (jiffies < the_time);
+}
+
+inline static void fdomain_make_bus_idle( void )
+{
+ outb( 0, SCSI_Cntl_port );
+ outb( 0, SCSI_Mode_Cntl_port );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ outb( 0x21 | PARITY_MASK, TMC_Cntl_port ); /* Clear forced intr. */
+ else
+ outb( 0x01 | PARITY_MASK, TMC_Cntl_port );
+}
+
+static int fdomain_is_valid_port( int port )
+{
+#if DEBUG_DETECT
+ printk( " (%x%x),",
+ inb( port + MSB_ID_Code ), inb( port + LSB_ID_Code ) );
+#endif
+
+ /* The MCA ID is a unique id for each MCA compatible board. We
+ are using ISA boards, but Future Domain provides the MCA ID
+ anyway. We can use this ID to ensure that this is a Future
+ Domain TMC-1660/TMC-1680.
+ */
+
+ if (inb( port + LSB_ID_Code ) != 0xe9) { /* test for 0x6127 id */
+ if (inb( port + LSB_ID_Code ) != 0x27) return 0;
+ if (inb( port + MSB_ID_Code ) != 0x61) return 0;
+ chip = tmc1800;
+ } else { /* test for 0xe960 id */
+ if (inb( port + MSB_ID_Code ) != 0x60) return 0;
+ chip = tmc18c50;
+
+#if 0
+
+ /* Try to toggle 32-bit mode. This only
+ works on an 18c30 chip. (User reports
+ say that this doesn't work at all, so
+ we'll use the other method.) */
+
+ outb( 0x80, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x80) {
+ outb( 0x00, port + IO_Control );
+ if ((inb( port + Configuration2 ) & 0x80) == 0x00) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+ }
+ }
+#else
+
+ /* That should have worked, but appears to
+ have problems. Lets assume it is an
+ 18c30 if the RAM is disabled. */
+
+ if (inb( port + Configuration2 ) & 0x02) {
+ chip = tmc18c30;
+ FIFO_Size = 0x800; /* 2k FIFO */
+ }
+#endif
+ /* If that failed, we are an 18c50. */
+ }
+
+ return 1;
+}
+
+static int fdomain_test_loopback( void )
+{
+ int i;
+ int result;
+
+ for (i = 0; i < 255; i++) {
+ outb( i, port_base + Write_Loopback );
+ result = inb( port_base + Read_Loopback );
+ if (i != result)
+ return 1;
+ }
+ return 0;
+}
+
+/* fdomain_get_irq assumes that we have a valid MCA ID for a
+ TMC-1660/TMC-1680 Future Domain board. Now, check to be sure the
+ bios_base matches these ports. If someone was unlucky enough to have
+ purchased more than one Future Domain board, then they will have to
+ modify this code, as we only detect one board here. [The one with the
+ lowest bios_base.]
+
+ Note that this routine is only used for systems without a PCI BIOS32
+ (e.g., ISA bus). For PCI bus systems, this routine will likely fail
+ unless one of the IRQs listed in the ints array is used by the board.
+ Sometimes it is possible to use the computer's BIOS setup screen to
+ configure a PCI system so that one of these IRQs will be used by the
+ Future Domain card. */
+
+static int fdomain_get_irq( int base )
+{
+ int options = inb( base + Configuration1 );
+
+#if DEBUG_DETECT
+ printk( " Options = %x\n", options );
+#endif
+
+ /* Check for board with lowest bios_base --
+ this isn't valid for the 18c30 or for
+ boards on the PCI bus, so just assume we
+ have the right board. */
+
+ if (chip != tmc18c30
+ && !PCI_bus
+ && addresses[ (options & 0xc0) >> 6 ] != bios_base) return 0;
+
+ return ints[ (options & 0x0e) >> 1 ];
+}
+
+static int fdomain_isa_detect( int *irq, int *iobase )
+{
+ int i;
+ int base;
+ int flag = 0;
+
+ if (bios_major == 2) {
+ /* The TMC-1660/TMC-1680 has a RAM area just after the BIOS ROM.
+ Assuming the ROM is enabled (otherwise we wouldn't have been
+ able to read the ROM signature :-), then the ROM sets up the
+ RAM area with some magic numbers, such as a list of port
+ base addresses and a list of the disk "geometry" reported to
+ DOS (this geometry has nothing to do with physical geometry).
+ */
+
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ case 3: /* ISA_250MG */
+ base = *((char *)bios_base + 0x1fa2)
+ + (*((char *)bios_base + 0x1fa3) << 8);
+ break;
+ case 4: /* ISA_200S (another one) */
+ base = *((char *)bios_base + 0x1fa3)
+ + (*((char *)bios_base + 0x1fa4) << 8);
+ break;
+ default:
+ base = *((char *)bios_base + 0x1fcc)
+ + (*((char *)bios_base + 0x1fcd) << 8);
+ break;
+ }
+
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+
+ for (flag = 0, i = 0; !flag && i < PORT_COUNT; i++) {
+ if (base == ports[i])
+ ++flag;
+ }
+
+ if (flag && fdomain_is_valid_port( base )) {
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+ return 1;
+ }
+
+ /* This is a bad sign. It usually means that someone patched the
+ BIOS signature list (the signatures variable) to contain a BIOS
+ signature for a board *OTHER THAN* the TMC-1660/TMC-1680. */
+
+#if DEBUG_DETECT
+ printk( " RAM FAILED, " );
+#endif
+ }
+
+ /* Anyway, the alternative to finding the address in the RAM is to just
+ search through every possible port address for one that is attached
+ to the Future Domain card. Don't panic, though, about reading all
+ these random port addresses -- there are rumors that the Future
+ Domain BIOS does something very similar.
+
+ Do not, however, check ports which the kernel knows are being used by
+ another driver. */
+
+ for (i = 0; i < PORT_COUNT; i++) {
+ base = ports[i];
+ if (check_region( base, 0x10 )) {
+#if DEBUG_DETECT
+ printk( " (%x inuse),", base );
+#endif
+ continue;
+ }
+#if DEBUG_DETECT
+ printk( " %x,", base );
+#endif
+ if ((flag = fdomain_is_valid_port( base ))) break;
+ }
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( base );
+ *iobase = base;
+
+ return 1; /* success */
+}
+
+static int fdomain_pci_nobios_detect( int *irq, int *iobase )
+{
+ int i;
+ int flag = 0;
+
+ /* The proper way of doing this is to use ask the PCI bus for the device
+ IRQ and interrupt level. But we can't do that if PCI BIOS32 support
+ isn't compiled into the kernel, or if a PCI BIOS32 isn't present.
+
+ Instead, we scan down a bunch of addresses (Future Domain tech
+ support says we will probably find the address before we get to
+ 0xf800). This works fine on some systems -- other systems may have
+ to scan more addresses. If you have to modify this section for your
+ installation, please send mail to faith@cs.unc.edu. */
+
+ for (i = 0xfff8; i > 0xe000; i -= 8) {
+ if (check_region( i, 0x10 )) {
+#if DEBUG_DETECT
+ printk( " (%x inuse)," , i );
+#endif
+ continue;
+ }
+ if ((flag = fdomain_is_valid_port( i ))) break;
+ }
+
+ if (!flag) return 0; /* iobase not found */
+
+ *irq = fdomain_get_irq( i );
+ *iobase = i;
+
+ return 1; /* success */
+}
+
+/* PCI detection function: int fdomain_pci_bios_detect(int* irq, int*
+ iobase) This function gets the Interrupt Level and I/O base address from
+ the PCI configuration registers. The I/O base address is masked with
+ 0xfff8 since on my card the address read from the PCI config registers
+ is off by one from the actual I/O base address necessary for accessing
+ the status and control registers on the card (PCI config register gives
+ 0xf801, actual address is 0xf800). This is likely a bug in the FD
+ config code that writes to the PCI registers, however using a mask
+ should be safe since I think the scan done by the card to determine the
+ I/O base is done in increments of 8 (i.e., 0xf800, 0xf808, ...), at
+ least the old scan code we used to use to get the I/O base did... Also,
+ the device ID from the PCI config registers is 0x0 and should be 0x60e9
+ as it is in the status registers (offset 5 from I/O base). If this is
+ changed in future hardware/BIOS changes it will need to be fixed in this
+ detection function. Comments, bug reports, etc... on this function
+ should be sent to mckinley@msupa.pa.msu.edu - James T. McKinley. */
+
+#ifdef CONFIG_PCI
+static int fdomain_pci_bios_detect( int *irq, int *iobase )
+{
+ int error;
+ unsigned char pci_bus, pci_dev_fn; /* PCI bus & device function */
+ unsigned char pci_irq; /* PCI interrupt line */
+ unsigned int pci_base; /* PCI I/O base address */
+ unsigned short pci_vendor, pci_device; /* PCI vendor & device IDs */
+
+ /* If the PCI BIOS doesn't exist, use the old-style detection routines.
+ Otherwise, get the I/O base address and interrupt from the PCI config
+ registers. */
+
+ if (!pcibios_present()) return fdomain_pci_nobios_detect( irq, iobase );
+
+#if DEBUG_DETECT
+ /* Tell how to print a list of the known PCI devices from bios32 and
+ list vendor and device IDs being used if in debug mode. */
+
+ printk( "\nINFO: cat /proc/pci to see list of PCI devices from bios32\n" );
+ printk( "\nTMC-3260 detect:"
+ " Using PCI Vendor ID: 0x%x, PCI Device ID: 0x%x\n",
+ PCI_VENDOR_ID_FD,
+ PCI_DEVICE_ID_FD_36C70 );
+#endif
+
+ /* We will have to change this if more than 1 PCI bus is present and the
+ FD scsi host is not on the first bus (i.e., a PCI to PCI bridge,
+ which is not supported by bios32 right now anyway). This should
+ probably be done by a call to pcibios_find_device but I can't get it
+ to work... Also the device ID reported from the PCI config registers
+ does not match the device ID quoted in the tech manual or available
+ from offset 5 from the I/O base address. It should be 0x60E9, but it
+ is 0x0 if read from the PCI config registers. I guess the FD folks
+ neglected to write it to the PCI registers... This loop is necessary
+ to get the device function (at least until someone can get
+ pcibios_find_device to work, I cannot but 53c7,8xx.c uses it...). */
+
+ pci_bus = 0;
+
+ for (pci_dev_fn = 0x0; pci_dev_fn < 0xff; pci_dev_fn++) {
+ pcibios_read_config_word( pci_bus,
+ pci_dev_fn,
+ PCI_VENDOR_ID,
+ &pci_vendor );
+
+ if (pci_vendor == PCI_VENDOR_ID_FD) {
+ pcibios_read_config_word( pci_bus,
+ pci_dev_fn,
+ PCI_DEVICE_ID,
+ &pci_device );
+
+ if (pci_device == PCI_DEVICE_ID_FD_36C70) {
+ /* Break out once we have the correct device. If other FD
+ PCI devices are added to this driver we will need to add
+ an or of the other PCI_DEVICE_ID_FD_XXXXX's here. */
+ break;
+ } else {
+ /* If we can't find an FD scsi card we give up. */
+ return 0;
+ }
+ }
+ }
+
+#if DEBUG_DETECT
+ printk( "Future Domain 36C70 : at PCI bus %u, device %u, function %u\n",
+ pci_bus,
+ (pci_dev_fn & 0xf8) >> 3,
+ pci_dev_fn & 7 );
+#endif
+
+ /* We now have the appropriate device function for the FD board so we
+ just read the PCI config info from the registers. */
+
+ if ((error = pcibios_read_config_dword( pci_bus,
+ pci_dev_fn,
+ PCI_BASE_ADDRESS_0,
+ &pci_base ))
+ || (error = pcibios_read_config_byte( pci_bus,
+ pci_dev_fn,
+ PCI_INTERRUPT_LINE,
+ &pci_irq ))) {
+ printk ( "PCI ERROR: Future Domain 36C70 not initializing"
+ " due to error reading configuration space\n" );
+ return 0;
+ } else {
+#if DEBUG_DETECT
+ printk( "TMC-3260 PCI: IRQ = %u, I/O base = 0x%lx\n",
+ pci_irq, pci_base );
+#endif
+
+ /* Now we have the I/O base address and interrupt from the PCI
+ configuration registers. Unfortunately it seems that the I/O base
+ address is off by one on my card so I mask it with 0xfff8. This
+ must be some kind of goof in the FD code that does the autoconfig
+ and writes to the PCI registers (or maybe I just don't understand
+ something). If they fix it in later versions of the card or BIOS
+ we may have to adjust the address based on the signature or
+ something... */
+
+ *irq = pci_irq;
+ *iobase = (pci_base & 0xfff8);
+
+#if DEBUG_DETECT
+ printk( "TMC-3260 fix: Masking I/O base address with 0xff00.\n" );
+ printk( "TMC-3260: IRQ = %d, I/O base = 0x%x\n", *irq, *iobase );
+#endif
+
+ if (!fdomain_is_valid_port( *iobase )) return 0;
+ return 1;
+ }
+ return 0;
+}
+#endif
+
+int fdomain_16x0_detect( Scsi_Host_Template *tpnt )
+{
+ int i, j;
+ int retcode;
+ struct Scsi_Host *shpnt;
+#if DO_DETECT
+ const int buflen = 255;
+ Scsi_Cmnd SCinit;
+ unsigned char do_inquiry[] = { INQUIRY, 0, 0, 0, buflen, 0 };
+ unsigned char do_request_sense[] = { REQUEST_SENSE, 0, 0, 0, buflen, 0 };
+ unsigned char do_read_capacity[] = { READ_CAPACITY,
+ 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+ unsigned char buf[buflen];
+#endif
+
+#if DEBUG_DETECT
+ printk( "fdomain_16x0_detect()," );
+#endif
+ tpnt->proc_dir = &proc_scsi_fdomain;
+
+ if (setup_called) {
+#if DEBUG_DETECT
+ printk( "no BIOS, using port_base = 0x%x, irq = %d\n",
+ port_base, interrupt_level );
+#endif
+ if (!fdomain_is_valid_port( port_base )) {
+ printk( "fdomain: cannot locate chip at port base 0x%x\n",
+ port_base );
+ printk( "fdomain: bad LILO parameters?\n" );
+ return 0;
+ }
+ } else {
+ int flag = 0;
+
+ for (i = 0; !bios_base && i < ADDRESS_COUNT; i++) {
+#if DEBUG_DETECT
+ printk( " %x(%x),", (unsigned)addresses[i], (unsigned)bios_base );
+#endif
+ for (j = 0; !bios_base && j < SIGNATURE_COUNT; j++) {
+ if (!memcmp( ((char *)addresses[i] + signatures[j].sig_offset),
+ signatures[j].signature, signatures[j].sig_length )) {
+ bios_major = signatures[j].major_bios_version;
+ bios_minor = signatures[j].minor_bios_version;
+ PCI_bus = (signatures[j].flag == 1);
+ Quantum = (signatures[j].flag > 1) ? signatures[j].flag : 0;
+ bios_base = addresses[i];
+ }
+ }
+ }
+
+ if (!bios_base) {
+#if DEBUG_DETECT
+ printk( " FAILED: NO BIOS\n" );
+#endif
+ return 0;
+ }
+
+ if (!PCI_bus) {
+ flag = fdomain_isa_detect( &interrupt_level, &port_base );
+ } else {
+#ifdef CONFIG_PCI
+ flag = fdomain_pci_bios_detect( &interrupt_level, &port_base );
+#else
+ flag = fdomain_pci_nobios_detect( &interrupt_level, &port_base );
+#endif
+ }
+
+ if (!flag) {
+#if DEBUG_DETECT
+ printk( " FAILED: NO PORT\n" );
+#endif
+#ifdef CONFIG_PCI
+ printk( "\nTMC-3260 36C70 PCI scsi chip detection failed.\n" );
+ printk( "Send mail to mckinley@msupa.pa.msu.edu.\n" );
+#endif
+ return 0; /* Cannot find valid set of ports */
+ }
+ }
+
+ SCSI_Mode_Cntl_port = port_base + SCSI_Mode_Cntl;
+ FIFO_Data_Count_port = port_base + FIFO_Data_Count;
+ Interrupt_Cntl_port = port_base + Interrupt_Cntl;
+ Interrupt_Status_port = port_base + Interrupt_Status;
+ Read_FIFO_port = port_base + Read_FIFO;
+ Read_SCSI_Data_port = port_base + Read_SCSI_Data;
+ SCSI_Cntl_port = port_base + SCSI_Cntl;
+ SCSI_Data_NoACK_port = port_base + SCSI_Data_NoACK;
+ SCSI_Status_port = port_base + SCSI_Status;
+ TMC_Cntl_port = port_base + TMC_Cntl;
+ TMC_Status_port = port_base + TMC_Status;
+ Write_FIFO_port = port_base + Write_FIFO;
+ Write_SCSI_Data_port = port_base + Write_SCSI_Data;
+
+ fdomain_16x0_reset( NULL );
+
+ if (fdomain_test_loopback()) {
+#if DEBUG_DETECT
+ printk( "fdomain: LOOPBACK TEST FAILED, FAILING DETECT!\n" );
+#endif
+ if (setup_called) {
+ printk( "fdomain: loopback test failed at port base 0x%x\n",
+ port_base );
+ printk( "fdomain: bad LILO parameters?\n" );
+ }
+ return 0;
+ }
+
+ if (this_id) {
+ tpnt->this_id = (this_id & 0x07);
+ adapter_mask = (1 << tpnt->this_id);
+ } else {
+ if ((bios_major == 3 && bios_minor >= 2) || bios_major < 0) {
+ tpnt->this_id = 7;
+ adapter_mask = 0x80;
+ } else {
+ tpnt->this_id = 6;
+ adapter_mask = 0x40;
+ }
+ }
+
+ /* Print out a banner here in case we can't
+ get resources. */
+
+ shpnt = scsi_register( tpnt, 0 );
+ print_banner( shpnt );
+
+ /* Log IRQ with kernel */
+ if (!interrupt_level) {
+ panic( "fdomain: *NO* interrupt level selected!\n" );
+ } else {
+ /* Register the IRQ with the kernel */
+
+ retcode = request_irq( interrupt_level,
+ fdomain_16x0_intr, SA_INTERRUPT, "fdomain" );
+
+ if (retcode < 0) {
+ if (retcode == -EINVAL) {
+ printk( "fdomain: IRQ %d is bad!\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
+ } else if (retcode == -EBUSY) {
+ printk( "fdomain: IRQ %d is already in use!\n", interrupt_level );
+ printk( " Please use another IRQ!\n" );
+ } else {
+ printk( "fdomain: Error getting IRQ %d\n", interrupt_level );
+ printk( " This shouldn't happen!\n" );
+ printk( " Send mail to faith@cs.unc.edu\n" );
+ }
+ panic( "fdomain: Driver requires interruptions\n" );
+ }
+ }
+
+ /* Log I/O ports with kernel */
+ request_region( port_base, 0x10, "fdomain" );
+
+#if DO_DETECT
+
+ /* These routines are here because of the way the SCSI bus behaves after
+ a reset. This appropriate behavior was not handled correctly by the
+ higher level SCSI routines when I first wrote this driver. Now,
+ however, correct scan routines are part of scsi.c and these routines
+ are no longer needed. However, this code is still good for
+ debugging. */
+
+ SCinit.request_buffer = SCinit.buffer = buf;
+ SCinit.request_bufflen = SCinit.bufflen = sizeof(buf)-1;
+ SCinit.use_sg = 0;
+ SCinit.lun = 0;
+
+ printk( "fdomain: detection routine scanning for devices:\n" );
+ for (i = 0; i < 8; i++) {
+ SCinit.target = i;
+ if (i == tpnt->this_id) /* Skip host adapter */
+ continue;
+ memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
+ retcode = fdomain_16x0_command(&SCinit);
+ if (!retcode) {
+ memcpy(SCinit.cmnd, do_inquiry, sizeof(do_inquiry));
+ retcode = fdomain_16x0_command(&SCinit);
+ if (!retcode) {
+ printk( " SCSI ID %d: ", i );
+ for (j = 8; j < (buf[4] < 32 ? buf[4] : 32); j++)
+ printk( "%c", buf[j] >= 20 ? buf[j] : ' ' );
+ memcpy(SCinit.cmnd, do_read_capacity, sizeof(do_read_capacity));
+ retcode = fdomain_16x0_command(&SCinit);
+ if (!retcode) {
+ unsigned long blocks, size, capacity;
+
+ blocks = (buf[0] << 24) | (buf[1] << 16)
+ | (buf[2] << 8) | buf[3];
+ size = (buf[4] << 24) | (buf[5] << 16) | (buf[6] << 8) | buf[7];
+ capacity = +( +(blocks / 1024L) * +(size * 10L)) / 1024L;
+
+ printk( "%lu MB (%lu byte blocks)",
+ ((capacity + 5L) / 10L), size );
+ } else {
+ memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
+ retcode = fdomain_16x0_command(&SCinit);
+ }
+ printk ("\n" );
+ } else {
+ memcpy(SCinit.cmnd, do_request_sense, sizeof(do_request_sense));
+ retcode = fdomain_16x0_command(&SCinit);
+ }
+ }
+ }
+#endif
+
+ return 1; /* Maximum of one adapter will be detected. */
+}
+
+const char *fdomain_16x0_info( struct Scsi_Host *ignore )
+{
+ static char buffer[80];
+ char *pt;
+
+ strcpy( buffer, "Future Domain TMC-16x0 SCSI driver, version" );
+ if (strchr( VERSION, ':')) { /* Assume VERSION is an RCS Revision string */
+ strcat( buffer, strchr( VERSION, ':' ) + 1 );
+ pt = strrchr( buffer, '$') - 1;
+ if (!pt) /* Stripped RCS Revision string? */
+ pt = buffer + strlen( buffer ) - 1;
+ if (*pt != ' ')
+ ++pt;
+ *pt = '\0';
+ } else { /* Assume VERSION is a number */
+ strcat( buffer, " " VERSION );
+ }
+
+ return buffer;
+}
+
+ /* First pass at /proc information routine. */
+/*
+ * inout : decides on the direction of the dataflow and the meaning of the
+ * variables
+ * buffer: If inout==FALSE data is beeing written to it else read from it
+ * *start: If inout==FALSE start of the valid data in the buffer
+ * offset: If inout==FALSE offset from the beginning of the imaginary file
+ * from which we start writing into the buffer
+ * length: If inout==FALSE max number of bytes to be written into the buffer
+ * else number of bytes in the buffer
+ */
+int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout )
+{
+ const char *info = fdomain_16x0_info( NULL );
+ int len;
+ int pos;
+ int begin;
+
+ if (inout) return(-ENOSYS);
+
+ begin = 0;
+ strcpy( buffer, info );
+ strcat( buffer, "\n" );
+
+ pos = len = strlen( buffer );
+
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length) len = length;
+
+ return(len);
+}
+
+#if 0
+static int fdomain_arbitrate( void )
+{
+ int status = 0;
+ unsigned long timeout;
+
+#if EVERY_ACCESS
+ printk( "fdomain_arbitrate()\n" );
+#endif
+
+ outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
+ outb( adapter_mask, port_base + SCSI_Data_NoACK ); /* Set our id bit */
+ outb( 0x04 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
+
+ timeout = jiffies + 50; /* 500 mS */
+ while (jiffies < timeout) {
+ status = inb( TMC_Status_port ); /* Read adapter status */
+ if (status & 0x02) /* Arbitration complete */
+ return 0;
+ }
+
+ /* Make bus idle */
+ fdomain_make_bus_idle();
+
+#if EVERY_ACCESS
+ printk( "Arbitration failed, status = %x\n", status );
+#endif
+#if ERRORS_ONLY
+ printk( "fdomain: Arbitration failed, status = %x\n", status );
+#endif
+ return 1;
+}
+#endif
+
+static int fdomain_select( int target )
+{
+ int status;
+ unsigned long timeout;
+ static int flag = 0;
+
+
+ outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
+ outb( adapter_mask | (1 << target), SCSI_Data_NoACK_port );
+
+ /* Stop arbitration and enable parity */
+ outb( PARITY_MASK, TMC_Cntl_port );
+
+ timeout = jiffies + 35; /* 350mS -- because of timeouts
+ (was 250mS) */
+
+ while (jiffies < timeout) {
+ status = inb( SCSI_Status_port ); /* Read adapter status */
+ if (status & 1) { /* Busy asserted */
+ /* Enable SCSI Bus (on error, should make bus idle with 0) */
+ outb( 0x80, SCSI_Cntl_port );
+ return 0;
+ }
+ }
+ /* Make bus idle */
+ fdomain_make_bus_idle();
+#if EVERY_ACCESS
+ if (!target) printk( "Selection failed\n" );
+#endif
+#if ERRORS_ONLY
+ if (!target) {
+ if (chip == tmc18c30 && !flag) /* Skip first failure for 18C30 chips. */
+ ++flag;
+ else
+ printk( "fdomain: Selection failed\n" );
+ }
+#endif
+ return 1;
+}
+
+void my_done( int error )
+{
+ if (in_command) {
+ in_command = 0;
+ outb( 0x00, Interrupt_Cntl_port );
+ fdomain_make_bus_idle();
+ current_SC->result = error;
+ if (current_SC->scsi_done)
+ current_SC->scsi_done( current_SC );
+ else panic( "fdomain: current_SC->scsi_done() == NULL" );
+ } else {
+ panic( "fdomain: my_done() called outside of command\n" );
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+}
+
+void fdomain_16x0_intr( int irq, struct pt_regs * regs )
+{
+ int status;
+ int done = 0;
+ unsigned data_count;
+
+ /* The fdomain_16x0_intr is only called via
+ the interrupt handler. The goal of the
+ sti() here is to allow other
+ interruptions while this routine is
+ running. */
+
+ sti(); /* Yes, we really want sti() here */
+
+ outb( 0x00, Interrupt_Cntl_port );
+
+ /* We usually have one spurious interrupt after each command. Ignore it. */
+ if (!in_command || !current_SC) { /* Spurious interrupt */
+#if EVERY_ACCESS
+ printk( "Spurious interrupt, in_command = %d, current_SC = %x\n",
+ in_command, current_SC );
+#endif
+ return;
+ }
+
+ /* Abort calls my_done, so we do nothing here. */
+ if (current_SC->SCp.phase & aborted) {
+#if DEBUG_ABORT
+ printk( "Interrupt after abort, ignoring\n" );
+#endif
+ /*
+ return; */
+ }
+
+#if DEBUG_RACE
+ ++in_interrupt_flag;
+#endif
+
+ if (current_SC->SCp.phase & in_arbitration) {
+ status = inb( TMC_Status_port ); /* Read adapter status */
+ if (!(status & 0x02)) {
+#if EVERY_ACCESS
+ printk( " AFAIL " );
+#endif
+ my_done( DID_BUS_BUSY << 16 );
+ return;
+ }
+ current_SC->SCp.phase = in_selection;
+
+ outb( 0x40 | FIFO_COUNT, Interrupt_Cntl_port );
+
+ outb( 0x82, SCSI_Cntl_port ); /* Bus Enable + Select */
+ outb( adapter_mask | (1 << current_SC->target), SCSI_Data_NoACK_port );
+
+ /* Stop arbitration and enable parity */
+ outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+ } else if (current_SC->SCp.phase & in_selection) {
+ status = inb( SCSI_Status_port );
+ if (!(status & 0x01)) {
+ /* Try again, for slow devices */
+ if (fdomain_select( current_SC->target )) {
+#if EVERY_ACCESS
+ printk( " SFAIL " );
+#endif
+ my_done( DID_NO_CONNECT << 16 );
+ return;
+ } else {
+#if EVERY_ACCESS
+ printk( " AltSel " );
+#endif
+ /* Stop arbitration and enable parity */
+ outb( 0x10 | PARITY_MASK, TMC_Cntl_port );
+ }
+ }
+ current_SC->SCp.phase = in_other;
+ outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
+ outb( 0x80, SCSI_Cntl_port );
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+ }
+
+ /* current_SC->SCp.phase == in_other: this is the body of the routine */
+
+ status = inb( SCSI_Status_port );
+
+ if (status & 0x10) { /* REQ */
+
+ switch (status & 0x0e) {
+
+ case 0x08: /* COMMAND OUT */
+ outb( current_SC->cmnd[current_SC->SCp.sent_command++],
+ Write_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "CMD = %x,",
+ current_SC->cmnd[ current_SC->SCp.sent_command - 1] );
+#endif
+ break;
+ case 0x00: /* DATA OUT -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = -1;
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ }
+ break;
+ case 0x04: /* DATA IN -- tmc18c50/tmc18c30 only */
+ if (chip != tmc1800 && !current_SC->SCp.have_data_in) {
+ current_SC->SCp.have_data_in = 1;
+ outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
+ }
+ break;
+ case 0x0c: /* STATUS IN */
+ current_SC->SCp.Status = inb( Read_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "Status = %x, ", current_SC->SCp.Status );
+#endif
+#if ERRORS_ONLY
+ if (current_SC->SCp.Status && current_SC->SCp.Status != 2) {
+ printk( "fdomain: target = %d, command = %x, status = %x\n",
+ current_SC->target,
+ current_SC->cmnd[0],
+ current_SC->SCp.Status );
+ }
+#endif
+ break;
+ case 0x0a: /* MESSAGE OUT */
+ outb( MESSAGE_REJECT, Write_SCSI_Data_port ); /* Reject */
+ break;
+ case 0x0e: /* MESSAGE IN */
+ current_SC->SCp.Message = inb( Read_SCSI_Data_port );
+#if EVERY_ACCESS
+ printk( "Message = %x, ", current_SC->SCp.Message );
+#endif
+ if (!current_SC->SCp.Message) ++done;
+#if DEBUG_MESSAGES || EVERY_ACCESS
+ if (current_SC->SCp.Message) {
+ printk( "fdomain: message = %x\n", current_SC->SCp.Message );
+ }
+#endif
+ break;
+ }
+ }
+
+ if (chip == tmc1800
+ && !current_SC->SCp.have_data_in
+ && (current_SC->SCp.sent_command
+ >= current_SC->cmd_len)) {
+ /* We have to get the FIFO direction
+ correct, so I've made a table based
+ on the SCSI Standard of which commands
+ appear to require a DATA OUT phase.
+ */
+ /*
+ p. 94: Command for all device types
+ CHANGE DEFINITION 40 DATA OUT
+ COMPARE 39 DATA OUT
+ COPY 18 DATA OUT
+ COPY AND VERIFY 3a DATA OUT
+ INQUIRY 12
+ LOG SELECT 4c DATA OUT
+ LOG SENSE 4d
+ MODE SELECT (6) 15 DATA OUT
+ MODE SELECT (10) 55 DATA OUT
+ MODE SENSE (6) 1a
+ MODE SENSE (10) 5a
+ READ BUFFER 3c
+ RECEIVE DIAGNOSTIC RESULTS 1c
+ REQUEST SENSE 03
+ SEND DIAGNOSTIC 1d DATA OUT
+ TEST UNIT READY 00
+ WRITE BUFFER 3b DATA OUT
+
+ p.178: Commands for direct-access devices (not listed on p. 94)
+ FORMAT UNIT 04 DATA OUT
+ LOCK-UNLOCK CACHE 36
+ PRE-FETCH 34
+ PREVENT-ALLOW MEDIUM REMOVAL 1e
+ READ (6)/RECEIVE 08
+ READ (10) 3c
+ READ CAPACITY 25
+ READ DEFECT DATA (10) 37
+ READ LONG 3e
+ REASSIGN BLOCKS 07 DATA OUT
+ RELEASE 17
+ RESERVE 16 DATA OUT
+ REZERO UNIT/REWIND 01
+ SEARCH DATA EQUAL (10) 31 DATA OUT
+ SEARCH DATA HIGH (10) 30 DATA OUT
+ SEARCH DATA LOW (10) 32 DATA OUT
+ SEEK (6) 0b
+ SEEK (10) 2b
+ SET LIMITS (10) 33
+ START STOP UNIT 1b
+ SYNCHRONIZE CACHE 35
+ VERIFY (10) 2f
+ WRITE (6)/PRINT/SEND 0a DATA OUT
+ WRITE (10)/SEND 2a DATA OUT
+ WRITE AND VERIFY (10) 2e DATA OUT
+ WRITE LONG 3f DATA OUT
+ WRITE SAME 41 DATA OUT ?
+
+ p. 261: Commands for sequential-access devices (not previously listed)
+ ERASE 19
+ LOAD UNLOAD 1b
+ LOCATE 2b
+ READ BLOCK LIMITS 05
+ READ POSITION 34
+ READ REVERSE 0f
+ RECOVER BUFFERED DATA 14
+ SPACE 11
+ WRITE FILEMARKS 10 ?
+
+ p. 298: Commands for printer devices (not previously listed)
+ ****** NOT SUPPORTED BY THIS DRIVER, since 0b is SEEK (6) *****
+ SLEW AND PRINT 0b DATA OUT -- same as seek
+ STOP PRINT 1b
+ SYNCHRONIZE BUFFER 10
+
+ p. 315: Commands for processor devices (not previously listed)
+
+ p. 321: Commands for write-once devices (not previously listed)
+ MEDIUM SCAN 38
+ READ (12) a8
+ SEARCH DATA EQUAL (12) b1 DATA OUT
+ SEARCH DATA HIGH (12) b0 DATA OUT
+ SEARCH DATA LOW (12) b2 DATA OUT
+ SET LIMITS (12) b3
+ VERIFY (12) af
+ WRITE (12) aa DATA OUT
+ WRITE AND VERIFY (12) ae DATA OUT
+
+ p. 332: Commands for CD-ROM devices (not previously listed)
+ PAUSE/RESUME 4b
+ PLAY AUDIO (10) 45
+ PLAY AUDIO (12) a5
+ PLAY AUDIO MSF 47
+ PLAY TRACK RELATIVE (10) 49
+ PLAY TRACK RELATIVE (12) a9
+ READ HEADER 44
+ READ SUB-CHANNEL 42
+ READ TOC 43
+
+ p. 370: Commands for scanner devices (not previously listed)
+ GET DATA BUFFER STATUS 34
+ GET WINDOW 25
+ OBJECT POSITION 31
+ SCAN 1b
+ SET WINDOW 24 DATA OUT
+
+ p. 391: Commands for optical memory devices (not listed)
+ ERASE (10) 2c
+ ERASE (12) ac
+ MEDIUM SCAN 38 DATA OUT
+ READ DEFECT DATA (12) b7
+ READ GENERATION 29
+ READ UPDATED BLOCK 2d
+ UPDATE BLOCK 3d DATA OUT
+
+ p. 419: Commands for medium changer devices (not listed)
+ EXCHANGE MEDIUM 46
+ INITIALIZE ELEMENT STATUS 07
+ MOVE MEDIUM a5
+ POSITION TO ELEMENT 2b
+ READ ELEMENT STATUS b8
+ REQUEST VOL. ELEMENT ADDRESS b5
+ SEND VOLUME TAG b6 DATA OUT
+
+ p. 454: Commands for communications devices (not listed previously)
+ GET MESSAGE (6) 08
+ GET MESSAGE (10) 28
+ GET MESSAGE (12) a8
+ */
+
+ switch (current_SC->cmnd[0]) {
+ case CHANGE_DEFINITION: case COMPARE: case COPY:
+ case COPY_VERIFY: case LOG_SELECT: case MODE_SELECT:
+ case MODE_SELECT_10: case SEND_DIAGNOSTIC: case WRITE_BUFFER:
+
+ case FORMAT_UNIT: case REASSIGN_BLOCKS: case RESERVE:
+ case SEARCH_EQUAL: case SEARCH_HIGH: case SEARCH_LOW:
+ case WRITE_6: case WRITE_10: case WRITE_VERIFY:
+ case 0x3f: case 0x41:
+
+ case 0xb1: case 0xb0: case 0xb2:
+ case 0xaa: case 0xae:
+
+ case 0x24:
+
+ case 0x38: case 0x3d:
+
+ case 0xb6:
+
+ case 0xea: /* alternate number for WRITE LONG */
+
+ current_SC->SCp.have_data_in = -1;
+ outb( 0xd0 | PARITY_MASK, TMC_Cntl_port );
+ break;
+
+ case 0x00:
+ default:
+
+ current_SC->SCp.have_data_in = 1;
+ outb( 0x90 | PARITY_MASK, TMC_Cntl_port );
+ break;
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == -1) { /* DATA OUT */
+ while ( (data_count = FIFO_Size - inw( FIFO_Data_Count_port )) > 512 ) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count ) ;
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count > 0) {
+#if EVERY_ACCESS
+ printk( "%d OUT, ", data_count );
+#endif
+ if (data_count == 1) {
+ outb( *current_SC->SCp.ptr++, Write_FIFO_port );
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1;
+ outsw( Write_FIFO_port, current_SC->SCp.ptr, data_count );
+ current_SC->SCp.ptr += 2 * data_count;
+ current_SC->SCp.this_residual -= 2 * data_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual) {
+ if (current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ } else
+ break;
+ }
+ }
+ }
+
+ if (current_SC->SCp.have_data_in == 1) { /* DATA IN */
+ while ((data_count = inw( FIFO_Data_Count_port )) > 0) {
+#if EVERY_ACCESS
+ printk( "DC=%d, ", data_count );
+#endif
+ if (data_count > current_SC->SCp.this_residual)
+ data_count = current_SC->SCp.this_residual;
+ if (data_count) {
+#if EVERY_ACCESS
+ printk( "%d IN, ", data_count );
+#endif
+ if (data_count == 1) {
+ *current_SC->SCp.ptr++ = inb( Read_FIFO_port );
+ --current_SC->SCp.this_residual;
+ } else {
+ data_count >>= 1; /* Number of words */
+ insw( Read_FIFO_port, current_SC->SCp.ptr, data_count );
+ current_SC->SCp.ptr += 2 * data_count;
+ current_SC->SCp.this_residual -= 2 * data_count;
+ }
+ }
+ if (!current_SC->SCp.this_residual
+ && current_SC->SCp.buffers_residual) {
+ --current_SC->SCp.buffers_residual;
+ ++current_SC->SCp.buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ }
+ }
+ }
+
+ if (done) {
+#if EVERY_ACCESS
+ printk( " ** IN DONE %d ** ", current_SC->SCp.have_data_in );
+#endif
+
+#if ERRORS_ONLY
+ if (current_SC->cmnd[0] == REQUEST_SENSE && !current_SC->SCp.Status) {
+ if ((unsigned char)(*((char *)current_SC->request_buffer+2)) & 0x0f) {
+ unsigned char key;
+ unsigned char code;
+ unsigned char qualifier;
+
+ key = (unsigned char)(*((char *)current_SC->request_buffer + 2))
+ & 0x0f;
+ code = (unsigned char)(*((char *)current_SC->request_buffer + 12));
+ qualifier = (unsigned char)(*((char *)current_SC->request_buffer
+ + 13));
+
+ if (!(key == UNIT_ATTENTION && (code == 0x29 || !code))
+ && !(key == NOT_READY
+ && code == 0x04
+ && (!qualifier || qualifier == 0x02 || qualifier == 0x01))
+ && !(key == ILLEGAL_REQUEST && (code == 0x25
+ || code == 0x24
+ || !code)))
+
+ printk( "fdomain: REQUEST SENSE "
+ "Key = %x, Code = %x, Qualifier = %x\n",
+ key, code, qualifier );
+ }
+ }
+#endif
+#if EVERY_ACCESS
+ printk( "BEFORE MY_DONE. . ." );
+#endif
+ my_done( (current_SC->SCp.Status & 0xff)
+ | ((current_SC->SCp.Message & 0xff) << 8) | (DID_OK << 16) );
+#if EVERY_ACCESS
+ printk( "RETURNING.\n" );
+#endif
+
+ } else {
+ if (current_SC->SCp.phase & disconnect) {
+ outb( 0xd0 | FIFO_COUNT, Interrupt_Cntl_port );
+ outb( 0x00, SCSI_Cntl_port );
+ } else {
+ outb( 0x90 | FIFO_COUNT, Interrupt_Cntl_port );
+ }
+ }
+#if DEBUG_RACE
+ in_interrupt_flag = 0;
+#endif
+ return;
+}
+
+int fdomain_16x0_queue( Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ if (in_command) {
+ panic( "fdomain: fdomain_16x0_queue() NOT REENTRANT!\n" );
+ }
+#if EVERY_ACCESS
+ printk( "queue: target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+#endif
+
+ fdomain_make_bus_idle();
+
+ current_SC = SCpnt; /* Save this for the done function */
+ current_SC->scsi_done = done;
+
+ /* Initialize static data */
+
+ if (current_SC->use_sg) {
+ current_SC->SCp.buffer =
+ (struct scatterlist *)current_SC->request_buffer;
+ current_SC->SCp.ptr = current_SC->SCp.buffer->address;
+ current_SC->SCp.this_residual = current_SC->SCp.buffer->length;
+ current_SC->SCp.buffers_residual = current_SC->use_sg - 1;
+ } else {
+ current_SC->SCp.ptr = (char *)current_SC->request_buffer;
+ current_SC->SCp.this_residual = current_SC->request_bufflen;
+ current_SC->SCp.buffer = NULL;
+ current_SC->SCp.buffers_residual = 0;
+ }
+
+
+ current_SC->SCp.Status = 0;
+ current_SC->SCp.Message = 0;
+ current_SC->SCp.have_data_in = 0;
+ current_SC->SCp.sent_command = 0;
+ current_SC->SCp.phase = in_arbitration;
+
+ /* Start arbitration */
+ outb( 0x00, Interrupt_Cntl_port );
+ outb( 0x00, SCSI_Cntl_port ); /* Disable data drivers */
+ outb( adapter_mask, SCSI_Data_NoACK_port ); /* Set our id bit */
+ ++in_command;
+ outb( 0x20, Interrupt_Cntl_port );
+ outb( 0x14 | PARITY_MASK, TMC_Cntl_port ); /* Start arbitration */
+
+ return 0;
+}
+
+/* The following code, which simulates the old-style command function, was
+ taken from Tommy Thorn's aha1542.c file. This code is Copyright (C)
+ 1992 Tommy Thorn. */
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done( Scsi_Cmnd *SCpnt )
+{
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+int fdomain_16x0_command( Scsi_Cmnd *SCpnt )
+{
+ fdomain_16x0_queue( SCpnt, internal_done );
+
+ while (!internal_done_flag)
+ ;
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+/* End of code derived from Tommy Thorn's work. */
+
+void print_info( Scsi_Cmnd *SCpnt )
+{
+ unsigned int imr;
+ unsigned int irr;
+ unsigned int isr;
+
+ if (!SCpnt || !SCpnt->host) {
+ printk( "fdomain: cannot provide detailed information\n" );
+ }
+
+ printk( "%s\n", fdomain_16x0_info( SCpnt->host ) );
+ print_banner( SCpnt->host );
+ switch (SCpnt->SCp.phase) {
+ case in_arbitration: printk( "arbitration " ); break;
+ case in_selection: printk( "selection " ); break;
+ case in_other: printk( "other " ); break;
+ default: printk( "unknown " ); break;
+ }
+
+ printk( "(%d), target = %d cmnd = 0x%02x pieces = %d size = %u\n",
+ SCpnt->SCp.phase,
+ SCpnt->target,
+ *(unsigned char *)SCpnt->cmnd,
+ SCpnt->use_sg,
+ SCpnt->request_bufflen );
+ printk( "sent_command = %d, have_data_in = %d, timeout = %d\n",
+ SCpnt->SCp.sent_command,
+ SCpnt->SCp.have_data_in,
+ SCpnt->timeout );
+#if DEBUG_RACE
+ printk( "in_interrupt_flag = %d\n", in_interrupt_flag );
+#endif
+
+ imr = (inb( 0x0a1 ) << 8) + inb( 0x21 );
+ outb( 0x0a, 0xa0 );
+ irr = inb( 0xa0 ) << 8;
+ outb( 0x0a, 0x20 );
+ irr += inb( 0x20 );
+ outb( 0x0b, 0xa0 );
+ isr = inb( 0xa0 ) << 8;
+ outb( 0x0b, 0x20 );
+ isr += inb( 0x20 );
+
+ /* Print out interesting information */
+ printk( "IMR = 0x%04x", imr );
+ if (imr & (1 << interrupt_level))
+ printk( " (masked)" );
+ printk( ", IRR = 0x%04x, ISR = 0x%04x\n", irr, isr );
+
+ printk( "SCSI Status = 0x%02x\n", inb( SCSI_Status_port ) );
+ printk( "TMC Status = 0x%02x", inb( TMC_Status_port ) );
+ if (inb( TMC_Status_port & 1))
+ printk( " (interrupt)" );
+ printk( "\n" );
+ printk( "Interrupt Status = 0x%02x", inb( Interrupt_Status_port ) );
+ if (inb( Interrupt_Status_port ) & 0x08)
+ printk( " (enabled)" );
+ printk( "\n" );
+ if (chip == tmc18c50 || chip == tmc18c30) {
+ printk( "FIFO Status = 0x%02x\n", inb( port_base + FIFO_Status ) );
+ printk( "Int. Condition = 0x%02x\n",
+ inb( port_base + Interrupt_Cond ) );
+ }
+ printk( "Configuration 1 = 0x%02x\n", inb( port_base + Configuration1 ) );
+ if (chip == tmc18c50 || chip == tmc18c30)
+ printk( "Configuration 2 = 0x%02x\n",
+ inb( port_base + Configuration2 ) );
+}
+
+int fdomain_16x0_abort( Scsi_Cmnd *SCpnt)
+{
+ unsigned long flags;
+#if EVERY_ACCESS || ERRORS_ONLY || DEBUG_ABORT
+ printk( "fdomain: abort " );
+#endif
+
+ save_flags( flags );
+ cli();
+ if (!in_command) {
+#if EVERY_ACCESS || ERRORS_ONLY
+ printk( " (not in command)\n" );
+#endif
+ restore_flags( flags );
+ return SCSI_ABORT_NOT_RUNNING;
+ } else printk( "\n" );
+
+#if DEBUG_ABORT
+ print_info( SCpnt );
+#endif
+
+ fdomain_make_bus_idle();
+
+ current_SC->SCp.phase |= aborted;
+
+ current_SC->result = DID_ABORT << 16;
+
+ restore_flags( flags );
+
+ /* Aborts are not done well. . . */
+ my_done( DID_ABORT << 16 );
+
+ return SCSI_ABORT_SUCCESS;
+}
+
+int fdomain_16x0_reset( Scsi_Cmnd *SCpnt )
+{
+#if DEBUG_RESET
+ static int called_once = 0;
+#endif
+
+#if ERRORS_ONLY
+ if (SCpnt) printk( "fdomain: SCSI Bus Reset\n" );
+#endif
+
+#if DEBUG_RESET
+ if (called_once) print_info( current_SC );
+ called_once = 1;
+#endif
+
+ outb( 1, SCSI_Cntl_port );
+ do_pause( 2 );
+ outb( 0, SCSI_Cntl_port );
+ do_pause( 115 );
+ outb( 0, SCSI_Mode_Cntl_port );
+ outb( PARITY_MASK, TMC_Cntl_port );
+
+ /* Unless this is the very first call (i.e., SCPnt == NULL), everything
+ is probably hosed at this point. We will, however, try to keep
+ things going by informing the high-level code that we need help. */
+
+ return SCSI_RESET_WAKEUP;
+}
+
+#include "sd.h"
+#include "scsi_ioctl.h"
+
+int fdomain_16x0_biosparam( Scsi_Disk *disk, kdev_t dev, int *info_array )
+{
+ int drive;
+ unsigned char buf[512 + sizeof( int ) * 2];
+ int size = disk->capacity;
+ int *sizes = (int *)buf;
+ unsigned char *data = (unsigned char *)(sizes + 2);
+ unsigned char do_read[] = { READ_6, 0, 0, 0, 1, 0 };
+ int retcode;
+ struct drive_info {
+ unsigned short cylinders;
+ unsigned char heads;
+ unsigned char sectors;
+ } *i;
+
+ /* NOTES:
+ The RAM area starts at 0x1f00 from the bios_base address.
+
+ For BIOS Version 2.0:
+
+ The drive parameter table seems to start at 0x1f30.
+ The first byte's purpose is not known.
+ Next is the cylinder, head, and sector information.
+ The last 4 bytes appear to be the drive's size in sectors.
+ The other bytes in the drive parameter table are unknown.
+ If anyone figures them out, please send me mail, and I will
+ update these notes.
+
+ Tape drives do not get placed in this table.
+
+ There is another table at 0x1fea:
+ If the byte is 0x01, then the SCSI ID is not in use.
+ If the byte is 0x18 or 0x48, then the SCSI ID is in use,
+ although tapes don't seem to be in this table. I haven't
+ seen any other numbers (in a limited sample).
+
+ 0x1f2d is a drive count (i.e., not including tapes)
+
+ The table at 0x1fcc are I/O ports addresses for the various
+ operations. I calculate these by hand in this driver code.
+
+
+
+ For the ISA-200S version of BIOS Version 2.0:
+
+ The drive parameter table starts at 0x1f33.
+
+ WARNING: Assume that the table entry is 25 bytes long. Someone needs
+ to check this for the Quantum ISA-200S card.
+
+
+
+ For BIOS Version 3.2:
+
+ The drive parameter table starts at 0x1f70. Each entry is
+ 0x0a bytes long. Heads are one less than we need to report.
+ */
+
+ drive = MINOR(dev) / 16;
+
+ if (bios_major == 2) {
+ switch (Quantum) {
+ case 2: /* ISA_200S */
+ /* The value of 25 has never been verified.
+ It should probably be 15. */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f33 + drive * 25 );
+ break;
+ case 3: /* ISA_250MG */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f36 + drive * 15 );
+ break;
+ case 4: /* ISA_200S (another one) */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f34 + drive * 15 );
+ break;
+ default:
+ i = (struct drive_info *)( (char *)bios_base + 0x1f31 + drive * 25 );
+ break;
+ }
+ info_array[0] = i->heads;
+ info_array[1] = i->sectors;
+ info_array[2] = i->cylinders;
+ } else if (bios_major == 3
+ && bios_minor >= 0
+ && bios_minor < 4) { /* 3.0 and 3.2 BIOS */
+ i = (struct drive_info *)( (char *)bios_base + 0x1f71 + drive * 10 );
+ info_array[0] = i->heads + 1;
+ info_array[1] = i->sectors;
+ info_array[2] = i->cylinders;
+ } else { /* 3.4 BIOS (and up?) */
+ /* This algorithm was provided by Future Domain (much thanks!). */
+
+ sizes[0] = 0; /* zero bytes out */
+ sizes[1] = 512; /* one sector in */
+ memcpy( data, do_read, sizeof( do_read ) );
+ retcode = kernel_scsi_ioctl( disk->device,
+ SCSI_IOCTL_SEND_COMMAND,
+ (void *)buf );
+ if (!retcode /* SCSI command ok */
+ && data[511] == 0xaa && data[510] == 0x55 /* Partition table valid */
+ && data[0x1c2]) { /* Partition type */
+
+ /* The partition table layout is as follows:
+
+ Start: 0x1b3h
+ Offset: 0 = partition status
+ 1 = starting head
+ 2 = starting sector and cylinder (word, encoded)
+ 4 = partition type
+ 5 = ending head
+ 6 = ending sector and cylinder (word, encoded)
+ 8 = starting absolute sector (double word)
+ c = number of sectors (double word)
+ Signature: 0x1fe = 0x55aa
+
+ So, this algorithm assumes:
+ 1) the first partition table is in use,
+ 2) the data in the first entry is correct, and
+ 3) partitions never divide cylinders
+
+ Note that (1) may be FALSE for NetBSD (and other BSD flavors),
+ as well as for Linux. Note also, that Linux doesn't pay any
+ attention to the fields that are used by this algorithm -- it
+ only uses the absolute sector data. Recent versions of Linux's
+ fdisk(1) will fill this data in correctly, and forthcoming
+ versions will check for consistency.
+
+ Checking for a non-zero partition type is not part of the
+ Future Domain algorithm, but it seemed to be a reasonable thing
+ to do, especially in the Linux and BSD worlds. */
+
+ info_array[0] = data[0x1c3] + 1; /* heads */
+ info_array[1] = data[0x1c4] & 0x3f; /* sectors */
+ } else {
+
+ /* Note that this new method guarantees that there will always be
+ less than 1024 cylinders on a platter. This is good for drives
+ up to approximately 7.85GB (where 1GB = 1024 * 1024 kB). */
+
+ if ((unsigned int)size >= 0x7e0000U) {
+ info_array[0] = 0xff; /* heads = 255 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else if ((unsigned int)size >= 0x200000U) {
+ info_array[0] = 0x80; /* heads = 128 */
+ info_array[1] = 0x3f; /* sectors = 63 */
+ } else {
+ info_array[0] = 0x40; /* heads = 64 */
+ info_array[1] = 0x20; /* sectors = 32 */
+ }
+ }
+ /* For both methods, compute the cylinders */
+ info_array[2] = (unsigned int)size / (info_array[0] * info_array[1] );
+ }
+
+ return 0;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = FDOMAIN_16X0;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/fdomain.h b/i386/i386at/gpl/linux/scsi/fdomain.h
new file mode 100644
index 00000000..e0c4e045
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/fdomain.h
@@ -0,0 +1,61 @@
+/* fdomain.h -- Header for Future Domain TMC-16x0 driver
+ * Created: Sun May 3 18:47:33 1992 by faith@cs.unc.edu
+ * Revised: Thu Oct 12 13:21:35 1995 by r.faith@ieee.org
+ * Author: Rickard E. Faith, faith@cs.unc.edu
+ * Copyright 1992, 1993, 1994, 1995 Rickard E. Faith
+ *
+ * $Id: fdomain.h,v 1.1.1.1 1997/02/25 21:27:49 thomas Exp $
+
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2, or (at your option) any
+ * later version.
+
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+
+ * You should have received a copy of the GNU General Public License along
+ * with this program; if not, write to the Free Software Foundation, Inc.,
+ * 675 Mass Ave, Cambridge, MA 02139, USA.
+
+ */
+
+#ifndef _FDOMAIN_H
+#define _FDOMAIN_H
+
+int fdomain_16x0_detect( Scsi_Host_Template * );
+int fdomain_16x0_command( Scsi_Cmnd * );
+int fdomain_16x0_abort( Scsi_Cmnd * );
+const char *fdomain_16x0_info( struct Scsi_Host * );
+int fdomain_16x0_reset( Scsi_Cmnd * );
+int fdomain_16x0_queue( Scsi_Cmnd *, void (*done)(Scsi_Cmnd *) );
+int fdomain_16x0_biosparam( Disk *, kdev_t, int * );
+int fdomain_16x0_proc_info( char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout );
+
+extern struct proc_dir_entry proc_scsi_fdomain;
+
+#define FDOMAIN_16X0 { NULL, \
+ NULL, \
+ NULL, \
+ fdomain_16x0_proc_info, \
+ NULL, \
+ fdomain_16x0_detect, \
+ NULL, \
+ fdomain_16x0_info, \
+ fdomain_16x0_command, \
+ fdomain_16x0_queue, \
+ fdomain_16x0_abort, \
+ fdomain_16x0_reset, \
+ NULL, \
+ fdomain_16x0_biosparam, \
+ 1, \
+ 6, \
+ 64, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING }
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/g_NCR5380.c b/i386/i386at/gpl/linux/scsi/g_NCR5380.c
new file mode 100644
index 00000000..f7f2aabc
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/g_NCR5380.c
@@ -0,0 +1,588 @@
+/*
+ * Generic Generic NCR5380 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * TODO : flesh out DMA support, find some one actually using this (I have
+ * a memory mapped Trantor board that works fine)
+ */
+
+/*
+ * Options :
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. With command line overrides - NCR5380=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 2. With the GENERIC_NCR5380_OVERRIDE compile time define. This is
+ * specified as an array of address, irq, dma, board tuples. Ie, for
+ * one board at 0x350, IRQ5, no dma, I could say
+ * -DGENERIC_NCR5380_OVERRIDE={{0xcc000, 5, DMA_NONE, BOARD_NCR5380}}
+ *
+ * -1 should be specified for no or DMA interrupt, -2 to autoprobe for an
+ * IRQ line if overridden on the command line.
+ */
+
+/*
+ * $Log: g_NCR5380.c,v $
+ * Revision 1.1.1.1 1996/10/30 01:40:05 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:35 goel
+ * Linux driver merge.
+ *
+ */
+
+#define AUTOPROBE_IRQ
+#define AUTOSENSE
+
+#include <linux/config.h>
+
+#ifdef MACH
+#define CONFIG_SCSI_G_NCR5380_MEM
+#endif
+
+#ifdef CONFIG_SCSI_GENERIC_NCR53C400
+#define NCR53C400_PSEUDO_DMA 1
+#define PSEUDO_DMA
+#define NCR53C400
+#endif
+#if defined(CONFIG_SCSI_G_NCR5380_PORT) && defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You can not configure the Generic NCR 5380 SCSI Driver for memory mapped I/O and port mapped I/O at the same time (yet)
+#endif
+#if !defined(CONFIG_SCSI_G_NCR5380_PORT) && !defined(CONFIG_SCSI_G_NCR5380_MEM)
+#error You must configure the Generic NCR 5380 SCSI Driver for one of memory mapped I/O and port mapped I/O.
+#endif
+
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "g_NCR5380.h"
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_g_ncr5380 = {
+ PROC_SCSI_GENERIC_NCR5380, 9, "g_NCR5380",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+static struct override {
+ NCR5380_implementation_fields;
+ int irq;
+ int dma;
+ int board; /* Use NCR53c400, Ricoh, etc. extensions ? */
+} overrides
+#ifdef GENERIC_NCR5380_OVERRIDE
+ [] = GENERIC_NCR5380_OVERRIDE
+#else
+ [1] = {{0,},};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+/*
+ * Function : static internal_setup(int board, char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : board - either BOARD_NCR5380 for a normal NCR5380 board,
+ * or BOARD_NCR53C400 for a NCR53C400 board. str - unused, ints -
+ * array of integer parameters with ints[0] equal to the number of ints.
+ *
+ */
+
+static void internal_setup(int board, char *str, int *ints) {
+ static int commandline_current = 0;
+ switch (board) {
+ case BOARD_NCR5380:
+ if (ints[0] != 2 && ints[0] != 3)
+ printk("generic_NCR5380_setup : usage ncr5380=" STRVAL(NCR5380_map_name) ",irq,dma\n");
+ return;
+ case BOARD_NCR53C400:
+ if (ints[0] != 2)
+ printk("generic_NCR53C400_setup : usage ncr53c400= " STRVAL(NCR5380_map_name) ",irq\n");
+ return;
+ }
+
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].NCR5380_map_name = (NCR5380_map_type)ints[1];
+ overrides[commandline_current].irq = ints[2];
+ if (ints[0] == 3)
+ overrides[commandline_current].dma = ints[3];
+ else
+ overrides[commandline_current].dma = DMA_NONE;
+ overrides[commandline_current].board = board;
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : generic_NCR5380_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer paramters with ints[0]
+ * equal to the number of ints.
+ */
+
+void generic_NCR5380_setup (char *str, int *ints) {
+ internal_setup (BOARD_NCR5380, str, ints);
+}
+
+/*
+ * Function : generic_NCR53C400_setup (char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer paramters with ints[0]
+ * equal to the number of ints.
+ */
+
+void generic_NCR53C400_setup (char *str, int *ints) {
+ internal_setup (BOARD_NCR53C400, str, ints);
+}
+
+/*
+ * Function : int generic_NCR5380_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : initializes generic NCR5380 driver based on the
+ * command line / compile time port and irq definitions.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int generic_NCR5380_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0;
+ int count;
+ int flags = 0;
+ struct Scsi_Host *instance;
+
+ tpnt->proc_dir = &proc_scsi_g_ncr5380;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ if (!(overrides[current_override].NCR5380_map_name))
+ continue;
+
+ switch (overrides[current_override].board) {
+ case BOARD_NCR5380:
+ flags = FLAG_NO_PSEUDO_DMA;
+ break;
+ case BOARD_NCR53C400:
+ flags = FLAG_NCR53C400;
+ break;
+ }
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->NCR5380_instance_name = overrides[current_override].NCR5380_map_name;
+
+ NCR5380_init(instance, flags);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, 0xffff);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, generic_NCR5380_intr, SA_INTERRUPT, "NCR5380")) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+ printk("scsi%d : at " STRVAL(NCR5380_map_name) " 0x%x", instance->host_no, (unsigned int)instance->NCR5380_instance_name);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, GENERIC_NCR5380_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+const char * generic_NCR5380_info (void) {
+ static const char string[]="Generic NCR5380/53C400 Info";
+ return string;
+}
+
+int generic_NCR5380_release_resources(struct Scsi_Host * instance)
+{
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+ free_irq(instance->irq);
+
+ return 0;
+}
+
+#ifdef BIOSPARAM
+/*
+ * Function : int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatable H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : allways 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int generic_NCR5380_biosparam(Disk * disk, kdev_t dev, int *ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+#endif
+
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout)
+{
+ int len = 0;
+ struct Scsi_Host *scsi_ptr;
+
+ for (scsi_ptr = first_instance; scsi_ptr; scsi_ptr=scsi_ptr->next)
+ if (scsi_ptr->host_no == hostno)
+ break;
+
+ len += sprintf(buffer+len, "SCSI host number %d : %s\n", scsi_ptr->host_no, scsi_ptr->hostt->name);
+ len += sprintf(buffer+len, "Generic NCR5380 driver version %d\n", GENERIC_NCR5380_PUBLIC_RELEASE);
+ len += sprintf(buffer+len, "NCR5380 driver core version %d\n", NCR5380_PUBLIC_RELEASE);
+#ifdef NCR53C400
+ len += sprintf(buffer+len, "NCR53C400 driver extension version %d\n", NCR53C400_PUBLIC_RELEASE);
+ len += sprintf(buffer+len, "NCR53C400 card%s detected\n", (((struct NCR5380_hostdata *)scsi_ptr->hostdata)->flags & FLAG_NCR53C400)?"":" not");
+# if NCR53C400_PSEUDO_DMA
+ len += sprintf(buffer+len, "NCR53C400 pseudo DMA being used\n");
+# endif
+#else
+ len += sprintf(buffer+len, "NO NCR53C400 driver extensions\n");
+#endif
+ len += sprintf(buffer+len, "Using %s mapping at %s 0x%x, ", STRVAL(NCR5380_map_config), STRVAL(NCR5380_map_name), scsi_ptr->NCR5380_instance_name);
+ if (scsi_ptr->irq == IRQ_NONE)
+ len += sprintf(buffer+len, "interrupts disabled\n");
+ else
+ len += sprintf(buffer+len, "on interrupt %d\n", scsi_ptr->irq);
+
+ *start = buffer + offset;
+ len -= offset;
+ if (len > length)
+ len = length;
+ return len;
+}
+
+#if NCR53C400_PSEUDO_DMA
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int i;
+ int bl;
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: About to read %d blocks for %d bytes\n", blocks, len);
+#endif
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE | CSR_TRANS_DIR);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: %d blocks left\n", blocks);
+#endif
+
+ if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ if (blocks)
+ printk("53C400r: blocks still == %d\n", blocks);
+ else
+ printk("53C400r: Exiting loop\n");
+#endif
+ break;
+ }
+
+#if 1
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk("53C400r: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Waiting for buffer, bl=%d\n", bl);
+#endif
+
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Transferring 128 bytes\n");
+#endif
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: EXTRA: Waiting for buffer\n");
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ dst[start+i] = NCR5380_read(C400_HOST_BUFFER);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(dst+start,NCR53C400_host_buffer+NCR5380_map_name,128);
+#endif
+ start+=128;
+ blocks--;
+
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ printk("53C400r: Final values: blocks=%d start=%d\n", blocks, start);
+#endif
+
+ if (!(NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ printk("53C400r: no 53C80 gated irq after transfer");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: Got 53C80 interupt and tried to clear it\n");
+#endif
+
+/* DON'T DO THIS - THEY NEVER ARRIVE!
+ printk("53C400r: Waiting for 53C80 registers\n");
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+*/
+
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER))
+ printk("53C400r: no end dma signal\n");
+#if (NDEBUG & NDEBUG_C400_PREAD)
+ else
+ printk("53C400r: end dma as expected\n");
+#endif
+
+ NCR5380_write(MODE_REG, MR_BASE);
+ NCR5380_read(RESET_PARITY_INTERRUPT_REG);
+ return 0;
+}
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src, int len)
+{
+ int blocks = len / 128;
+ int start = 0;
+ int i;
+ int bl;
+ NCR5380_local_declare();
+
+ NCR5380_setup(instance);
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: About to write %d blocks for %d bytes\n", blocks, len);
+#endif
+
+ NCR5380_write(C400_CONTROL_STATUS_REG, CSR_BASE);
+ NCR5380_write(C400_BLOCK_COUNTER_REG, blocks);
+ while (1) {
+ if (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ) {
+ printk("53C400w: Got 53C80_IRQ start=%d, blocks=%d\n", start, blocks);
+ return -1;
+ }
+
+ if ((bl=NCR5380_read(C400_BLOCK_COUNTER_REG)) == 0) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ if (blocks)
+ printk("53C400w: exiting loop, blocks still == %d\n", blocks);
+ else
+ printk("53C400w: exiting loop\n");
+#endif
+ break;
+ }
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: %d blocks left\n", blocks);
+
+ printk("53C400w: waiting for buffer, bl=%d\n", bl);
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: transferring 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+ if (blocks) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: EXTRA waiting for buffer\n");
+#endif
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_HOST_BUF_NOT_RDY)
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: transferring EXTRA 128 bytes\n");
+#endif
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+ for (i=0; i<128; i++)
+ NCR5380_write(C400_HOST_BUFFER, src[start+i]);
+#else
+ /* implies CONFIG_SCSI_G_NCR5380_MEM */
+ memmove(NCR53C400_host_buffer+NCR5380_map_name,src+start,128);
+#endif
+ start+=128;
+ blocks--;
+ }
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ else
+ printk("53C400w: No EXTRA required\n");
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: Final values: blocks=%d start=%d\n", blocks, start);
+#endif
+
+#if 0
+ printk("53C400w: waiting for registers to be available\n");
+ THEY NEVER DO!
+ while (NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_53C80_REG)
+ ;
+ printk("53C400w: Got em\n");
+#endif
+
+ /* Let's wait for this instead - could be ugly */
+ /* All documentation says to check for this. Maybe my hardware is too
+ * fast. Waiting for it seems to work fine! KLL
+ */
+ while (!(i = NCR5380_read(C400_CONTROL_STATUS_REG) & CSR_GATED_53C80_IRQ))
+ ;
+
+ /*
+ * I know. i is certainly != 0 here but the loop is new. See previous
+ * comment.
+ */
+ if (i) {
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ prink("53C400w: got 53C80 gated irq (last block)\n");
+#endif
+ if (!((i=NCR5380_read(BUS_AND_STATUS_REG)) & BASR_END_DMA_TRANSFER))
+ printk("53C400w: No END OF DMA bit - WHOOPS! BASR=%0x\n",i);
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ else
+ printk("53C400w: Got END OF DMA\n");
+#endif
+ }
+ else
+ printk("53C400w: no 53C80 gated irq after transfer (last block)\n");
+
+#if 0
+ if (!(NCR5380_read(BUS_AND_STATUS_REG) & BASR_END_DMA_TRANSFER)) {
+ printk("53C400w: no end dma signal\n");
+ }
+#endif
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: waiting for last byte...\n");
+#endif
+ while (!(NCR5380_read(TARGET_COMMAND_REG) & TCR_LAST_BYTE_SENT))
+ ;
+
+#if (NDEBUG & NDEBUG_C400_PWRITE)
+ printk("53C400w: got last byte.\n");
+ printk("53C400w: pwrite exiting with status 0, whoopee!\n");
+#endif
+ return 0;
+}
+#endif /* PSEUDO_DMA */
+
+#ifdef MACH
+#include "NCR5380.src"
+#else
+#include "NCR5380.c"
+#endif
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = GENERIC_NCR5380;
+
+#include <linux/module.h>
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/g_NCR5380.h b/i386/i386at/gpl/linux/scsi/g_NCR5380.h
new file mode 100644
index 00000000..52e64499
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/g_NCR5380.h
@@ -0,0 +1,166 @@
+/*
+ * Generic Generic NCR5380 driver defines
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * NCR53C400 extensions (c) 1994,1995,1996, Kevin Lentin
+ * K.Lentin@cs.monash.edu.au
+ *
+ * ALPHA RELEASE 1.
+ *
+ * For more information, please consult
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: g_NCR5380.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:40:05 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:35 goel
+ * Linux driver merge.
+ *
+ */
+
+#ifndef GENERIC_NCR5380_H
+#define GENERIC_NCR5380_H
+
+#define GENERIC_NCR5380_PUBLIC_RELEASE 1
+
+#ifdef NCR53C400
+#define BIOSPARAM
+#define NCR5380_BIOSPARAM generic_NCR5380_biosparam
+#else
+#define NCR5380_BIOSPARAM NULL
+#endif
+
+#ifndef ASM
+int generic_NCR5380_abort(Scsi_Cmnd *);
+int generic_NCR5380_detect(Scsi_Host_Template *);
+int generic_NCR5380_release_resources(struct Scsi_Host *);
+int generic_NCR5380_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int generic_NCR5380_reset(Scsi_Cmnd *);
+#ifdef BIOSPARAM
+int generic_NCR5380_biosparam(Disk *, kdev_t, int *);
+#endif
+
+int generic_NCR5380_proc_info(char* buffer, char** start, off_t offset, int length, int hostno, int inout);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 16
+#endif
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define GENERIC_NCR5380 {NULL, NULL, NULL, \
+ generic_NCR5380_proc_info, \
+ "Generic NCR5380/NCR53C400 Scsi Driver", \
+ generic_NCR5380_detect, generic_NCR5380_release_resources, \
+ generic_NCR5380_info, NULL, \
+ generic_NCR5380_queue_command, generic_NCR5380_abort, \
+ generic_NCR5380_reset, NULL, \
+ NCR5380_BIOSPARAM, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+
+#ifndef HOSTS_C
+
+#define __STRVAL(x) #x
+#define STRVAL(x) __STRVAL(x)
+
+#ifdef CONFIG_SCSI_G_NCR5380_PORT
+
+#define NCR5380_map_config port
+
+#define NCR5380_map_type int
+
+#define NCR5380_map_name port
+
+#define NCR5380_instance_name io_port
+
+#define NCR53C400_register_offset 0
+
+#define NCR53C400_address_adjust 8
+
+#ifdef NCR53C400
+#define NCR5380_region_size 16
+#else
+#define NCR5380_region_size 8
+#endif
+
+#define NCR5380_read(reg) (inb(NCR5380_map_name + (reg)))
+#define NCR5380_write(reg, value) (outb((value), (NCR5380_map_name + (reg))))
+
+#else
+/* therefore CONFIG_SCSI_G_NCR5380_MEM */
+
+#define NCR5380_map_config memory
+
+#define NCR5380_map_type volatile unsigned char*
+
+#define NCR5380_map_name base
+
+#define NCR5380_instance_name base
+
+#define NCR53C400_register_offset 0x108
+
+#define NCR53C400_address_adjust 0
+
+#define NCR53C400_mem_base 0x3880
+
+#define NCR53C400_host_buffer 0x3900
+
+#define NCR5380_region_size 0x3a00
+
+
+#define NCR5380_read(reg) (*(NCR5380_map_name + NCR53C400_mem_base + (reg)))
+#define NCR5380_write(reg, value) (*(NCR5380_map_name + NCR53C400_mem_base + (reg)) = value)
+
+#endif
+
+#define NCR5380_implementation_fields \
+ NCR5380_map_type NCR5380_map_name
+
+#define NCR5380_local_declare() \
+ register NCR5380_implementation_fields
+
+#define NCR5380_setup(instance) \
+ NCR5380_map_name = (NCR5380_map_type)((instance)->NCR5380_instance_name)
+
+#define NCR5380_intr generic_NCR5380_intr
+#define NCR5380_queue_command generic_NCR5380_queue_command
+#define NCR5380_abort generic_NCR5380_abort
+#define NCR5380_reset generic_NCR5380_reset
+#define NCR5380_pread generic_NCR5380_pread
+#define NCR5380_pwrite generic_NCR5380_pwrite
+
+#define BOARD_NCR5380 0
+#define BOARD_NCR53C400 1
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* GENERIC_NCR5380_H */
+
diff --git a/i386/i386at/gpl/linux/scsi/hosts.c b/i386/i386at/gpl/linux/scsi/hosts.c
new file mode 100644
index 00000000..cb0163b4
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/hosts.c
@@ -0,0 +1,436 @@
+/*
+ * hosts.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * mid to lowlevel SCSI driver interface
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ */
+
+
+/*
+ * This file contains the medium level SCSI
+ * host interface initialization, as well as the scsi_hosts array of SCSI
+ * hosts currently present in the system.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/config.h>
+#include <linux/blk.h>
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/proc_fs.h>
+
+#include "scsi.h"
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+#define HOSTS_C
+
+#include "hosts.h"
+
+#ifdef CONFIG_SCSI_ADVANSYS
+#include "advansys.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA152X
+#include "aha152x.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA1542
+#include "aha1542.h"
+#endif
+
+#ifdef CONFIG_SCSI_AHA1740
+#include "aha1740.h"
+#endif
+
+#ifdef CONFIG_SCSI_AIC7XXX
+#include "aic7xxx.h"
+#endif
+
+#ifdef CONFIG_SCSI_BUSLOGIC
+#include "BusLogic.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA_DMA
+#include "eata_dma.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA_PIO
+#include "eata_pio.h"
+#endif
+
+#ifdef CONFIG_SCSI_U14_34F
+#include "u14-34f.h"
+#endif
+
+#ifdef CONFIG_SCSI_FUTURE_DOMAIN
+#include "fdomain.h"
+#endif
+
+#ifdef CONFIG_SCSI_GENERIC_NCR5380
+#include "g_NCR5380.h"
+#endif
+
+#ifdef CONFIG_SCSI_IN2000
+#include "in2000.h"
+#endif
+
+#ifdef CONFIG_SCSI_PAS16
+#include "pas16.h"
+#endif
+
+#ifdef CONFIG_SCSI_QLOGIC
+#include "qlogic.h"
+#endif
+
+#ifdef CONFIG_SCSI_SEAGATE
+#include "seagate.h"
+#endif
+
+#ifdef CONFIG_SCSI_T128
+#include "t128.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C7xx
+#include "53c7,8xx.h"
+#endif
+
+#ifdef CONFIG_SCSI_ULTRASTOR
+#include "ultrastor.h"
+#endif
+
+#ifdef CONFIG_SCSI_7000FASST
+#include "wd7000.h"
+#endif
+
+#ifdef CONFIG_SCSI_EATA
+#include "eata.h"
+#endif
+
+#ifdef CONFIG_SCSI_NCR53C406A
+#include "NCR53c406a.h"
+#endif
+
+#ifdef CONFIG_SCSI_AM53C974
+#include "AM53C974.h"
+#endif
+
+#ifdef CONFIG_SCSI_DEBUG
+#include "scsi_debug.h"
+#endif
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/i386/i386at/gpl/linux/scsi/Attic/hosts.c,v 1.1.1.1 1997/02/25 21:27:49 thomas Exp $";
+*/
+
+/*
+ * The scsi host entries should be in the order you wish the
+ * cards to be detected. A driver may appear more than once IFF
+ * it can deal with being detected (and therefore initialized)
+ * with more than one simultaneous host number, can handle being
+ * reentrant, etc.
+ *
+ * They may appear in any order, as each SCSI host is told which host
+ * number it is during detection.
+ */
+
+/* This is a placeholder for controllers that are not configured into
+ * the system - we do this to ensure that the controller numbering is
+ * always consistent, no matter how the kernel is configured. */
+
+#define NO_CONTROLLER {NULL, NULL, NULL, NULL, NULL, NULL, NULL, \
+ NULL, NULL, 0, 0, 0, 0, 0, 0}
+
+/*
+ * When figure is run, we don't want to link to any object code. Since
+ * the macro for each host will contain function pointers, we cannot
+ * use it and instead must use a "blank" that does no such
+ * idiocy.
+ */
+
+Scsi_Host_Template * scsi_hosts = NULL;
+
+static Scsi_Host_Template builtin_scsi_hosts[] =
+{
+#ifdef CONFIG_SCSI_ADVANSYS
+ ADVANSYS,
+#endif
+/* BusLogic must come before aha1542.c */
+#ifdef CONFIG_SCSI_BUSLOGIC
+ BUSLOGIC,
+#endif
+#ifdef CONFIG_SCSI_U14_34F
+ ULTRASTOR_14_34F,
+#endif
+#ifdef CONFIG_SCSI_ULTRASTOR
+ ULTRASTOR_14F,
+#endif
+#ifdef CONFIG_SCSI_AHA152X
+ AHA152X,
+#endif
+#ifdef CONFIG_SCSI_AHA1542
+ AHA1542,
+#endif
+#ifdef CONFIG_SCSI_AHA1740
+ AHA1740,
+#endif
+#ifdef CONFIG_SCSI_AIC7XXX
+ AIC7XXX,
+#endif
+#ifdef CONFIG_SCSI_FUTURE_DOMAIN
+ FDOMAIN_16X0,
+#endif
+#ifdef CONFIG_SCSI_IN2000
+ IN2000,
+#endif
+#ifdef CONFIG_SCSI_GENERIC_NCR5380
+ GENERIC_NCR5380,
+#endif
+#ifdef CONFIG_SCSI_NCR53C406A /* 53C406A should come before QLOGIC */
+ NCR53c406a,
+#endif
+#ifdef CONFIG_SCSI_QLOGIC
+ QLOGIC,
+#endif
+#ifdef CONFIG_SCSI_PAS16
+ MV_PAS16,
+#endif
+#ifdef CONFIG_SCSI_SEAGATE
+ SEAGATE_ST0X,
+#endif
+#ifdef CONFIG_SCSI_T128
+ TRANTOR_T128,
+#endif
+#ifdef CONFIG_SCSI_NCR53C7xx
+ NCR53c7xx,
+#endif
+#ifdef CONFIG_SCSI_EATA_DMA
+ EATA_DMA,
+#endif
+#ifdef CONFIG_SCSI_EATA_PIO
+ EATA_PIO,
+#endif
+#ifdef CONFIG_SCSI_7000FASST
+ WD7000,
+#endif
+#ifdef CONFIG_SCSI_EATA
+ EATA,
+#endif
+#ifdef CONFIG_SCSI_AM53C974
+ AM53C974,
+#endif
+#ifdef CONFIG_SCSI_DEBUG
+ SCSI_DEBUG,
+#endif
+};
+
+#define MAX_SCSI_HOSTS (sizeof(builtin_scsi_hosts) / sizeof(Scsi_Host_Template))
+
+
+/*
+ * Our semaphores and timeout counters, where size depends on
+ * MAX_SCSI_HOSTS here.
+ */
+
+struct Scsi_Host * scsi_hostlist = NULL;
+struct Scsi_Device_Template * scsi_devicelist = NULL;
+
+int max_scsi_hosts = 0;
+int next_scsi_host = 0;
+
+void
+scsi_unregister(struct Scsi_Host * sh){
+ struct Scsi_Host * shpnt;
+
+ if(scsi_hostlist == sh)
+ scsi_hostlist = sh->next;
+ else {
+ shpnt = scsi_hostlist;
+ while(shpnt->next != sh) shpnt = shpnt->next;
+ shpnt->next = shpnt->next->next;
+ }
+
+ /* If we are removing the last host registered, it is safe to reuse
+ * its host number (this avoids "holes" at boot time) (DB)
+ */
+ if (max_scsi_hosts == next_scsi_host && !scsi_loadable_module_flag)
+ max_scsi_hosts--;
+
+ next_scsi_host--;
+ scsi_init_free((char *) sh, sizeof(struct Scsi_Host) + sh->extra_bytes);
+}
+
+/* We call this when we come across a new host adapter. We only do this
+ * once we are 100% sure that we want to use this host adapter - it is a
+ * pain to reverse this, so we try and avoid it
+ */
+
+struct Scsi_Host * scsi_register(Scsi_Host_Template * tpnt, int j){
+ struct Scsi_Host * retval, *shpnt;
+ retval = (struct Scsi_Host *)scsi_init_malloc(sizeof(struct Scsi_Host) + j,
+ (tpnt->unchecked_isa_dma && j ? GFP_DMA : 0) | GFP_ATOMIC);
+ retval->host_busy = 0;
+ retval->block = NULL;
+ retval->wish_block = 0;
+ if(j > 0xffff) panic("Too many extra bytes requested\n");
+ retval->extra_bytes = j;
+ retval->loaded_as_module = scsi_loadable_module_flag;
+ retval->host_no = max_scsi_hosts++; /* never reuse host_no (DB) */
+ next_scsi_host++;
+ retval->host_queue = NULL;
+ retval->host_wait = NULL;
+ retval->last_reset = 0;
+ retval->irq = 0;
+ retval->dma_channel = 0xff;
+
+ /* These three are default values which can be overridden */
+ retval->max_channel = 0;
+ retval->max_id = 8;
+ retval->max_lun = 8;
+
+ retval->unique_id = 0;
+ retval->io_port = 0;
+ retval->hostt = tpnt;
+ retval->next = NULL;
+#ifdef DEBUG
+ printk("Register %x %x: %d\n", (int)retval, (int)retval->hostt, j);
+#endif
+
+ /* The next six are the default values which can be overridden
+ * if need be */
+ retval->this_id = tpnt->this_id;
+ retval->can_queue = tpnt->can_queue;
+ retval->sg_tablesize = tpnt->sg_tablesize;
+ retval->cmd_per_lun = tpnt->cmd_per_lun;
+ retval->unchecked_isa_dma = tpnt->unchecked_isa_dma;
+ retval->use_clustering = tpnt->use_clustering;
+ if(!scsi_hostlist)
+ scsi_hostlist = retval;
+ else
+ {
+ shpnt = scsi_hostlist;
+ while(shpnt->next) shpnt = shpnt->next;
+ shpnt->next = retval;
+ }
+
+ return retval;
+}
+
+int
+scsi_register_device(struct Scsi_Device_Template * sdpnt)
+{
+ if(sdpnt->next) panic("Device already registered");
+ sdpnt->next = scsi_devicelist;
+ scsi_devicelist = sdpnt;
+ return 0;
+}
+
+unsigned int scsi_init()
+{
+ static int called = 0;
+ int i, pcount;
+ Scsi_Host_Template * tpnt;
+ struct Scsi_Host * shpnt;
+ const char * name;
+
+ if(called) return 0;
+
+ called = 1;
+ for (tpnt = &builtin_scsi_hosts[0], i = 0; i < MAX_SCSI_HOSTS; ++i, tpnt++)
+ {
+ /*
+ * Initialize our semaphores. -1 is interpreted to mean
+ * "inactive" - where as 0 will indicate a time out condition.
+ */
+
+ pcount = next_scsi_host;
+ if ((tpnt->detect) &&
+ (tpnt->present =
+ tpnt->detect(tpnt)))
+ {
+ /* The only time this should come up is when people use
+ * some kind of patched driver of some kind or another. */
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1)
+ panic("Failure to register low-level scsi driver");
+ /* The low-level driver failed to register a driver. We
+ * can do this now. */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts;
+ scsi_hosts = tpnt;
+
+ /* Add the driver to /proc/scsi */
+#if CONFIG_PROC_FS
+ build_proc_dir_entries(tpnt);
+#endif
+ }
+ }
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if(shpnt->hostt->info)
+ name = shpnt->hostt->info(shpnt);
+ else
+ name = shpnt->hostt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* Now attach the high level drivers */
+#ifdef CONFIG_BLK_DEV_SD
+ scsi_register_device(&sd_template);
+#endif
+#ifdef CONFIG_BLK_DEV_SR
+ scsi_register_device(&sr_template);
+#endif
+#ifdef CONFIG_CHR_DEV_ST
+ scsi_register_device(&st_template);
+#endif
+#ifdef CONFIG_CHR_DEV_SG
+ scsi_register_device(&sg_template);
+#endif
+
+#if 0
+ max_scsi_hosts = next_scsi_host;
+#endif
+ return 0;
+}
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/hosts.h b/i386/i386at/gpl/linux/scsi/hosts.h
new file mode 100644
index 00000000..1da480de
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/hosts.h
@@ -0,0 +1,409 @@
+/*
+ * hosts.h Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * mid to low-level SCSI driver interface header
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Further modified by Eric Youngdale to support multiple host adapters
+ * of the same type.
+ */
+
+#ifndef _HOSTS_H
+#define _HOSTS_H
+
+/*
+ $Header: cvs/gnumach/i386/i386at/gpl/linux/scsi/Attic/hosts.h,v 1.1.1.1 1997/02/25 21:27:49 thomas Exp $
+*/
+
+#include <linux/proc_fs.h>
+
+/* It is senseless to set SG_ALL any higher than this - the performance
+ * does not get any better, and it wastes memory
+ */
+#define SG_NONE 0
+#define SG_ALL 0xff
+
+#define DISABLE_CLUSTERING 0
+#define ENABLE_CLUSTERING 1
+
+/* The various choices mean:
+ * NONE: Self evident. Host adapter is not capable of scatter-gather.
+ * ALL: Means that the host adapter module can do scatter-gather,
+ * and that there is no limit to the size of the table to which
+ * we scatter/gather data.
+ * Anything else: Indicates the maximum number of chains that can be
+ * used in one scatter-gather request.
+ */
+
+/*
+ * The Scsi_Host_Template type has all that is needed to interface with a SCSI
+ * host in a device independent matter. There is one entry for each different
+ * type of host adapter that is supported on the system.
+ */
+
+typedef struct scsi_disk Disk;
+
+typedef struct SHT
+{
+
+ /* Used with loadable modules so we can construct a linked list. */
+ struct SHT * next;
+
+ /* Used with loadable modules so that we know when it is safe to unload */
+ long * usage_count;
+
+ /* The pointer to the /proc/scsi directory entry */
+ struct proc_dir_entry *proc_dir;
+
+ /* proc-fs info function.
+ * Can be used to export driver statistics and other infos to the world
+ * outside the kernel ie. userspace and it also provides an interface
+ * to feed the driver with information. Check eata_dma_proc.c for reference
+ */
+ int (*proc_info)(char *, char **, off_t, int, int, int);
+
+ /*
+ * The name pointer is a pointer to the name of the SCSI
+ * device detected.
+ */
+ const char *name;
+
+ /*
+ * The detect function shall return non zero on detection,
+ * indicating the number of host adapters of this particular
+ * type were found. It should also
+ * initialize all data necessary for this particular
+ * SCSI driver. It is passed the host number, so this host
+ * knows where the first entry is in the scsi_hosts[] array.
+ *
+ * Note that the detect routine MUST not call any of the mid level
+ * functions to queue commands because things are not guaranteed
+ * to be set up yet. The detect routine can send commands to
+ * the host adapter as long as the program control will not be
+ * passed to scsi.c in the processing of the command. Note
+ * especially that scsi_malloc/scsi_free must not be called.
+ */
+ int (* detect)(struct SHT *);
+
+ /* Used with loadable modules to unload the host structures. Note:
+ * there is a default action built into the modules code which may
+ * be sufficient for most host adapters. Thus you may not have to supply
+ * this at all.
+ */
+ int (*release)(struct Scsi_Host *);
+
+ /*
+ * The info function will return whatever useful
+ * information the developer sees fit. If not provided, then
+ * the name field will be used instead.
+ */
+ const char *(* info)(struct Scsi_Host *);
+
+ /*
+ * The command function takes a target, a command (this is a SCSI
+ * command formatted as per the SCSI spec, nothing strange), a
+ * data buffer pointer, and data buffer length pointer. The return
+ * is a status int, bit fielded as follows :
+ * Byte What
+ * 0 SCSI status code
+ * 1 SCSI 1 byte message
+ * 2 host error return.
+ * 3 mid level error return
+ */
+ int (* command)(Scsi_Cmnd *);
+
+ /*
+ * The QueueCommand function works in a similar manner
+ * to the command function. It takes an additional parameter,
+ * void (* done)(int host, int code) which is passed the host
+ * # and exit result when the command is complete.
+ * Host number is the POSITION IN THE hosts array of THIS
+ * host adapter.
+ */
+ int (* queuecommand)(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+ /*
+ * Since the mid level driver handles time outs, etc, we want to
+ * be able to abort the current command. Abort returns 0 if the
+ * abortion was successful. The field SCpnt->abort reason
+ * can be filled in with the appropriate reason why we wanted
+ * the abort in the first place, and this will be used
+ * in the mid-level code instead of the host_byte().
+ * If non-zero, the code passed to it
+ * will be used as the return code, otherwise
+ * DID_ABORT should be returned.
+ *
+ * Note that the scsi driver should "clean up" after itself,
+ * resetting the bus, etc. if necessary.
+ */
+ int (* abort)(Scsi_Cmnd *);
+
+ /*
+ * The reset function will reset the SCSI bus. Any executing
+ * commands should fail with a DID_RESET in the host byte.
+ * The Scsi_Cmnd is passed so that the reset routine can figure
+ * out which host adapter should be reset, and also which command
+ * within the command block was responsible for the reset in
+ * the first place. Some hosts do not implement a reset function,
+ * and these hosts must call scsi_request_sense(SCpnt) to keep
+ * the command alive.
+ */
+ int (* reset)(Scsi_Cmnd *);
+
+ /*
+ * This function is used to select synchronous communications,
+ * which will result in a higher data throughput. Not implemented
+ * yet.
+ */
+ int (* slave_attach)(int, int);
+
+ /*
+ * This function determines the bios parameters for a given
+ * harddisk. These tend to be numbers that are made up by
+ * the host adapter. Parameters:
+ * size, device number, list (heads, sectors, cylinders)
+ */
+ int (* bios_param)(Disk *, kdev_t, int []);
+
+ /*
+ * This determines if we will use a non-interrupt driven
+ * or an interrupt driven scheme, It is set to the maximum number
+ * of simultaneous commands a given host adapter will accept.
+ */
+ int can_queue;
+
+ /*
+ * In many instances, especially where disconnect / reconnect are
+ * supported, our host also has an ID on the SCSI bus. If this is
+ * the case, then it must be reserved. Please set this_id to -1 if
+ * your setup is in single initiator mode, and the host lacks an
+ * ID.
+ */
+ int this_id;
+
+ /*
+ * This determines the degree to which the host adapter is capable
+ * of scatter-gather.
+ */
+ short unsigned int sg_tablesize;
+
+ /*
+ * True if this host adapter can make good use of linked commands.
+ * This will allow more than one command to be queued to a given
+ * unit on a given host. Set this to the maximum number of command
+ * blocks to be provided for each device. Set this to 1 for one
+ * command block per lun, 2 for two, etc. Do not set this to 0.
+ * You should make sure that the host adapter will do the right thing
+ * before you try setting this above 1.
+ */
+ short cmd_per_lun;
+
+ /*
+ * present contains counter indicating how many boards of this
+ * type were found when we did the scan.
+ */
+ unsigned char present;
+
+ /*
+ * true if this host adapter uses unchecked DMA onto an ISA bus.
+ */
+ unsigned unchecked_isa_dma:1;
+
+ /*
+ * true if this host adapter can make good use of clustering.
+ * I originally thought that if the tablesize was large that it
+ * was a waste of CPU cycles to prepare a cluster list, but
+ * it works out that the Buslogic is faster if you use a smaller
+ * number of segments (i.e. use clustering). I guess it is
+ * inefficient.
+ */
+ unsigned use_clustering:1;
+
+} Scsi_Host_Template;
+
+/*
+ * The scsi_hosts array is the array containing the data for all
+ * possible <supported> scsi hosts. This is similar to the
+ * Scsi_Host_Template, except that we have one entry for each
+ * actual physical host adapter on the system, stored as a linked
+ * list. Note that if there are 2 aha1542 boards, then there will
+ * be two Scsi_Host entries, but only 1 Scsi_Host_Template entries.
+ */
+
+struct Scsi_Host
+{
+ struct Scsi_Host * next;
+ unsigned short extra_bytes;
+ volatile unsigned char host_busy;
+ char host_no; /* Used for IOCTL_GET_IDLUN, /proc/scsi et al. */
+ int last_reset;
+ struct wait_queue *host_wait;
+ Scsi_Cmnd *host_queue;
+ Scsi_Host_Template * hostt;
+
+ /*
+ * These three parameters can be used to allow for wide scsi,
+ * and for host adapters that support multiple busses
+ * The first two should be set to 1 more than the actual max id
+ * or lun (i.e. 8 for normal systems).
+ */
+ unsigned int max_id;
+ unsigned int max_lun;
+ unsigned int max_channel;
+
+ /*
+ * Pointer to a circularly linked list - this indicates the hosts
+ * that should be locked out of performing I/O while we have an active
+ * command on this host.
+ */
+ struct Scsi_Host * block;
+ unsigned wish_block:1;
+
+ /* These parameters should be set by the detect routine */
+ unsigned char *base;
+ unsigned int io_port;
+ unsigned char n_io_port;
+ unsigned char irq;
+ unsigned char dma_channel;
+
+ /*
+ * This is a unique identifier that must be assigned so that we
+ * have some way of identifying each detected host adapter properly
+ * and uniquely. For hosts that do not support more than one card
+ * in the system at one time, this does not need to be set. It is
+ * initialized to 0 in scsi_register.
+ */
+ unsigned int unique_id;
+
+ /*
+ * The rest can be copied from the template, or specifically
+ * initialized, as required.
+ */
+
+ int this_id;
+ int can_queue;
+ short cmd_per_lun;
+ short unsigned int sg_tablesize;
+ unsigned unchecked_isa_dma:1;
+ unsigned use_clustering:1;
+ /*
+ * True if this host was loaded as a loadable module
+ */
+ unsigned loaded_as_module:1;
+
+ /*
+ * True when we call the low-level reset function, and
+ * the midlevel code suggests a full bus reset.
+ */
+ unsigned suggest_bus_reset:1;
+
+ unsigned long hostdata[0]; /* Used for storage of host specific stuff */
+};
+
+extern struct Scsi_Host * scsi_hostlist;
+extern struct Scsi_Device_Template * scsi_devicelist;
+
+extern Scsi_Host_Template * scsi_hosts;
+
+extern void build_proc_dir_entries(Scsi_Host_Template *);
+
+
+/*
+ * scsi_init initializes the scsi hosts.
+ */
+
+/*
+ * We use these goofy things because the MM is not set up when we init
+ * the scsi subsystem. By using these functions we can write code that
+ * looks normal. Also, it makes it possible to use the same code for a
+ * loadable module.
+ */
+
+extern void * scsi_init_malloc(unsigned int size, int priority);
+extern void scsi_init_free(char * ptr, unsigned int size);
+
+extern int next_scsi_host;
+
+extern int scsi_loadable_module_flag;
+unsigned int scsi_init(void);
+extern struct Scsi_Host * scsi_register(Scsi_Host_Template *, int j);
+extern void scsi_unregister(struct Scsi_Host * i);
+
+#define BLANK_HOST {"", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}
+
+struct Scsi_Device_Template
+{
+ struct Scsi_Device_Template * next;
+ const char * name;
+ const char * tag;
+ long * usage_count; /* Used for loadable modules */
+ unsigned char scsi_type;
+ unsigned char major;
+ unsigned char nr_dev; /* Number currently attached */
+ unsigned char dev_noticed; /* Number of devices detected. */
+ unsigned char dev_max; /* Current size of arrays */
+ unsigned blk:1; /* 0 if character device */
+ int (*detect)(Scsi_Device *); /* Returns 1 if we can attach this device */
+ int (*init)(void); /* Sizes arrays based upon number of devices
+ * detected */
+ void (*finish)(void); /* Perform initialization after attachment */
+ int (*attach)(Scsi_Device *); /* Attach devices to arrays */
+ void (*detach)(Scsi_Device *);
+};
+
+extern struct Scsi_Device_Template sd_template;
+extern struct Scsi_Device_Template st_template;
+extern struct Scsi_Device_Template sr_template;
+extern struct Scsi_Device_Template sg_template;
+
+int scsi_register_device(struct Scsi_Device_Template * sdpnt);
+
+/* These are used by loadable modules */
+extern int scsi_register_module(int, void *);
+extern void scsi_unregister_module(int, void *);
+
+/* The different types of modules that we can load and unload */
+#define MODULE_SCSI_HA 1
+#define MODULE_SCSI_CONST 2
+#define MODULE_SCSI_IOCTL 3
+#define MODULE_SCSI_DEV 4
+
+
+/*
+ * This is an ugly hack. If we expect to be able to load devices at run time,
+ * we need to leave extra room in some of the data structures. Doing a
+ * realloc to enlarge the structures would be riddled with race conditions,
+ * so until a better solution is discovered, we use this crude approach
+ */
+#define SD_EXTRA_DEVS 2
+#define ST_EXTRA_DEVS 2
+#define SR_EXTRA_DEVS 2
+#define SG_EXTRA_DEVS (SD_EXTRA_DEVS + SR_EXTRA_DEVS + ST_EXTRA_DEVS)
+
+#endif
+/*
+ * 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/i386/i386at/gpl/linux/scsi/in2000.c b/i386/i386at/gpl/linux/scsi/in2000.c
new file mode 100644
index 00000000..ac2cc656
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/in2000.c
@@ -0,0 +1,731 @@
+/*
+ * This file is in2000.c, written and
+ * Copyright (C) 1993 Brad McLean
+ * Last edit 1/19/95 TZ
+ * Disclaimer:
+ * Note: This is ugly. I know it, I wrote it, but my whole
+ * focus was on getting the damn thing up and out quickly.
+ * Future stuff that would be nice: Command chaining, and
+ * a local queue of commands would speed stuff up considerably.
+ * Disconnection needs some supporting code. All of this
+ * is beyond the scope of what I wanted to address, but if you
+ * have time and patience, more power to you.
+ * Also, there are some constants scattered throughout that
+ * should have defines, and I should have built functions to
+ * address the registers on the WD chip.
+ * Oh well, I'm out of time for this project.
+ * The one good thing to be said is that you can use the card.
+ */
+
+/*
+ * This module was updated by Shaun Savage first on 5-13-93
+ * At that time the write was fixed, irq detection, and some
+ * timing stuff. since that time other problems were fixed.
+ * On 7-20-93 this file was updated for patch level 11
+ * There are still problems with it but it work on 95% of
+ * the machines. There are still problems with it working with
+ * IDE drives, as swap drive and HD that support reselection.
+ * But for most people it will work.
+ */
+/* More changes by Bill Earnest, wde@aluxpo.att.com
+ * through 4/07/94. Includes rewrites of FIFO routines,
+ * length-limited commands to make swap partitions work.
+ * Merged the changes released by Larry Doolittle, based on input
+ * from Jon Luckey, Roger Sunshine, John Shifflett. The FAST_FIFO
+ * doesn't work for me. Scatter-gather code from Eric. The change to
+ * an IF stmt. in the interrupt routine finally made it stable.
+ * Limiting swap request size patch to ll_rw_blk.c not needed now.
+ * Please ignore the clutter of debug stmts., pretty can come later.
+ */
+/* Merged code from Matt Postiff improving the auto-sense validation
+ * for all I/O addresses. Some reports of problems still come in, but
+ * have been unable to reproduce or localize the cause. Some are from
+ * LUN > 0 problems, but that is not host specific. Now 6/6/94.
+ */
+/* Changes for 1.1.28 kernel made 7/19/94, code not affected. (WDE)
+ */
+/* Changes for 1.1.43+ kernels made 8/25/94, code added to check for
+ * new BIOS version, derived by jshiffle@netcom.com. (WDE)
+ *
+ * 1/7/95 Fix from Peter Lu (swift@world.std.com) for datalen vs. dataptr
+ * logic, much more stable under load.
+ *
+ * 1/19/95 (zerucha@shell.portal.com) Added module and biosparam support for
+ * larger SCSI hard drives (untested).
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <asm/dma.h>
+#include <asm/system.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#include "in2000.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_in2000 = {
+ PROC_SCSI_IN2000, 6, "in2000",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*#define FAST_FIFO_IO*/
+
+/*#define DEBUG*/
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+/* These functions are based on include/asm/io.h */
+#ifndef inw
+inline static unsigned short inw( unsigned short port )
+{
+ unsigned short _v;
+
+ __asm__ volatile ("inw %1,%0"
+ :"=a" (_v):"d" ((unsigned short) port));
+ return _v;
+}
+#endif
+
+#ifndef outw
+inline static void outw( unsigned short value, unsigned short port )
+{
+ __asm__ volatile ("outw %0,%1"
+ : /* no outputs */
+ :"a" ((unsigned short) value),
+ "d" ((unsigned short) port));
+}
+#endif
+
+/* These functions are lifted from drivers/block/hd.c */
+
+#define port_read(port,buf,nr) \
+__asm__("cld;rep;insw": :"d" (port),"D" (buf),"c" (nr):"cx","di")
+
+#define port_write(port,buf,nr) \
+__asm__("cld;rep;outsw": :"d" (port),"S" (buf),"c" (nr):"cx","si")
+
+static unsigned int base;
+static unsigned int ficmsk;
+static unsigned char irq_level;
+static int in2000_datalen;
+static unsigned int in2000_nsegment;
+static unsigned int in2000_current_segment;
+static unsigned short *in2000_dataptr;
+static char in2000_datawrite;
+static struct scatterlist * in2000_scatter;
+static Scsi_Cmnd *in2000_SCptr = 0;
+
+static void (*in2000_done)(Scsi_Cmnd *);
+
+static int in2000_test_port(int index)
+{
+ static const int *bios_tab[] = {
+ (int *) 0xc8000, (int *) 0xd0000, (int *) 0xd8000 };
+ int i;
+ char tmp;
+
+ tmp = inb(INFLED);
+ /* First, see if the DIP switch values are valid */
+ /* The test of B7 may fail on some early boards, mine works. */
+ if ( ((~tmp & 0x3) != index ) || (tmp & 0x80) || !(tmp & 0x4) )
+ return 0;
+ printk("IN-2000 probe got dip setting of %02X\n", tmp);
+ tmp = inb(INVERS);
+/* Add some extra sanity checks here */
+ for(i=0; i < 3; i++)
+ if(*(bios_tab[i]+0x04) == 0x41564f4e ||
+ *(bios_tab[i]+0xc) == 0x61776c41) {
+ printk("IN-2000 probe found hdw. vers. %02x, BIOS at %06x\n",
+ tmp, (unsigned int)bios_tab[i]);
+ return 1;
+ }
+ printk("in2000 BIOS not found.\n");
+ return 0;
+}
+
+
+/*
+ * retrieve the current transaction counter from the WD
+ */
+
+static unsigned in2000_txcnt(void)
+{
+ unsigned total=0;
+
+ if(inb(INSTAT) & 0x20) return 0xffffff; /* not readable now */
+ outb(TXCNTH,INSTAT); /* then autoincrement */
+ total = (inb(INDATA) & 0xff) << 16;
+ outb(TXCNTM,INSTAT);
+ total += (inb(INDATA) & 0xff) << 8;
+ outb(TXCNTL,INSTAT);
+ total += (inb(INDATA) & 0xff);
+ return total;
+}
+
+/*
+ * Note: the FIFO is screwy, and has a counter granularity of 16 bytes, so
+ * we have to reconcile the FIFO counter, the transaction byte count from the
+ * WD chip, and of course, our desired transaction size. It may look strange,
+ * and could probably use improvement, but it works, for now.
+ */
+
+static void in2000_fifo_out(void) /* uses FIFOCNTR */
+{
+ unsigned count, infcnt, txcnt;
+
+ infcnt = inb(INFCNT)& 0xfe; /* FIFO counter */
+ do {
+ txcnt = in2000_txcnt();
+/*DEB(printk("FIw:%d %02x %d\n", in2000_datalen, infcnt, txcnt));*/
+ count = (infcnt << 3) - 32; /* don't fill completely */
+ if ( count > in2000_datalen )
+ count = in2000_datalen; /* limit to actual data on hand */
+ count >>= 1; /* Words, not bytes */
+#ifdef FAST_FIFO_IO
+ if ( count ) {
+ port_write(INFIFO, in2000_dataptr, count);
+ in2000_datalen -= (count<<1);
+ }
+#else
+ while ( count-- )
+ {
+ outw(*in2000_dataptr++, INFIFO);
+ in2000_datalen -= 2;
+ }
+#endif
+ } while((in2000_datalen > 0) && ((infcnt = (inb(INFCNT)) & 0xfe) >= 0x20) );
+ /* If scatter-gather, go on to next segment */
+ if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
+ {
+ in2000_scatter++;
+ in2000_datalen = in2000_scatter->length;
+ in2000_dataptr = (unsigned short*)in2000_scatter->address;
+ }
+ if ( in2000_datalen <= 0 )
+ {
+ ficmsk = 0;
+ count = 32; /* Always says to use this much flush */
+ while ( count-- )
+ outw(0, INFIFO);
+ outb(2, ININTR); /* Mask FIFO Interrupts when done */
+ }
+}
+
+static void in2000_fifo_in(void) /* uses FIFOCNTR */
+{
+ unsigned fic, count, count2;
+
+ count = inb(INFCNT) & 0xe1;
+ do{
+ count2 = count;
+ count = (fic = inb(INFCNT)) & 0xe1;
+ } while ( count != count2 );
+DEB(printk("FIir:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
+ do {
+ count2 = in2000_txcnt(); /* bytes yet to come over SCSI bus */
+DEB(printk("FIr:%d %02x %08x %08x\n", in2000_datalen,fic,count2,(unsigned int)in2000_dataptr));
+ if(count2 > 65536) count2 = 0;
+ if(fic > 128) count = 1024;
+ else if(fic > 64) count = 512;
+ else if (fic > 32) count = 256;
+ else if ( count2 < in2000_datalen ) /* if drive has < what we want */
+ count = in2000_datalen - count2; /* FIFO has the rest */
+ if ( count > in2000_datalen ) /* count2 is lesser of FIFO & rqst */
+ count2 = in2000_datalen >> 1; /* converted to word count */
+ else
+ count2 = count >> 1;
+ count >>= 1; /* also to words */
+ count -= count2; /* extra left over in FIFO */
+#ifdef FAST_FIFO_IO
+ if ( count2 ) {
+ port_read(INFIFO, in2000_dataptr, count2);
+ in2000_datalen -= (count2<<1);
+ }
+#else
+ while ( count2-- )
+ {
+ *in2000_dataptr++ = inw(INFIFO);
+ in2000_datalen -=2;
+ }
+#endif
+ } while((in2000_datalen > 0) && (fic = inb(INFCNT)) );
+DEB(printk("FIer:%d %02x %08x\n", in2000_datalen,fic,(unsigned int )in2000_dataptr));
+/* while ( count-- )
+ inw(INFIFO);*/ /* Throw away some extra stuff */
+ if( !in2000_datalen && ++in2000_current_segment < in2000_nsegment)
+ {
+ in2000_scatter++;
+ in2000_datalen = in2000_scatter->length;
+ in2000_dataptr = (unsigned short*)in2000_scatter->address;
+ }
+ if ( ! in2000_datalen ){
+ outb(2, ININTR); /* Mask FIFO Interrupts when done */
+ ficmsk = 0;}
+}
+
+static void in2000_intr_handle(int irq, struct pt_regs *regs)
+{
+ int result=0;
+ unsigned int count,auxstatus,scsistatus,cmdphase,scsibyte;
+ int action=0;
+ Scsi_Cmnd *SCptr;
+
+ DEB(printk("INT:%d %02x %08x\n", in2000_datalen, inb(INFCNT),(unsigned int)in2000_dataptr));
+
+ if (( (ficmsk & (count = inb(INFCNT))) == 0xfe ) ||
+ ( (inb(INSTAT) & 0x8c) == 0x80))
+ { /* FIFO interrupt or WD interrupt */
+ auxstatus = inb(INSTAT); /* need to save now */
+ outb(SCSIST,INSTAT);
+ scsistatus = inb(INDATA); /* This clears the WD intrpt bit */
+ outb(TARGETU,INSTAT); /* then autoincrement */
+ scsibyte = inb(INDATA); /* Get the scsi status byte */
+ outb(CMDPHAS,INSTAT);
+ cmdphase = inb(INDATA);
+ DEB(printk("(int2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
+ scsistatus,cmdphase,scsibyte));
+
+ /* Why do we assume that we need to send more data here??? ERY */
+ if ( in2000_datalen ) /* data xfer pending */
+ {
+ if ( in2000_dataptr == NULL )
+ printk("int2000: dataptr=NULL datalen=%d\n",
+ in2000_datalen);
+ else if ( in2000_datawrite )
+ in2000_fifo_out();
+ else
+ in2000_fifo_in();
+ }
+ if ( (auxstatus & 0x8c) == 0x80 )
+ { /* There is a WD Chip interrupt & register read good */
+ outb(2,ININTR); /* Disable fifo interrupts */
+ ficmsk = 0;
+ result = DID_OK << 16;
+ /* 16=Select & transfer complete, 85=got disconnect */
+ if ((scsistatus != 0x16) && (scsistatus != 0x85)
+ && (scsistatus != 0x42)){
+/* printk("(WDi2000:%02x %02x %02x %02x %02x)\n",count,auxstatus,
+ scsistatus,cmdphase,scsibyte);*/
+/* printk("QDAT:%d %08x %02x\n",
+ in2000_datalen,(unsigned int)in2000_dataptr,ficmsk);*/
+ ;
+ }
+ switch ( scsistatus & 0xf0 )
+ {
+ case 0x00: /* Card Reset Completed */
+ action = 3;
+ break;
+ case 0x10: /* Successful Command Completion */
+ if ( scsistatus & 0x8 )
+ action = 1;
+ break;
+ case 0x20: /* Command Paused or Aborted */
+ if ( (scsistatus & 0x8) )
+ action = 1;
+ else if ( (scsistatus & 7) < 2 )
+ action = 2;
+ else
+ result = DID_ABORT << 16;
+ break;
+ case 0x40: /* Terminated early */
+ if ( scsistatus & 0x8 )
+ action = 1;
+ else if ( (scsistatus & 7) > 2 )
+ action = 2;
+ else
+ result = DID_TIME_OUT << 16;
+ break;
+ case 0x80: /* Service Required from SCSI bus */
+ if ( scsistatus & 0x8 )
+ action = 1;
+ else
+ action = 2;
+ break;
+ } /* end switch(scsistatus) */
+ outb(0,INFLED);
+ switch ( action )
+ {
+ case 0x02: /* Issue an abort */
+ outb(COMMAND,INSTAT);
+ outb(1,INDATA); /* ABORT COMMAND */
+ result = DID_ABORT << 16;
+ case 0x00: /* Basically all done */
+ if ( ! in2000_SCptr )
+ return;
+ in2000_SCptr->result = result | scsibyte;
+ SCptr = in2000_SCptr;
+ in2000_SCptr = 0;
+ if ( in2000_done )
+ (*in2000_done)(SCptr);
+ break;
+ case 0x01: /* We need to reissue a command */
+ outb(CMDPHAS,INSTAT);
+ switch ( scsistatus & 7 )
+ {
+ case 0: /* Data out phase */
+ case 1: /* Data in phase */
+ case 4: /* Unspec info out phase */
+ case 5: /* Unspec info in phase */
+ case 6: /* Message in phase */
+ case 7: /* Message in phase */
+ outb(0x41,INDATA); /* rdy to disconn */
+ break;
+ case 2: /* command phase */
+ outb(0x30,INDATA); /* rdy to send cmd bytes */
+ break;
+ case 3: /* status phase */
+ outb(0x45,INDATA); /* To go to status phase,*/
+ outb(TXCNTH,INSTAT); /* elim. data, autoinc */
+ outb(0,INDATA);
+ outb(0,INDATA);
+ outb(0,INDATA);
+ in2000_datalen = 0;
+ in2000_dataptr = 0;
+ break;
+ } /* end switch(scsistatus) */
+ outb(COMMAND,INSTAT);
+ outb(8,INDATA); /* RESTART THE COMMAND */
+ break;
+ case 0x03: /* Finish up a Card Reset */
+ outb(TIMEOUT,INSTAT); /* I got these values */
+ /* by reverse Engineering */
+ outb(IN2000_TMOUT,INDATA); /* the Always' bios. */
+ outb(CONTROL,INSTAT);
+ outb(0,INDATA);
+ outb(SYNCTXR,INSTAT);
+ outb(0x40,INDATA); /* async, 4 cyc xfer per. */
+ break;
+ } /* end switch(action) */
+ } /* end if auxstatus for WD int */
+ } /* end while intrpt active */
+}
+
+int in2000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar direction;
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ unchar target = SCpnt->target;
+ void *buff = SCpnt->request_buffer;
+ unsigned long flags;
+ int bufflen = SCpnt->request_bufflen;
+ int timeout, size, loop;
+ int i;
+
+ /*
+ * This SCSI command has no data phase, but unfortunately the mid-level
+ * SCSI drivers ask for 256 bytes of data xfer. Our card hangs if you
+ * do this, so we protect against it here. It would be nice if the mid-
+ * level could be changed, but who knows if that would break other host
+ * adapter drivers.
+ */
+ if ( *cmd == TEST_UNIT_READY )
+ bufflen = 0;
+
+ /*
+ * What it looks like. Boy did I get tired of reading its output.
+ */
+ if (*cmd == READ_10 || *cmd == WRITE_10) {
+ i = xscsi2int((cmd+1));
+ } else if (*cmd == READ_6 || *cmd == WRITE_6) {
+ i = scsi2int((cmd+1));
+ } else {
+ i = -1;
+ }
+#ifdef DEBUG
+ printk("in2000qcmd: pos %d len %d ", i, bufflen);
+ printk("scsi cmd:");
+ for (i = 0; i < SCpnt->cmd_len; i++) printk("%02x ", cmd[i]);
+ printk("\n");
+#endif
+ direction = 1; /* assume for most commands */
+ if (*cmd == WRITE_10 || *cmd == WRITE_6)
+ direction = 0;
+ size = SCpnt->cmd_len; /* CDB length */
+ /*
+ * Setup our current pointers
+ * This is where you would allocate a control structure in a queue,
+ * If you were going to upgrade this to do multiple issue.
+ * Note that datalen and dataptr exist because we can change the
+ * values during the course of the operation, while managing the
+ * FIFO.
+ * Note the nasty little first clause. In theory, the mid-level
+ * drivers should never hand us more than one command at a time,
+ * but just in case someone gets cute in configuring the driver,
+ * we'll protect them, although not very politely.
+ */
+ if ( in2000_SCptr )
+ {
+ printk("in2000_queue_command waiting for free command block!\n");
+ while ( in2000_SCptr )
+ barrier();
+ }
+ for ( timeout = jiffies + 5; timeout > jiffies; )
+ {
+ if ( ! ( inb(INSTAT) & 0xb0 ) )
+ {
+ timeout = 0;
+ break;
+ }
+ else
+ {
+ inb(INSTAT);
+ outb(SCSIST,INSTAT);
+ inb(INDATA);
+ outb(TARGETU,INSTAT); /* then autoinc */
+ inb(INDATA);
+ inb(INDATA);
+ }
+ }
+ if ( timeout )
+ {
+ printk("in2000_queue_command timeout!\n");
+ SCpnt->result = DID_TIME_OUT << 16;
+ (*done)(SCpnt);
+ return 1;
+ }
+ /* Added for scatter-gather support */
+ in2000_nsegment = SCpnt->use_sg;
+ in2000_current_segment = 0;
+ if(SCpnt->use_sg){
+ in2000_scatter = (struct scatterlist *) buff;
+ in2000_datalen = in2000_scatter->length;
+ in2000_dataptr = (unsigned short*)in2000_scatter->address;
+ } else {
+ in2000_scatter = NULL;
+ in2000_datalen = bufflen;
+ in2000_dataptr = (unsigned short*) buff;
+ };
+ in2000_done = done;
+ in2000_SCptr = SCpnt;
+ /*
+ * Write the CDB to the card, then the LUN, the length, and the target.
+ */
+ outb(TOTSECT, INSTAT); /* start here then autoincrement */
+ for ( loop=0; loop < size; loop++ )
+ outb(cmd[loop],INDATA);
+ outb(TARGETU,INSTAT);
+ outb(SCpnt->lun & 7,INDATA);
+ SCpnt->host_scribble = NULL;
+ outb(TXCNTH,INSTAT); /* then autoincrement */
+ outb(bufflen>>16,INDATA);
+ outb(bufflen>>8,INDATA);
+ outb(bufflen,INDATA);
+ outb(target&7,INDATA);
+ /*
+ * Set up the FIFO
+ */
+ save_flags(flags);
+ cli(); /* so FIFO init waits till WD set */
+ outb(0,INFRST);
+ if ( direction == 1 )
+ {
+ in2000_datawrite = 0;
+ outb(0,INFWRT);
+ }
+ else
+ {
+ in2000_datawrite = 1;
+ for ( loop=16; --loop; ) /* preload the outgoing fifo */
+ {
+ outw(*in2000_dataptr++,INFIFO);
+ if(in2000_datalen > 0) in2000_datalen-=2;
+ }
+ }
+ ficmsk = 0xff;
+ /*
+ * Start it up
+ */
+ outb(CONTROL,INSTAT); /* WD BUS Mode */
+ outb(0x4C,INDATA);
+ if ( in2000_datalen ) /* if data xfer cmd */
+ outb(0,ININTR); /* Enable FIFO intrpt some boards? */
+ outb(COMMAND,INSTAT);
+ outb(0,INNLED);
+ outb(8,INDATA); /* Select w/ATN & Transfer */
+ restore_flags(flags); /* let the intrpt rip */
+ return 0;
+}
+
+static volatile int internal_done_flag = 0;
+static volatile int internal_done_errcode = 0;
+
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+int in2000_command(Scsi_Cmnd * SCpnt)
+{
+ in2000_queuecommand(SCpnt, internal_done);
+
+ while (!internal_done_flag);
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+int in2000_detect(Scsi_Host_Template * tpnt)
+{
+/* Order chosen to reduce conflicts with some multi-port serial boards */
+ int base_tab[] = { 0x220,0x200,0x110,0x100 };
+ int int_tab[] = { 15,14,11,10 };
+ struct Scsi_Host * shpnt;
+ int loop, tmp;
+
+ DEB(printk("in2000_detect: \n"));
+
+ tpnt->proc_dir = &proc_scsi_in2000;
+
+ for ( loop=0; loop < 4; loop++ )
+ {
+ base = base_tab[loop];
+ if ( in2000_test_port(loop)) break;
+ }
+ if ( loop == 4 )
+ return 0;
+
+ /* Read the dip switch values again for miscellaneous checking and
+ informative messages */
+ tmp = inb(INFLED);
+
+ /* Bit 2 tells us if interrupts are disabled */
+ if ( (tmp & 0x4) == 0 ) {
+ printk("The IN-2000 is not configured for interrupt operation\n");
+ printk("Change the DIP switch settings to enable interrupt operation\n");
+ }
+
+ /* Bit 6 tells us about floppy controller */
+ printk("IN-2000 probe found floppy controller on IN-2000 ");
+ if ( (tmp & 0x40) == 0)
+ printk("enabled\n");
+ else
+ printk("disabled\n");
+
+ /* Bit 5 tells us about synch/asynch mode */
+ printk("IN-2000 probe found IN-2000 in ");
+ if ( (tmp & 0x20) == 0)
+ printk("synchronous mode\n");
+ else
+ printk("asynchronous mode\n");
+
+ irq_level = int_tab [ ((~inb(INFLED)>>3)&0x3) ];
+
+ printk("Configuring IN2000 at IO:%x, IRQ %d"
+#ifdef FAST_FIFO_IO
+ " (using fast FIFO I/O code)"
+#endif
+ "\n",base, irq_level);
+
+ outb(2,ININTR); /* Shut off the FIFO first, so it won't ask for data.*/
+ if (request_irq(irq_level,in2000_intr_handle, 0, "in2000"))
+ {
+ printk("in2000_detect: Unable to allocate IRQ.\n");
+ return 0;
+ }
+ outb(0,INFWRT); /* read mode so WD can intrpt */
+ outb(SCSIST,INSTAT);
+ inb(INDATA); /* free status reg, clear WD intrpt */
+ outb(OWNID,INSTAT);
+ outb(0x7,INDATA); /* we use addr 7 */
+ outb(COMMAND,INSTAT);
+ outb(0,INDATA); /* do chip reset */
+ shpnt = scsi_register(tpnt, 0);
+ /* Set these up so that we can unload the driver properly. */
+ shpnt->io_port = base;
+ shpnt->n_io_port = 12;
+ shpnt->irq = irq_level;
+ request_region(base, 12,"in2000"); /* Prevent other drivers from using this space */
+ return 1;
+}
+
+int in2000_abort(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("in2000_abort\n"));
+ /*
+ * Ask no stupid questions, just order the abort.
+ */
+ outb(COMMAND,INSTAT);
+ outb(1,INDATA); /* Abort Command */
+ return 0;
+}
+
+static inline void delay( unsigned how_long )
+{
+ unsigned long time = jiffies + how_long;
+ while (jiffies < time) ;
+}
+
+int in2000_reset(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("in2000_reset called\n"));
+ /*
+ * Note: this is finished off by an incoming interrupt
+ */
+ outb(0,INFWRT); /* read mode so WD can intrpt */
+ outb(SCSIST,INSTAT);
+ inb(INDATA);
+ outb(OWNID,INSTAT);
+ outb(0x7,INDATA); /* ID=7,noadv, no parity, clk div=2 (8-10Mhz clk) */
+ outb(COMMAND,INSTAT);
+ outb(0,INDATA); /* reset WD chip */
+ delay(2);
+#ifdef SCSI_RESET_PENDING
+ return SCSI_RESET_PENDING;
+#else
+ if(SCpnt) SCpnt->flags |= NEEDS_JUMPSTART;
+ return 0;
+#endif
+}
+
+int in2000_biosparam(Disk * disk, kdev_t dev, int* iinfo)
+ {
+ int size = disk->capacity;
+ DEB(printk("in2000_biosparam\n"));
+ iinfo[0] = 64;
+ iinfo[1] = 32;
+ iinfo[2] = size >> 11;
+/* This should approximate the large drive handling that the DOS ASPI manager
+ uses. Drives very near the boundaries may not be handled correctly (i.e.
+ near 2.0 Gb and 4.0 Gb) */
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 64;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 128;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ }
+ if (iinfo[2] > 1024) {
+ iinfo[0] = 255;
+ iinfo[1] = 63;
+ iinfo[2] = disk->capacity / (iinfo[0] * iinfo[1]);
+ if (iinfo[2] > 1023)
+ iinfo[2] = 1023;
+ }
+ return 0;
+ }
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = IN2000;
+
+#include "scsi_module.c"
+#endif
+
diff --git a/i386/i386at/gpl/linux/scsi/in2000.h b/i386/i386at/gpl/linux/scsi/in2000.h
new file mode 100644
index 00000000..cf68db7f
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/in2000.h
@@ -0,0 +1,122 @@
+#ifndef _IN2000_H
+
+/* $Id: in2000.h,v 1.1.1.1 1997/02/25 21:27:50 thomas Exp $
+ *
+ * Header file for the Always IN 2000 driver for Linux
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/ioport.h>
+
+/* The IN-2000 is based on a WD33C93 */
+
+#define INSTAT (base + 0x0) /* R: Auxiliary Status; W: register select */
+#define INDATA (base + 0x1) /* R/W: Data port */
+#define INFIFO (base + 0x2) /* R/W FIFO, Word access only */
+#define INREST (base + 0x3) /* W: Reset everything */
+#define INFCNT (base + 0x4) /* R: FIFO byte count */
+#define INFRST (base + 0x5) /* W: Reset Fifo count and to write */
+#define INFWRT (base + 0x7) /* W: Set FIFO to read */
+#define INFLED (base + 0x8) /* W: Set LED; R: Dip Switch settings */
+#define INNLED (base + 0x9) /* W: reset LED */
+#define INVERS (base + 0xa) /* R: Read hw version, end-reset */
+#define ININTR (base + 0xc) /* W: Interrupt Mask Port */
+#define G2CNTRL_HRDY 0x20 /* Sets HOST ready */
+
+/* WD33C93 defines */
+#define OWNID 0
+#undef CONTROL
+#define CONTROL 1
+#define TIMEOUT 2
+#define TOTSECT 3
+#define TOTHEAD 4
+#define TOTCYLH 5
+#define TOTCYLL 6
+#define LADRSHH 7
+#define LADRSHL 8
+#define LADRSLH 9
+#define LADRSLL 10
+#define SECTNUM 11
+#define HEADNUM 12
+#define CYLNUMH 13
+#define CYLNUML 14
+#define TARGETU 15
+#define CMDPHAS 16
+#define SYNCTXR 17
+#define TXCNTH 18
+#define TXCNTM 19
+#define TXCNTL 20
+#define DESTID 21
+#define SRCID 22
+#define SCSIST 23
+#define COMMAND 24
+#define WDDATA 25
+#define AUXSTAT 31
+
+/* OWNID Register Bits */
+#define OWN_EAF 0x08
+#define OWN_EHP 0x10
+#define OWN_FS0 0x40
+#define OWN_FS1 0x80
+/* AUX Register Bits */
+#define AUX_DBR 0
+#define AUX_PE 1
+#define AUX_CIP 0x10
+#define AUX_BSY 0x20
+#define AUX_LCI 0x40
+#define AUX_INT 0x80
+
+/* Select timeout const, 1 count = 8ms */
+#define IN2000_TMOUT 0x1f
+
+/* These belong in scsi.h also */
+#undef any2scsi
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16); \
+(up)[1] = (((unsigned long)(p)) >> 8); \
+(up)[2] = ((unsigned long)(p));
+
+#undef scsi2int
+#define scsi2int(up) ( ((((long)*(up))&0x1f) << 16) + (((long)(up)[1]) << 8) + ((long)(up)[2]) )
+
+#undef xany2scsi
+#define xany2scsi(up, p) \
+(up)[0] = ((long)(p)) >> 24; \
+(up)[1] = ((long)(p)) >> 16; \
+(up)[2] = ((long)(p)) >> 8; \
+(up)[3] = ((long)(p));
+
+#define xscsi2int(up) ( (((long)(up)[0]) << 24) + (((long)(up)[1]) << 16) \
+ + (((long)(up)[2]) << 8) + ((long)(up)[3]) )
+
+#define MAX_CDB 12
+#define MAX_SENSE 14
+#define MAX_STATUS 32
+
+int in2000_detect(Scsi_Host_Template *);
+int in2000_command(Scsi_Cmnd *);
+int in2000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int in2000_abort(Scsi_Cmnd *);
+int in2000_reset(Scsi_Cmnd *);
+int in2000_biosparam(Disk *, kdev_t, int*);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+
+/* next may be "SG_NONE" or "SG_ALL" or nr. of (1k) blocks per R/W Cmd. */
+#define IN2000_SG SG_ALL
+#define IN2000 {NULL, NULL, \
+ NULL, NULL, \
+ "Always IN2000", in2000_detect, NULL, \
+ NULL, in2000_command, \
+ in2000_queuecommand, \
+ in2000_abort, \
+ in2000_reset, \
+ NULL, \
+ in2000_biosparam, \
+ 1, 7, IN2000_SG, 1, 0, 0}
+
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/pas16.c b/i386/i386at/gpl/linux/scsi/pas16.c
new file mode 100644
index 00000000..9f5d8826
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/pas16.c
@@ -0,0 +1,553 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+#define FOO
+#define UNSAFE /* Not unsafe for PAS16 -- use it */
+
+/*
+ * This driver adapted from Drew Eckhardt's Trantor T128 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * ( Based on T128 - DISTRIBUTION RELEASE 3. )
+ *
+ * Modified to work with the Pro Audio Spectrum/Studio 16
+ * by John Weidman.
+ *
+ *
+ * For more information, please consult
+ *
+ * Media Vision
+ * (510) 770-8600
+ * (800) 348-7116
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * LIMIT_TRANSFERSIZE - if defined, limit the pseudo-dma transfers to 512
+ * bytes at a time. Since interrupts are disabled by default during
+ * these transfers, we might need this to give reasonable interrupt
+ * service time if the transfer size gets too large.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. This
+ * parameter comes from the NCR5380 code. It is NOT unsafe with
+ * the PAS16 and you should use it. If you don't you will have
+ * a problem with dropped characters during high speed
+ * communications during SCSI transfers. If you really don't
+ * want to use UNSAFE you can try defining LIMIT_TRANSFERSIZE or
+ * twiddle with the transfer size in the high level code.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - There are many different models of
+ * the Pro Audio Spectrum/Studio 16, and I only have one of
+ * them, so this may require a little tweaking. An interrupt
+ * is triggered to autoprobe for the interrupt line. Note:
+ * with the newer model boards, the interrupt is set via
+ * software after reset using the default_irq for the
+ * current board number.
+ *
+ *
+ * 2. With command line overrides - pas16=port,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 3. With the PAS16_OVERRIDE compile time define. This is
+ * specified as an array of address, irq tuples. Ie, for
+ * one board at the default 0x388 address, IRQ10, I could say
+ * -DPAS16_OVERRIDE={{0x388, 10}}
+ * NOTE: Untested.
+ *
+ * Note that if the override methods are used, place holders must
+ * be specified for other boards in the system.
+ *
+ *
+ * Configuration notes :
+ * The current driver does not support interrupt sharing with the
+ * sound portion of the card. If you use the same irq for the
+ * scsi port and sound you will have problems. Either use
+ * a different irq for the scsi port or don't use interrupts
+ * for the scsi port.
+ *
+ * If you have problems with your card not being recognized, use
+ * the LILO command line override. Try to get it recognized without
+ * interrupts. Ie, for a board at the default 0x388 base port,
+ * boot: linux pas16=0x388,255
+ *
+ * (255 is the IRQ_NONE constant in NCR5380.h)
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/proc_fs.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "pas16.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_pas16 = {
+ PROC_SCSI_PAS16, 5, "pas16",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+int scsi_irq_translate[] =
+ { 0, 0, 1, 2, 3, 4, 5, 6, 0, 0, 7, 8, 9, 0, 10, 11 };
+
+/* The default_irqs array contains values used to set the irq into the
+ * board via software (as must be done on newer model boards without
+ * irq jumpers on the board). The first value in the array will be
+ * assigned to logical board 0, the next to board 1, etc.
+ */
+int default_irqs[] = { PAS16_DEFAULT_BOARD_1_IRQ,
+ PAS16_DEFAULT_BOARD_2_IRQ,
+ PAS16_DEFAULT_BOARD_3_IRQ,
+ PAS16_DEFAULT_BOARD_4_IRQ
+ };
+
+static struct override {
+ unsigned short io_port;
+ int irq;
+} overrides
+#ifdef PAS16_OVERRIDE
+ [] = PAS16_OVERRIDE;
+#else
+ [4] = {{0,IRQ_AUTO}, {0,IRQ_AUTO}, {0,IRQ_AUTO},
+ {0,IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned short io_port;
+ int noauto;
+} bases[] = { {PAS16_DEFAULT_BASE_1, 0},
+ {PAS16_DEFAULT_BASE_2, 0},
+ {PAS16_DEFAULT_BASE_3, 0},
+ {PAS16_DEFAULT_BASE_4, 0}
+ };
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+unsigned short pas16_offset[ 8 ] =
+ {
+ 0x1c00, /* OUTPUT_DATA_REG */
+ 0x1c01, /* INITIATOR_COMMAND_REG */
+ 0x1c02, /* MODE_REG */
+ 0x1c03, /* TARGET_COMMAND_REG */
+ 0x3c00, /* STATUS_REG ro, SELECT_ENABLE_REG wo */
+ 0x3c01, /* BUS_AND_STATUS_REG ro, START_DMA_SEND_REG wo */
+ 0x3c02, /* INPUT_DATA_REGISTER ro, (N/A on PAS16 ?)
+ * START_DMA_TARGET_RECEIVE_REG wo
+ */
+ 0x3c03, /* RESET_PARITY_INTERRUPT_REG ro,
+ * START_DMA_INITIATOR_RECEIVE_REG wo
+ */
+ };
+
+
+
+/*
+ * Function : enable_board( int board_num, unsigned short port )
+ *
+ * Purpose : set address in new model board
+ *
+ * Inputs : board_num - logical board number 0-3, port - base address
+ *
+ */
+
+void enable_board( int board_num, unsigned short port )
+{
+ outb( 0xbc + board_num, MASTER_ADDRESS_PTR );
+ outb( port >> 2, MASTER_ADDRESS_PTR );
+}
+
+
+
+/*
+ * Function : init_board( unsigned short port, int irq )
+ *
+ * Purpose : Set the board up to handle the SCSI interface
+ *
+ * Inputs : port - base address of the board,
+ * irq - irq to assign to the SCSI port
+ * force_irq - set it even if it conflicts with sound driver
+ *
+ */
+
+void init_board( unsigned short io_port, int irq, int force_irq )
+{
+ unsigned int tmp;
+ unsigned int pas_irq_code;
+
+ /* Initialize the SCSI part of the board */
+
+ outb( 0x30, io_port + P_TIMEOUT_COUNTER_REG ); /* Timeout counter */
+ outb( 0x01, io_port + P_TIMEOUT_STATUS_REG_OFFSET ); /* Reset TC */
+ outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
+
+ NCR5380_read( RESET_PARITY_INTERRUPT_REG );
+
+ /* Set the SCSI interrupt pointer without mucking up the sound
+ * interrupt pointer in the same byte.
+ */
+ pas_irq_code = ( irq < 16 ) ? scsi_irq_translate[irq] : 0;
+ tmp = inb( io_port + IO_CONFIG_3 );
+
+ if( (( tmp & 0x0f ) == pas_irq_code) && pas_irq_code > 0
+ && !force_irq )
+ {
+ printk( "pas16: WARNING: Can't use same irq as sound "
+ "driver -- interrupts disabled\n" );
+ /* Set up the drive parameters, disable 5380 interrupts */
+ outb( 0x4d, io_port + SYS_CONFIG_4 );
+ }
+ else
+ {
+ tmp = ( tmp & 0x0f ) | ( pas_irq_code << 4 );
+ outb( tmp, io_port + IO_CONFIG_3 );
+
+ /* Set up the drive parameters and enable 5380 interrupts */
+ outb( 0x6d, io_port + SYS_CONFIG_4 );
+ }
+}
+
+
+/*
+ * Function : pas16_hw_detect( unsigned short board_num )
+ *
+ * Purpose : determine if a pas16 board is present
+ *
+ * Inputs : board_num - logical board number ( 0 - 3 )
+ *
+ * Returns : 0 if board not found, 1 if found.
+ */
+
+int pas16_hw_detect( unsigned short board_num )
+{
+ unsigned char board_rev, tmp;
+ unsigned short io_port = bases[ board_num ].io_port;
+
+ /* See if we can find a PAS16 board at the address associated
+ * with this logical board number.
+ */
+
+ /* First, attempt to take a newer model board out of reset and
+ * give it a base address. This shouldn't affect older boards.
+ */
+ enable_board( board_num, io_port );
+
+ /* Now see if it looks like a PAS16 board */
+ board_rev = inb( io_port + PCB_CONFIG );
+
+ if( board_rev == 0xff )
+ return 0;
+
+ tmp = board_rev ^ 0xe0;
+
+ outb( tmp, io_port + PCB_CONFIG );
+ tmp = inb( io_port + PCB_CONFIG );
+ outb( board_rev, io_port + PCB_CONFIG );
+
+ if( board_rev != tmp ) /* Not a PAS-16 */
+ return 0;
+
+ if( ( inb( io_port + OPERATION_MODE_1 ) & 0x03 ) != 0x03 )
+ return 0; /* return if no SCSI interface found */
+
+ /* Mediavision has some new model boards that return ID bits
+ * that indicate a SCSI interface, but they're not (LMS). We'll
+ * put in an additional test to try and weed them out.
+ */
+
+ outb( 0x01, io_port + WAIT_STATE ); /* 1 Wait state */
+ NCR5380_write( MODE_REG, 0x20 ); /* Is it really SCSI? */
+ if( NCR5380_read( MODE_REG ) != 0x20 ) /* Write to a reg. */
+ return 0; /* and try to read */
+ NCR5380_write( MODE_REG, 0x00 ); /* it back. */
+ if( NCR5380_read( MODE_REG ) != 0x00 )
+ return 0;
+
+ return 1;
+}
+
+
+/*
+ * Function : pas16_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void pas16_setup(char *str, int *ints) {
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("pas16_setup : usage pas16=io_port,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].io_port = (unsigned short) ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].io_port == (unsigned short) ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int pas16_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes PAS16 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int pas16_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0;
+ static unsigned short current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned short io_port;
+ int count;
+
+ tpnt->proc_dir = &proc_scsi_pas16;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ io_port = 0;
+
+ if (overrides[current_override].io_port)
+ {
+ io_port = overrides[current_override].io_port;
+ enable_board( current_override, io_port );
+ init_board( io_port, overrides[current_override].irq, 1 );
+ }
+ else
+ for (; !io_port && (current_base < NO_BASES); ++current_base) {
+#if (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : probing io_port %04x\n", (unsigned int) bases[current_base].io_port);
+#endif
+ if ( !bases[current_base].noauto &&
+ pas16_hw_detect( current_base ) ){
+ io_port = bases[current_base].io_port;
+ init_board( io_port, default_irqs[ current_base ], 0 );
+#if (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : detected board.\n");
+#endif
+ }
+ }
+
+
+#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
+ printk("scsi-pas16 : io_port = %04x\n", (unsigned int) io_port);
+#endif
+
+ if (!io_port)
+ break;
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->io_port = io_port;
+
+ NCR5380_init(instance, 0);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, PAS16_IRQS);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, pas16_intr, SA_INTERRUPT, "pas16")) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ /* Disable 5380 interrupts, leave drive params the same */
+ outb( 0x4d, io_port + SYS_CONFIG_4 );
+ outb( (inb(io_port + IO_CONFIG_3) & 0x0f), io_port + IO_CONFIG_3 );
+ }
+
+#if defined(PDEBUG) && (PDEBUG & PDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%04x", instance->host_no, (int)
+ instance->io_port);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, PAS16_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int pas16_biosparam(Disk *disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int pas16_biosparam(Disk * disk, kdev_t dev, int * ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11; /* I think I have it as /(32*64) */
+ if( ip[2] > 1024 ) { /* yes, >, not >= */
+ ip[0]=255;
+ ip[1]=63;
+ ip[2]=size/(63*255);
+ if( ip[2] > 1023 ) /* yes >1023... */
+ ip[2] = 1023;
+ }
+
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
+ int len) {
+ register unsigned char *d = dst;
+ register unsigned short reg = (unsigned short) (instance->io_port +
+ P_DATA_REG_OFFSET);
+ register i = len;
+
+ while ( !(inb(instance->io_port + P_STATUS_REG_OFFSET) & P_ST_RDY) );
+
+ insb( reg, d, i );
+
+ if ( inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
+ outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
+ printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
+ int len) {
+ register unsigned char *s = src;
+ register unsigned short reg = (instance->io_port + P_DATA_REG_OFFSET);
+ register i = len;
+
+ while ( !((inb(instance->io_port + P_STATUS_REG_OFFSET)) & P_ST_RDY) );
+
+ outsb( reg, s, i );
+
+ if (inb(instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET) & P_TS_TIM) {
+ outb( P_TS_CT, instance->io_port + P_TIMEOUT_STATUS_REG_OFFSET);
+ printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+#ifdef MACH
+#include "NCR5380.src"
+#else
+#include "NCR5380.c"
+#endif
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = MV_PAS16;
+
+#include <linux/module.h>
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/pas16.h b/i386/i386at/gpl/linux/scsi/pas16.h
new file mode 100644
index 00000000..9733792b
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/pas16.h
@@ -0,0 +1,193 @@
+/*
+ * This driver adapted from Drew Eckhardt's Trantor T128 driver
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 666-5836
+ *
+ * ( Based on T128 - DISTRIBUTION RELEASE 3. )
+ *
+ * Modified to work with the Pro Audio Spectrum/Studio 16
+ * by John Weidman.
+ *
+ *
+ * For more information, please consult
+ *
+ * Media Vision
+ * (510) 770-8600
+ * (800) 348-7116
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+
+#ifndef PAS16_H
+#define PAS16_H
+
+#define PAS16_PUBLIC_RELEASE 3
+
+#define PDEBUG_INIT 0x1
+#define PDEBUG_TRANSFER 0x2
+
+#define PAS16_DEFAULT_BASE_1 0x388
+#define PAS16_DEFAULT_BASE_2 0x384
+#define PAS16_DEFAULT_BASE_3 0x38c
+#define PAS16_DEFAULT_BASE_4 0x288
+
+#define PAS16_DEFAULT_BOARD_1_IRQ 10
+#define PAS16_DEFAULT_BOARD_2_IRQ 12
+#define PAS16_DEFAULT_BOARD_3_IRQ 14
+#define PAS16_DEFAULT_BOARD_4_IRQ 15
+
+
+/*
+ * The Pro Audio Spectrum boards are I/O mapped. They use a Zilog 5380
+ * SCSI controller, which is the equivalent of NCR's 5380. "Pseudo-DMA"
+ * architecture is used, where a PAL drives the DMA signals on the 5380
+ * allowing fast, blind transfers with proper handshaking.
+ */
+
+
+/* The Time-out Counter register is used to safe-guard against a stuck
+ * bus (in the case of RDY driven handshake) or a stuck byte (if 16-Bit
+ * DMA conversion is used). The counter uses a 28.224MHz clock
+ * divided by 14 as its clock source. In the case of a stuck byte in
+ * the holding register, an interrupt is generated (and mixed with the
+ * one with the drive) using the CD-ROM interrupt pointer.
+ */
+
+#define P_TIMEOUT_COUNTER_REG 0x4000
+#define P_TC_DISABLE 0x80 /* Set to 0 to enable timeout int. */
+ /* Bits D6-D0 contain timeout count */
+
+
+#define P_TIMEOUT_STATUS_REG_OFFSET 0x4001
+#define P_TS_TIM 0x80 /* check timeout status */
+ /* Bits D6-D4 N/U */
+#define P_TS_ARM_DRQ_INT 0x08 /* Arm DRQ Int. When set high,
+ * the next rising edge will
+ * cause a CD-ROM interrupt.
+ * When set low, the interrupt
+ * will be cleared. There is
+ * no status available for
+ * this interrupt.
+ */
+#define P_TS_ENABLE_TO_ERR_INTERRUPT /* Enable timeout error int. */
+#define P_TS_ENABLE_WAIT /* Enable Wait */
+
+#define P_TS_CT 0x01 /* clear timeout. Note: writing
+ * to this register clears the
+ * timeout error int. or status
+ */
+
+
+/*
+ * The data register reads/writes to/from the 5380 in pseudo-DMA mode
+ */
+
+#define P_DATA_REG_OFFSET 0x5c00 /* rw */
+
+#define P_STATUS_REG_OFFSET 0x5c01 /* ro */
+#define P_ST_RDY 0x80 /* 5380 DDRQ Status */
+
+#define P_IRQ_STATUS 0x5c03
+#define P_IS_IRQ 0x80 /* DIRQ status */
+
+#define PCB_CONFIG 0x803
+#define MASTER_ADDRESS_PTR 0x9a01 /* Fixed position - no relo */
+#define SYS_CONFIG_4 0x8003
+#define WAIT_STATE 0xbc00
+#define OPERATION_MODE_1 0xec03
+#define IO_CONFIG_3 0xf002
+
+
+#ifndef ASM
+int pas16_abort(Scsi_Cmnd *);
+int pas16_biosparam(Disk *, kdev_t, int*);
+int pas16_detect(Scsi_Host_Template *);
+int pas16_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int pas16_reset(Scsi_Cmnd *);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+/*
+ * I hadn't thought of this with the earlier drivers - but to prevent
+ * macro definition conflicts, we shouldn't define all of the internal
+ * macros when this is being used solely for the host stub.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define MV_PAS16 {NULL, NULL, NULL, NULL, \
+ "Pro Audio Spectrum-16 SCSI", \
+ pas16_detect, NULL, NULL, \
+ NULL, pas16_queue_command, pas16_abort, pas16_reset, NULL, \
+ pas16_biosparam, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+#ifndef HOSTS_C
+
+#define NCR5380_implementation_fields \
+ volatile unsigned short io_port
+
+#define NCR5380_local_declare() \
+ volatile unsigned short io_port
+
+#define NCR5380_setup(instance) \
+ io_port = (instance)->io_port
+
+#define PAS16_io_port(reg) ( io_port + pas16_offset[(reg)] )
+
+#if !(PDEBUG & PDEBUG_TRANSFER)
+#define NCR5380_read(reg) ( inb(PAS16_io_port(reg)) )
+#define NCR5380_write(reg, value) ( outb((value),PAS16_io_port(reg)) )
+#else
+#define NCR5380_read(reg) \
+ (((unsigned char) printk("scsi%d : read register %d at io_port %04x\n"\
+ , instance->hostno, (reg), PAS16_io_port(reg))), inb( PAS16_io_port(reg)) )
+
+#define NCR5380_write(reg, value) \
+ (printk("scsi%d : write %02x to register %d at io_port %04x\n", \
+ instance->hostno, (value), (reg), PAS16_io_port(reg)), \
+ outb( (value),PAS16_io_port(reg) ) )
+
+#endif
+
+
+#define NCR5380_intr pas16_intr
+#define NCR5380_queue_command pas16_queue_command
+#define NCR5380_abort pas16_abort
+#define NCR5380_reset pas16_reset
+
+/* 15 14 12 10 7 5 3
+ 1101 0100 1010 1000 */
+
+#define PAS16_IRQS 0xd4a8
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* PAS16_H */
diff --git a/i386/i386at/gpl/linux/scsi/qlogic.c b/i386/i386at/gpl/linux/scsi/qlogic.c
new file mode 100644
index 00000000..8333275b
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/qlogic.c
@@ -0,0 +1,678 @@
+/*----------------------------------------------------------------*/
+/*
+ Qlogic linux driver - work in progress. No Warranty express or implied.
+ Use at your own risk. Support Tort Reform so you won't have to read all
+ these silly disclaimers.
+
+ Copyright 1994, Tom Zerucha.
+ zerucha@shell.portal.com
+
+ Additional Code, and much appreciated help by
+ Michael A. Griffith
+ grif@cs.ucr.edu
+
+ Thanks to Eric Youngdale and Dave Hinds for loadable module and PCMCIA
+ help respectively, and for suffering through my foolishness during the
+ debugging process.
+
+ Reference Qlogic FAS408 Technical Manual, 53408-510-00A, May 10, 1994
+ (you can reference it, but it is incomplete and inaccurate in places)
+
+ Version 0.43 4/6/95 - kernel 1.2.0+, pcmcia 2.5.4+
+
+ Functions as standalone, loadable, and PCMCIA driver, the latter from
+ Dave Hind's PCMCIA package.
+
+ Redistributable under terms of the GNU Public License
+
+*/
+/*----------------------------------------------------------------*/
+/* Configuration */
+
+/* Set the following to 2 to use normal interrupt (active high/totempole-
+ tristate), otherwise use 0 (REQUIRED FOR PCMCIA) for active low, open
+ drain */
+#define QL_INT_ACTIVE_HIGH 2
+
+/* Set the following to 1 to enable the use of interrupts. Note that 0 tends
+ to be more stable, but slower (or ties up the system more) */
+#define QL_USE_IRQ 1
+
+/* Set the following to max out the speed of the PIO PseudoDMA transfers,
+ again, 0 tends to be slower, but more stable. */
+#define QL_TURBO_PDMA 1
+
+/* This should be 1 to enable parity detection */
+#define QL_ENABLE_PARITY 1
+
+/* This will reset all devices when the driver is initialized (during bootup).
+ The other linux drivers don't do this, but the DOS drivers do, and after
+ using DOS or some kind of crash or lockup this will bring things back
+ without requiring a cold boot. It does take some time to recover from a
+ reset, so it is slower, and I have seen timeouts so that devices weren't
+ recognized when this was set. */
+#define QL_RESET_AT_START 0
+
+/* crystal frequency in megahertz (for offset 5 and 9)
+ Please set this for your card. Most Qlogic cards are 40 Mhz. The
+ Control Concepts ISA (not VLB) is 24 Mhz */
+#define XTALFREQ 40
+
+/**********/
+/* DANGER! modify these at your own risk */
+/* SLOWCABLE can usually be reset to zero if you have a clean setup and
+ proper termination. The rest are for synchronous transfers and other
+ advanced features if your device can transfer faster than 5Mb/sec.
+ If you are really curious, email me for a quick howto until I have
+ something official */
+/**********/
+
+/*****/
+/* config register 1 (offset 8) options */
+/* This needs to be set to 1 if your cabling is long or noisy */
+#define SLOWCABLE 1
+
+/*****/
+/* offset 0xc */
+/* This will set fast (10Mhz) synchronous timing when set to 1
+ For this to have an effect, FASTCLK must also be 1 */
+#define FASTSCSI 0
+
+/* This when set to 1 will set a faster sync transfer rate */
+#define FASTCLK 0
+/*(XTALFREQ>25?1:0)*/
+
+/*****/
+/* offset 6 */
+/* This is the sync transfer divisor, XTALFREQ/X will be the maximum
+ achievable data rate (assuming the rest of the system is capable
+ and set properly) */
+#define SYNCXFRPD 5
+/*(XTALFREQ/5)*/
+
+/*****/
+/* offset 7 */
+/* This is the count of how many synchronous transfers can take place
+ i.e. how many reqs can occur before an ack is given.
+ The maximum value for this is 15, the upper bits can modify
+ REQ/ACK assertion and deassertion during synchronous transfers
+ If this is 0, the bus will only transfer asynchronously */
+#define SYNCOFFST 0
+/* for the curious, bits 7&6 control the deassertion delay in 1/2 cycles
+ of the 40Mhz clock. If FASTCLK is 1, specifying 01 (1/2) will
+ cause the deassertion to be early by 1/2 clock. Bits 5&4 control
+ the assertion delay, also in 1/2 clocks (FASTCLK is ignored here). */
+
+/*----------------------------------------------------------------*/
+#ifdef PCMCIA
+#undef QL_INT_ACTIVE_HIGH
+#define QL_INT_ACTIVE_HIGH 0
+#define MODULE
+#endif
+
+#include <linux/module.h>
+
+#ifdef PCMCIA
+#undef MODULE
+#endif
+
+#include <linux/blk.h> /* to get disk capacity */
+#include <linux/kernel.h>
+#include <linux/string.h>
+#include <linux/ioport.h>
+#include <linux/sched.h>
+#include <linux/proc_fs.h>
+#include <linux/unistd.h>
+#include <asm/io.h>
+#include <asm/irq.h>
+#include "sd.h"
+#include "hosts.h"
+#include "qlogic.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_qlogic = {
+ PROC_SCSI_QLOGIC, 6, "qlogic",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/*----------------------------------------------------------------*/
+/* driver state info, local to driver */
+static int qbase = 0; /* Port */
+static int qinitid; /* initiator ID */
+static int qabort; /* Flag to cause an abort */
+static int qlirq = -1; /* IRQ being used */
+static char qinfo[80]; /* description */
+static Scsi_Cmnd *qlcmd; /* current command being processed */
+
+static int qlcfg5 = ( XTALFREQ << 5 ); /* 15625/512 */
+static int qlcfg6 = SYNCXFRPD;
+static int qlcfg7 = SYNCOFFST;
+static int qlcfg8 = ( SLOWCABLE << 7 ) | ( QL_ENABLE_PARITY << 4 );
+static int qlcfg9 = ( ( XTALFREQ + 4 ) / 5 );
+static int qlcfgc = ( FASTCLK << 3 ) | ( FASTSCSI << 4 );
+
+/*----------------------------------------------------------------*/
+/* The qlogic card uses two register maps - These macros select which one */
+#define REG0 ( outb( inb( qbase + 0xd ) & 0x7f , qbase + 0xd ), outb( 4 , qbase + 0xd ))
+#define REG1 ( outb( inb( qbase + 0xd ) | 0x80 , qbase + 0xd ), outb( 0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd ))
+
+/* following is watchdog timeout in microseconds */
+#define WATCHDOG 5000000
+
+/*----------------------------------------------------------------*/
+/* the following will set the monitor border color (useful to find
+ where something crashed or gets stuck at and as a simple profiler) */
+
+#if 0
+#define rtrc(i) {inb(0x3da);outb(0x31,0x3c0);outb((i),0x3c0);}
+#else
+#define rtrc(i) {}
+#endif
+
+/*----------------------------------------------------------------*/
+/* local functions */
+/*----------------------------------------------------------------*/
+static void ql_zap(void);
+/* error recovery - reset everything */
+void ql_zap()
+{
+int x;
+unsigned long flags;
+ save_flags( flags );
+ cli();
+ x = inb(qbase + 0xd);
+ REG0;
+ outb(3, qbase + 3); /* reset SCSI */
+ outb(2, qbase + 3); /* reset chip */
+ if (x & 0x80)
+ REG1;
+ restore_flags( flags );
+}
+
+/*----------------------------------------------------------------*/
+/* do pseudo-dma */
+static int ql_pdma(int phase, char *request, int reqlen)
+{
+int j;
+ j = 0;
+ if (phase & 1) { /* in */
+#if QL_TURBO_PDMA
+rtrc(4)
+ /* empty fifo in large chunks */
+ if( reqlen >= 128 && (inb( qbase + 8 ) & 2) ) { /* full */
+ insl( qbase + 4, request, 32 );
+ reqlen -= 128;
+ request += 128;
+ }
+ while( reqlen >= 84 && !( j & 0xc0 ) ) /* 2/3 */
+ if( (j=inb( qbase + 8 )) & 4 ) {
+ insl( qbase + 4, request, 21 );
+ reqlen -= 84;
+ request += 84;
+ }
+ if( reqlen >= 44 && (inb( qbase + 8 ) & 8) ) { /* 1/3 */
+ insl( qbase + 4, request, 11 );
+ reqlen -= 44;
+ request += 44;
+ }
+#endif
+ /* until both empty and int (or until reclen is 0) */
+rtrc(7)
+ j = 0;
+ while( reqlen && !( (j & 0x10) && (j & 0xc0) ) ) {
+ /* while bytes to receive and not empty */
+ j &= 0xc0;
+ while ( reqlen && !( (j=inb(qbase + 8)) & 0x10 ) ) {
+ *request++ = inb(qbase + 4);
+ reqlen--;
+ }
+ if( j & 0x10 )
+ j = inb(qbase+8);
+
+ }
+ }
+ else { /* out */
+#if QL_TURBO_PDMA
+rtrc(4)
+ if( reqlen >= 128 && inb( qbase + 8 ) & 0x10 ) { /* empty */
+ outsl(qbase + 4, request, 32 );
+ reqlen -= 128;
+ request += 128;
+ }
+ while( reqlen >= 84 && !( j & 0xc0 ) ) /* 1/3 */
+ if( !((j=inb( qbase + 8 )) & 8) ) {
+ outsl( qbase + 4, request, 21 );
+ reqlen -= 84;
+ request += 84;
+ }
+ if( reqlen >= 40 && !(inb( qbase + 8 ) & 4 ) ) { /* 2/3 */
+ outsl( qbase + 4, request, 10 );
+ reqlen -= 40;
+ request += 40;
+ }
+#endif
+ /* until full and int (or until reclen is 0) */
+rtrc(7)
+ j = 0;
+ while( reqlen && !( (j & 2) && (j & 0xc0) ) ) {
+ /* while bytes to send and not full */
+ while ( reqlen && !( (j=inb(qbase + 8)) & 2 ) ) {
+ outb(*request++, qbase + 4);
+ reqlen--;
+ }
+ if( j & 2 )
+ j = inb(qbase+8);
+ }
+ }
+/* maybe return reqlen */
+ return inb( qbase + 8 ) & 0xc0;
+}
+
+/*----------------------------------------------------------------*/
+/* wait for interrupt flag (polled - not real hardware interrupt) */
+static int ql_wai(void)
+{
+int i,k;
+ k = 0;
+ i = jiffies + WATCHDOG;
+ while ( i > jiffies && !qabort && !((k = inb(qbase + 4)) & 0xe0))
+ barrier();
+ if (i <= jiffies)
+ return (DID_TIME_OUT);
+ if (qabort)
+ return (qabort == 1 ? DID_ABORT : DID_RESET);
+ if (k & 0x60)
+ ql_zap();
+ if (k & 0x20)
+ return (DID_PARITY);
+ if (k & 0x40)
+ return (DID_ERROR);
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* initiate scsi command - queueing handler */
+static void ql_icmd(Scsi_Cmnd * cmd)
+{
+unsigned int i;
+unsigned long flags;
+
+ qabort = 0;
+
+ save_flags( flags );
+ cli();
+ REG0;
+/* clearing of interrupts and the fifo is needed */
+ inb(qbase + 5); /* clear interrupts */
+ if (inb(qbase + 5)) /* if still interrupting */
+ outb(2, qbase + 3); /* reset chip */
+ else if (inb(qbase + 7) & 0x1f)
+ outb(1, qbase + 3); /* clear fifo */
+ while (inb(qbase + 5)); /* clear ints */
+ REG1;
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ outb(0, qbase + 0xb); /* disable ints */
+ inb(qbase + 8); /* clear int bits */
+ REG0;
+ outb(0x40, qbase + 0xb); /* enable features */
+
+/* configurables */
+ outb( qlcfgc , qbase + 0xc);
+/* config: no reset interrupt, (initiator) bus id */
+ outb( 0x40 | qlcfg8 | qinitid, qbase + 8);
+ outb( qlcfg7 , qbase + 7 );
+ outb( qlcfg6 , qbase + 6 );
+/**/
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9 & 7, qbase + 9); /* prescaler */
+/* outb(0x99, qbase + 5); */
+ outb(cmd->target, qbase + 4);
+
+ for (i = 0; i < cmd->cmd_len; i++)
+ outb(cmd->cmnd[i], qbase + 2);
+ qlcmd = cmd;
+ outb(0x41, qbase + 3); /* select and send command */
+ restore_flags( flags );
+}
+/*----------------------------------------------------------------*/
+/* process scsi command - usually after interrupt */
+static unsigned int ql_pcmd(Scsi_Cmnd * cmd)
+{
+unsigned int i, j, k;
+unsigned int result; /* ultimate return result */
+unsigned int status; /* scsi returned status */
+unsigned int message; /* scsi returned message */
+unsigned int phase; /* recorded scsi phase */
+unsigned int reqlen; /* total length of transfer */
+struct scatterlist *sglist; /* scatter-gather list pointer */
+unsigned int sgcount; /* sg counter */
+
+rtrc(1)
+ j = inb(qbase + 6);
+ i = inb(qbase + 5);
+ if (i == 0x20) {
+ return (DID_NO_CONNECT << 16);
+ }
+ i |= inb(qbase + 5); /* the 0x10 bit can be set after the 0x08 */
+ if (i != 0x18) {
+ printk("Ql:Bad Interrupt status:%02x\n", i);
+ ql_zap();
+ return (DID_BAD_INTR << 16);
+ }
+ j &= 7; /* j = inb( qbase + 7 ) >> 5; */
+/* correct status is supposed to be step 4 */
+/* it sometimes returns step 3 but with 0 bytes left to send */
+/* We can try stuffing the FIFO with the max each time, but we will get a
+ sequence of 3 if any bytes are left (but we do flush the FIFO anyway */
+ if(j != 3 && j != 4) {
+ printk("Ql:Bad sequence for command %d, int %02X, cmdleft = %d\n", j, i, inb( qbase+7 ) & 0x1f );
+ ql_zap();
+ return (DID_ERROR << 16);
+ }
+ result = DID_OK;
+ if (inb(qbase + 7) & 0x1f) /* if some bytes in fifo */
+ outb(1, qbase + 3); /* clear fifo */
+/* note that request_bufflen is the total xfer size when sg is used */
+ reqlen = cmd->request_bufflen;
+/* note that it won't work if transfers > 16M are requested */
+ if (reqlen && !((phase = inb(qbase + 4)) & 6)) { /* data phase */
+rtrc(2)
+ outb(reqlen, qbase); /* low-mid xfer cnt */
+ outb(reqlen >> 8, qbase+1); /* low-mid xfer cnt */
+ outb(reqlen >> 16, qbase + 0xe); /* high xfer cnt */
+ outb(0x90, qbase + 3); /* command do xfer */
+/* PIO pseudo DMA to buffer or sglist */
+ REG1;
+ if (!cmd->use_sg)
+ ql_pdma(phase, cmd->request_buffer, cmd->request_bufflen);
+ else {
+ sgcount = cmd->use_sg;
+ sglist = cmd->request_buffer;
+ while (sgcount--) {
+ if (qabort) {
+ REG0;
+ return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+ }
+ if (ql_pdma(phase, sglist->address, sglist->length))
+ break;
+ sglist++;
+ }
+ }
+ REG0;
+rtrc(2)
+/* wait for irq (split into second state of irq handler if this can take time) */
+ if ((k = ql_wai()))
+ return (k << 16);
+ k = inb(qbase + 5); /* should be 0x10, bus service */
+ }
+/*** Enter Status (and Message In) Phase ***/
+ k = jiffies + WATCHDOG;
+ while ( k > jiffies && !qabort && !(inb(qbase + 4) & 6)); /* wait for status phase */
+ if ( k <= jiffies ) {
+ ql_zap();
+ return (DID_TIME_OUT << 16);
+ }
+ while (inb(qbase + 5)); /* clear pending ints */
+ if (qabort)
+ return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+ outb(0x11, qbase + 3); /* get status and message */
+ if ((k = ql_wai()))
+ return (k << 16);
+ i = inb(qbase + 5); /* get chip irq stat */
+ j = inb(qbase + 7) & 0x1f; /* and bytes rec'd */
+ status = inb(qbase + 2);
+ message = inb(qbase + 2);
+/* should get function complete int if Status and message, else bus serv if only status */
+ if (!((i == 8 && j == 2) || (i == 0x10 && j == 1))) {
+ printk("Ql:Error during status phase, int=%02X, %d bytes recd\n", i, j);
+ result = DID_ERROR;
+ }
+ outb(0x12, qbase + 3); /* done, disconnect */
+rtrc(1)
+ if ((k = ql_wai()))
+ return (k << 16);
+/* should get bus service interrupt and disconnect interrupt */
+ i = inb(qbase + 5); /* should be bus service */
+ while (!qabort && ((i & 0x20) != 0x20)) {
+ barrier();
+ i |= inb(qbase + 5);
+ }
+rtrc(0)
+ if (qabort)
+ return ((qabort == 1 ? DID_ABORT : DID_RESET) << 16);
+ return (result << 16) | (message << 8) | (status & STATUS_MASK);
+}
+
+#if QL_USE_IRQ
+/*----------------------------------------------------------------*/
+/* interrupt handler */
+static void ql_ihandl(int irq, struct pt_regs * regs)
+{
+Scsi_Cmnd *icmd;
+ REG0;
+ if (!(inb(qbase + 4) & 0x80)) /* false alarm? */
+ return;
+ if (qlcmd == NULL) { /* no command to process? */
+ int i;
+ i = 16;
+ while (i-- && inb(qbase + 5)); /* maybe also ql_zap() */
+ return;
+ }
+ icmd = qlcmd;
+ icmd->result = ql_pcmd(icmd);
+ qlcmd = NULL;
+/* if result is CHECK CONDITION done calls qcommand to request sense */
+ (icmd->scsi_done) (icmd);
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* global functions */
+/*----------------------------------------------------------------*/
+/* non queued command */
+#if QL_USE_IRQ
+static void qlidone(Scsi_Cmnd * cmd) {}; /* null function */
+#endif
+
+/* command process */
+int qlogic_command(Scsi_Cmnd * cmd)
+{
+int k;
+#if QL_USE_IRQ
+ if (qlirq >= 0) {
+ qlogic_queuecommand(cmd, qlidone);
+ while (qlcmd != NULL);
+ return cmd->result;
+ }
+#endif
+/* non-irq version */
+ if (cmd->target == qinitid)
+ return (DID_BAD_TARGET << 16);
+ ql_icmd(cmd);
+ if ((k = ql_wai()))
+ return (k << 16);
+ return ql_pcmd(cmd);
+
+}
+
+#if QL_USE_IRQ
+/*----------------------------------------------------------------*/
+/* queued command */
+int qlogic_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ if(cmd->target == qinitid) {
+ cmd->result = DID_BAD_TARGET << 16;
+ done(cmd);
+ return 0;
+ }
+
+ cmd->scsi_done = done;
+/* wait for the last command's interrupt to finish */
+ while (qlcmd != NULL)
+ barrier();
+ ql_icmd(cmd);
+ return 0;
+}
+#else
+int qlogic_queuecommand(Scsi_Cmnd * cmd, void (*done) (Scsi_Cmnd *))
+{
+ return 1;
+}
+#endif
+
+#ifdef PCMCIA
+/*----------------------------------------------------------------*/
+/* allow PCMCIA code to preset the port */
+/* port should be 0 and irq to -1 respectively for autoprobing */
+void qlogic_preset(int port, int irq)
+{
+ qbase=port;
+ qlirq=irq;
+}
+#endif
+
+/*----------------------------------------------------------------*/
+/* look for qlogic card and init if found */
+int qlogic_detect(Scsi_Host_Template * host)
+{
+int i, j; /* these are only used by IRQ detect */
+int qltyp; /* type of chip */
+struct Scsi_Host *hreg; /* registered host structure */
+unsigned long flags;
+
+host->proc_dir = &proc_scsi_qlogic;
+
+/* Qlogic Cards only exist at 0x230 or 0x330 (the chip itself decodes the
+ address - I check 230 first since MIDI cards are typically at 330
+
+ Theoretically, two Qlogic cards can coexist in the same system. This
+ should work by simply using this as a loadable module for the second
+ card, but I haven't tested this.
+*/
+
+ if( !qbase ) {
+ for (qbase = 0x230; qbase < 0x430; qbase += 0x100) {
+ if( check_region( qbase , 0x10 ) )
+ continue;
+ REG1;
+ if ( ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 )
+ && ( (inb(qbase + 0xe) ^ inb(qbase + 0xe)) == 7 ) )
+ break;
+ }
+ if (qbase == 0x430)
+ return 0;
+ }
+ else
+ printk( "Ql: Using preset base address of %03x\n", qbase );
+
+ qltyp = inb(qbase + 0xe) & 0xf8;
+ qinitid = host->this_id;
+ if (qinitid < 0)
+ qinitid = 7; /* if no ID, use 7 */
+ outb(1, qbase + 8); /* set for PIO pseudo DMA */
+ REG0;
+ outb(0x40 | qlcfg8 | qinitid, qbase + 8); /* (ini) bus id, disable scsi rst */
+ outb(qlcfg5, qbase + 5); /* select timer */
+ outb(qlcfg9, qbase + 9); /* prescaler */
+#if QL_RESET_AT_START
+ outb( 3 , qbase + 3 );
+ REG1;
+ while( inb( qbase + 0xf ) & 4 );
+ REG0;
+#endif
+#if QL_USE_IRQ
+/* IRQ probe - toggle pin and check request pending */
+
+ if( qlirq == -1 ) {
+ save_flags( flags );
+ cli();
+ i = 0xffff;
+ j = 3;
+ outb(0x90, qbase + 3); /* illegal command - cause interrupt */
+ REG1;
+ outb(10, 0x20); /* access pending interrupt map */
+ outb(10, 0xa0);
+ while (j--) {
+ outb(0xb0 | QL_INT_ACTIVE_HIGH , qbase + 0xd); /* int pin off */
+ i &= ~(inb(0x20) | (inb(0xa0) << 8)); /* find IRQ off */
+ outb(0xb4 | QL_INT_ACTIVE_HIGH , qbase + 0xd); /* int pin on */
+ i &= inb(0x20) | (inb(0xa0) << 8); /* find IRQ on */
+ }
+ REG0;
+ while (inb(qbase + 5)); /* purge int */
+ j = -1;
+ while (i) /* find on bit */
+ i >>= 1, j++; /* should check for exactly 1 on */
+ qlirq = j;
+ restore_flags( flags );
+ }
+ else
+ printk( "Ql: Using preset IRQ %d\n", qlirq );
+
+ if (qlirq >= 0 && !request_irq(qlirq, ql_ihandl, 0, "qlogic"))
+ host->can_queue = 1;
+#endif
+ request_region( qbase , 0x10 ,"qlogic");
+ hreg = scsi_register( host , 0 ); /* no host data */
+ hreg->io_port = qbase;
+ hreg->n_io_port = 16;
+ hreg->dma_channel = -1;
+ if( qlirq != -1 )
+ hreg->irq = qlirq;
+
+ sprintf(qinfo, "Qlogic Driver version 0.43, chip %02X at %03X, IRQ %d, TPdma:%d",
+ qltyp, qbase, qlirq, QL_TURBO_PDMA );
+ host->name = qinfo;
+
+ return 1;
+}
+
+/*----------------------------------------------------------------*/
+/* return bios parameters */
+int qlogic_biosparam(Disk * disk, kdev_t dev, int ip[])
+{
+/* This should mimic the DOS Qlogic driver's behavior exactly */
+ ip[0] = 0x40;
+ ip[1] = 0x20;
+ ip[2] = disk->capacity / (ip[0] * ip[1]);
+ if (ip[2] > 1024) {
+ ip[0] = 0xff;
+ ip[1] = 0x3f;
+ ip[2] = disk->capacity / (ip[0] * ip[1]);
+ if (ip[2] > 1023)
+ ip[2] = 1023;
+ }
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* abort command in progress */
+int qlogic_abort(Scsi_Cmnd * cmd)
+{
+ qabort = 1;
+ ql_zap();
+ return 0;
+}
+
+/*----------------------------------------------------------------*/
+/* reset SCSI bus */
+int qlogic_reset(Scsi_Cmnd * cmd)
+{
+ qabort = 2;
+ ql_zap();
+ return 1;
+}
+
+/*----------------------------------------------------------------*/
+/* return info string */
+const char *qlogic_info(struct Scsi_Host * host)
+{
+ return qinfo;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = QLOGIC;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/qlogic.h b/i386/i386at/gpl/linux/scsi/qlogic.h
new file mode 100644
index 00000000..0ff119ae
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/qlogic.h
@@ -0,0 +1,40 @@
+#ifndef _QLOGIC_H
+#define _QLOGIC_H
+
+int qlogic_detect(Scsi_Host_Template * );
+const char * qlogic_info(struct Scsi_Host *);
+int qlogic_command(Scsi_Cmnd *);
+int qlogic_queuecommand(Scsi_Cmnd *, void (* done)(Scsi_Cmnd *));
+int qlogic_abort(Scsi_Cmnd *);
+int qlogic_reset(Scsi_Cmnd *);
+int qlogic_biosparam(Disk *, kdev_t, int[]);
+
+#ifndef NULL
+#define NULL (0)
+#endif
+
+#define QLOGIC { \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ NULL, \
+ qlogic_detect, \
+ NULL, \
+ qlogic_info, \
+ qlogic_command, \
+ qlogic_queuecommand, \
+ qlogic_abort, \
+ qlogic_reset, \
+ NULL, \
+ qlogic_biosparam, \
+ 0, \
+ -1, \
+ SG_ALL, \
+ 1, \
+ 0, \
+ 0, \
+ DISABLE_CLUSTERING \
+}
+
+#endif /* _QLOGIC_H */
diff --git a/i386/i386at/gpl/linux/scsi/scsi.c b/i386/i386at/gpl/linux/scsi/scsi.c
new file mode 100644
index 00000000..85d234e1
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi.c
@@ -0,0 +1,3204 @@
+/*
+ * scsi.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * generic mid-level SCSI driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Bug correction thanks go to :
+ * Rik Faith <faith@cs.unc.edu>
+ * Tommy Thorn <tthorn>
+ * Thomas Wuensche <tw@fgb1.fgb.mw.tu-muenchen.de>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Native multichannel and wide scsi support added
+ * by Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#include <linux/module.h>
+
+#include <asm/system.h>
+#include <linux/sched.h>
+#include <linux/timer.h>
+#include <linux/string.h>
+#include <linux/malloc.h>
+#include <asm/irq.h>
+#include <asm/dma.h>
+#include <linux/ioport.h>
+#include <linux/kernel.h>
+#include<linux/stat.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "constants.h"
+
+#include <linux/config.h>
+
+#undef USE_STATIC_SCSI_MEMORY
+
+/*
+static const char RCSid[] = "$Header: cvs/gnumach/i386/i386at/gpl/linux/scsi/Attic/scsi.c,v 1.1.1.1 1997/02/25 21:27:50 thomas Exp $";
+*/
+
+
+/* Command groups 3 and 4 are reserved and should never be used. */
+const unsigned char scsi_command_size[8] = { 6, 10, 10, 12, 12, 12, 10, 10 };
+
+#define INTERNAL_ERROR (panic ("Internal error in file %s, line %d.\n", __FILE__, __LINE__))
+
+/*
+ * PAGE_SIZE must be a multiple of the sector size (512). True
+ * for all reasonably recent architectures (even the VAX...).
+ */
+#define SECTOR_SIZE 512
+#define SECTORS_PER_PAGE (PAGE_SIZE/SECTOR_SIZE)
+
+#if SECTORS_PER_PAGE <= 8
+ typedef unsigned char FreeSectorBitmap;
+#elif SECTORS_PER_PAGE <= 32
+ typedef unsigned int FreeSectorBitmap;
+#else
+# error You lose.
+#endif
+
+static void scsi_done (Scsi_Cmnd *SCpnt);
+static int update_timeout (Scsi_Cmnd *, int);
+static void print_inquiry(unsigned char *data);
+static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid);
+static int scan_scsis_single (int channel,int dev,int lun,int * max_scsi_dev ,
+ Scsi_Device ** SDpnt, Scsi_Cmnd * SCpnt,
+ struct Scsi_Host *shpnt, char * scsi_result);
+void scsi_build_commandblocks(Scsi_Device * SDpnt);
+
+#ifdef CONFIG_MODULES
+extern struct symbol_table scsi_symbol_table;
+#endif
+
+static FreeSectorBitmap * dma_malloc_freelist = NULL;
+static int scsi_need_isa_bounce_buffers;
+static unsigned int dma_sectors = 0;
+unsigned int dma_free_sectors = 0;
+unsigned int need_isa_buffer = 0;
+static unsigned char ** dma_malloc_pages = NULL;
+
+static int time_start;
+static int time_elapsed;
+static volatile struct Scsi_Host * host_active = NULL;
+#define SCSI_BLOCK(HOST) ((HOST->block && host_active && HOST != host_active) \
+ || (HOST->can_queue && HOST->host_busy >= HOST->can_queue))
+
+#define MAX_SCSI_DEVICE_CODE 10
+const char *const scsi_device_types[MAX_SCSI_DEVICE_CODE] =
+{
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications "
+};
+
+
+/*
+ * global variables :
+ * scsi_devices an array of these specifying the address for each
+ * (host, id, LUN)
+ */
+
+Scsi_Device * scsi_devices = NULL;
+
+/* Process ID of SCSI commands */
+unsigned long scsi_pid = 0;
+
+static unsigned char generic_sense[6] = {REQUEST_SENSE, 0,0,0, 255, 0};
+static void resize_dma_pool(void);
+
+/* This variable is merely a hook so that we can debug the kernel with gdb. */
+Scsi_Cmnd * last_cmnd = NULL;
+
+/* This is the pointer to the /proc/scsi code.
+ * It is only initialized to !=0 if the scsi code is present
+ */
+extern int (* dispatch_scsi_info_ptr)(int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+extern int dispatch_scsi_info(int ino, char *buffer, char **start,
+ off_t offset, int length, int inout);
+
+struct proc_dir_entry proc_scsi_scsi = {
+ PROC_SCSI_SCSI, 4, "scsi",
+ S_IFREG | S_IRUGO | S_IWUSR, 2, 0, 0, 0,
+ NULL,
+ NULL, NULL,
+ NULL, NULL, NULL
+};
+
+
+/*
+ * As the scsi do command functions are intelligent, and may need to
+ * redo a command, we need to keep track of the last command
+ * executed on each one.
+ */
+
+#define WAS_RESET 0x01
+#define WAS_TIMEDOUT 0x02
+#define WAS_SENSE 0x04
+#define IS_RESETTING 0x08
+#define IS_ABORTING 0x10
+#define ASKED_FOR_SENSE 0x20
+
+/*
+ * This is the number of clock ticks we should wait before we time out
+ * and abort the command. This is for where the scsi.c module generates
+ * the command, not where it originates from a higher level, in which
+ * case the timeout is specified there.
+ *
+ * ABORT_TIMEOUT and RESET_TIMEOUT are the timeouts for RESET and ABORT
+ * respectively.
+ */
+
+#ifdef DEBUG_TIMEOUT
+static void scsi_dump_status(void);
+#endif
+
+
+#ifdef DEBUG
+ #define SCSI_TIMEOUT (5*HZ)
+#else
+ #define SCSI_TIMEOUT (1*HZ)
+#endif
+
+#ifdef DEBUG
+ #define SENSE_TIMEOUT SCSI_TIMEOUT
+ #define ABORT_TIMEOUT SCSI_TIMEOUT
+ #define RESET_TIMEOUT SCSI_TIMEOUT
+#else
+ #define SENSE_TIMEOUT (5*HZ/10)
+ #define RESET_TIMEOUT (5*HZ/10)
+ #define ABORT_TIMEOUT (5*HZ/10)
+#endif
+
+#define MIN_RESET_DELAY (1*HZ)
+
+/* Do not call reset on error if we just did a reset within 10 sec. */
+#define MIN_RESET_PERIOD (10*HZ)
+
+/* The following devices are known not to tolerate a lun != 0 scan for
+ * one reason or another. Some will respond to all luns, others will
+ * lock up.
+ */
+
+#define BLIST_NOLUN 0x01
+#define BLIST_FORCELUN 0x02
+#define BLIST_BORKEN 0x04
+#define BLIST_KEY 0x08
+#define BLIST_SINGLELUN 0x10
+
+struct dev_info{
+ const char * vendor;
+ const char * model;
+ const char * revision; /* Latest revision known to be bad. Not used yet */
+ unsigned flags;
+};
+
+/*
+ * This is what was previously known as the blacklist. The concept
+ * has been expanded so that we can specify other types of things we
+ * need to be aware of.
+ */
+static struct dev_info device_list[] =
+{
+{"CHINON","CD-ROM CDS-431","H42", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"CHINON","CD-ROM CDS-535","Q14", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"DENON","DRD-25X","V", BLIST_NOLUN}, /* Locks up if probed for lun != 0 */
+{"HITACHI","DK312C","CM81", BLIST_NOLUN}, /* Responds to all lun - dtg */
+{"HITACHI","DK314C","CR21" , BLIST_NOLUN}, /* responds to all lun */
+{"IMS", "CDD521/10","2.06", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-3280","PR02", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","XT-4380S","B3C", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"MAXTOR","MXT-1240S","I1.2", BLIST_NOLUN}, /* Locks up when LUN>0 polled */
+{"MAXTOR","XT-4170S","B5A", BLIST_NOLUN}, /* Locks-up sometimes when LUN>0 polled. */
+{"MAXTOR","XT-8760S","B7B", BLIST_NOLUN}, /* guess what? */
+{"MEDIAVIS","RENO CD-ROMX2A","2.03",BLIST_NOLUN},/*Responds to all lun */
+{"NEC","CD-ROM DRIVE:841","1.0", BLIST_NOLUN}, /* Locks-up when LUN>0 polled. */
+{"RODIME","RO3000S","2.33", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"SEAGATE", "ST157N", "\004|j", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for aha152x controller, which causes
+ * SCSI code to reset bus.*/
+{"SEAGATE", "ST296","921", BLIST_NOLUN}, /* Responds to all lun */
+{"SEAGATE","ST1581","6538",BLIST_NOLUN}, /* Responds to all lun */
+{"SONY","CD-ROM CDU-541","4.3d", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-55S","1.0i", BLIST_NOLUN},
+{"SONY","CD-ROM CDU-561","1.7x", BLIST_NOLUN},
+{"TANDBERG","TDC 3600","U07", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"TEAC","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+{"TEXEL","CD-ROM","1.06", BLIST_NOLUN}, /* causes failed REQUEST SENSE on lun 1
+ * for seagate controller, which causes
+ * SCSI code to reset bus.*/
+{"QUANTUM","LPS525S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+{"QUANTUM","PD1225S","3110", BLIST_NOLUN}, /* Locks sometimes if polled for lun != 0 */
+{"MEDIAVIS","CDR-H93MV","1.31", BLIST_NOLUN}, /* Locks up if polled for lun != 0 */
+{"SANKYO", "CP525","6.64", BLIST_NOLUN}, /* causes failed REQ SENSE, extra reset */
+{"HP", "C1750A", "3226", BLIST_NOLUN}, /* scanjet iic */
+{"HP", "C1790A", "", BLIST_NOLUN}, /* scanjet iip */
+{"HP", "C2500A", "", BLIST_NOLUN}, /* scanjet iicx */
+
+/*
+ * Other types of devices that have special flags.
+ */
+{"SONY","CD-ROM CDU-8001","*", BLIST_BORKEN},
+{"TEXEL","CD-ROM","1.06", BLIST_BORKEN},
+{"INSITE","Floptical F*8I","*", BLIST_KEY},
+{"INSITE","I325VM","*", BLIST_KEY},
+{"PIONEER","CD-ROMDRM-602X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+{"PIONEER","CD-ROMDRM-604X","*", BLIST_FORCELUN | BLIST_SINGLELUN},
+/*
+ * Must be at end of list...
+ */
+{NULL, NULL, NULL}
+};
+
+static int get_device_flags(unsigned char * response_data){
+ int i = 0;
+ unsigned char * pnt;
+ for(i=0; 1; i++){
+ if(device_list[i].vendor == NULL) return 0;
+ pnt = &response_data[8];
+ while(*pnt && *pnt == ' ') pnt++;
+ if(memcmp(device_list[i].vendor, pnt,
+ strlen(device_list[i].vendor))) continue;
+ pnt = &response_data[16];
+ while(*pnt && *pnt == ' ') pnt++;
+ if(memcmp(device_list[i].model, pnt,
+ strlen(device_list[i].model))) continue;
+ return device_list[i].flags;
+ }
+ return 0;
+}
+
+void scsi_make_blocked_list(void) {
+ int block_count = 0, index;
+ unsigned int flags;
+ struct Scsi_Host * sh[128], * shpnt;
+
+ /*
+ * Create a circular linked list from the scsi hosts which have
+ * the "wish_block" field in the Scsi_Host structure set.
+ * The blocked list should include all the scsi hosts using ISA DMA.
+ * In some systems, using two dma channels simultaneously causes
+ * unpredictable results.
+ * Among the scsi hosts in the blocked list, only one host at a time
+ * is allowed to have active commands queued. The transition from
+ * one active host to the next one is allowed only when host_busy == 0
+ * for the active host (which implies host_busy == 0 for all the hosts
+ * in the list). Moreover for block devices the transition to a new
+ * active host is allowed only when a request is completed, since a
+ * block device request can be divided into multiple scsi commands
+ * (when there are few sg lists or clustering is disabled).
+ *
+ * (DB, 4 Feb 1995)
+ */
+
+ save_flags(flags);
+ cli();
+ host_active = NULL;
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next) {
+
+#if 0
+ /*
+ * Is this is a candidate for the blocked list?
+ * Useful to put into the blocked list all the hosts whose driver
+ * does not know about the host->block feature.
+ */
+ if (shpnt->unchecked_isa_dma) shpnt->wish_block = 1;
+#endif
+
+ if (shpnt->wish_block) sh[block_count++] = shpnt;
+ }
+
+ if (block_count == 1) sh[0]->block = NULL;
+
+ else if (block_count > 1) {
+
+ for(index = 0; index < block_count - 1; index++) {
+ sh[index]->block = sh[index + 1];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ sh[block_count - 1]->block = sh[0];
+ printk("scsi%d : added to blocked host list.\n",
+ sh[index]->host_no);
+ }
+
+ restore_flags(flags);
+}
+
+static void scan_scsis_done (Scsi_Cmnd * SCpnt)
+{
+
+#ifdef DEBUG
+ printk ("scan_scsis_done(%p, %06x)\n", SCpnt->host, SCpnt->result);
+#endif
+ SCpnt->request.rq_status = RQ_SCSI_DONE;
+
+ if (SCpnt->request.sem != NULL)
+ up(SCpnt->request.sem);
+}
+
+#ifdef CONFIG_SCSI_MULTI_LUN
+static int max_scsi_luns = 8;
+#else
+static int max_scsi_luns = 1;
+#endif
+
+void scsi_luns_setup(char *str, int *ints) {
+ if (ints[0] != 1)
+ printk("scsi_luns_setup : usage max_scsi_luns=n (n should be between 1 and 8)\n");
+ else
+ max_scsi_luns = ints[1];
+}
+
+/*
+ * Detecting SCSI devices :
+ * We scan all present host adapter's busses, from ID 0 to ID (max_id).
+ * We use the INQUIRY command, determine device type, and pass the ID /
+ * lun address of all sequential devices to the tape driver, all random
+ * devices to the disk driver.
+ */
+static void scan_scsis (struct Scsi_Host *shpnt, unchar hardcoded,
+ unchar hchannel, unchar hid, unchar hlun)
+{
+ int dev, lun, channel;
+ unsigned char scsi_result0[256];
+ unsigned char *scsi_result;
+ Scsi_Device *SDpnt;
+ int max_dev_lun;
+ Scsi_Cmnd *SCpnt;
+
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc (sizeof (Scsi_Cmnd), GFP_ATOMIC | GFP_DMA);
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ memset (SCpnt, 0, sizeof (Scsi_Cmnd));
+
+
+ /* Make sure we have something that is valid for DMA purposes */
+ scsi_result = ( ( !shpnt->unchecked_isa_dma )
+ ? &scsi_result0[0] : scsi_init_malloc (512, GFP_DMA));
+
+ if (scsi_result == NULL) {
+ printk ("Unable to obtain scsi_result buffer\n");
+ goto leave;
+ }
+
+ /* We must chain ourself in the host_queue, so commands can time out */
+ if(shpnt->host_queue)
+ shpnt->host_queue->prev = SCpnt;
+ SCpnt->next = shpnt->host_queue;
+ SCpnt->prev = NULL;
+ shpnt->host_queue = SCpnt;
+
+
+ if (hardcoded == 1) {
+ Scsi_Device *oldSDpnt=SDpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ channel = hchannel;
+ if(channel > shpnt->max_channel) goto leave;
+ dev = hid;
+ if(dev >= shpnt->max_id) goto leave;
+ lun = hlun;
+ if(lun >= shpnt->max_lun) goto leave;
+ scan_scsis_single (channel, dev, lun, &max_dev_lun,
+ &SDpnt, SCpnt, shpnt, scsi_result);
+ if(SDpnt!=oldSDpnt) {
+
+ /* it could happen the blockdevice hasn't yet been inited */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ oldSDpnt->scsi_request_fn = NULL;
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) {
+ (*sdtpnt->attach)(oldSDpnt);
+ if(oldSDpnt->attached) scsi_build_commandblocks(oldSDpnt);}
+ resize_dma_pool();
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next) {
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ {(*sdtpnt->finish)();}
+ }
+ }
+
+ }
+ else {
+ for (channel = 0; channel <= shpnt->max_channel; channel++) {
+ for (dev = 0; dev < shpnt->max_id; ++dev) {
+ if (shpnt->this_id != dev) {
+
+ /*
+ * We need the for so our continue, etc. work fine. We put this in
+ * a variable so that we can override it during the scan if we
+ * detect a device *KNOWN* to have multiple logical units.
+ */
+ max_dev_lun = (max_scsi_luns < shpnt->max_lun ?
+ max_scsi_luns : shpnt->max_lun);
+ for (lun = 0; lun < max_dev_lun; ++lun) {
+ if (!scan_scsis_single (channel, dev, lun, &max_dev_lun,
+ &SDpnt, SCpnt, shpnt, scsi_result))
+ break; /* break means don't probe further for luns!=0 */
+ } /* for lun ends */
+ } /* if this_id != id ends */
+ } /* for dev ends */
+ } /* for channel ends */
+ } /* if/else hardcoded */
+
+ leave:
+
+ {/* Unchain SCpnt from host_queue */
+ Scsi_Cmnd *prev,*next,*hqptr;
+ for(hqptr=shpnt->host_queue; hqptr!=SCpnt; hqptr=hqptr->next) ;
+ if(hqptr) {
+ prev=hqptr->prev;
+ next=hqptr->next;
+ if(prev)
+ prev->next=next;
+ else
+ shpnt->host_queue=next;
+ if(next) next->prev=prev;
+ }
+ }
+
+ /* Last device block does not exist. Free memory. */
+ if (SDpnt != NULL)
+ scsi_init_free ((char *) SDpnt, sizeof (Scsi_Device));
+
+ if (SCpnt != NULL)
+ scsi_init_free ((char *) SCpnt, sizeof (Scsi_Cmnd));
+
+ /* If we allocated a buffer so we could do DMA, free it now */
+ if (scsi_result != &scsi_result0[0] && scsi_result != NULL)
+ scsi_init_free (scsi_result, 512);
+
+}
+
+/*
+ * The worker for scan_scsis.
+ * Returning 0 means Please don't ask further for lun!=0, 1 means OK go on.
+ * Global variables used : scsi_devices(linked list)
+ */
+int scan_scsis_single (int channel, int dev, int lun, int *max_dev_lun,
+ Scsi_Device **SDpnt2, Scsi_Cmnd * SCpnt, struct Scsi_Host * shpnt,
+ char *scsi_result)
+{
+ unsigned char scsi_cmd[12];
+ struct Scsi_Device_Template *sdtpnt;
+ Scsi_Device * SDtail, *SDpnt=*SDpnt2;
+ int bflags, type=-1;
+
+ SDtail = scsi_devices;
+ if (scsi_devices)
+ while (SDtail->next)
+ SDtail = SDtail->next;
+
+ memset (SDpnt, 0, sizeof (Scsi_Device));
+ SDpnt->host = shpnt;
+ SDpnt->id = dev;
+ SDpnt->lun = lun;
+ SDpnt->channel = channel;
+
+ /* Some low level driver could use device->type (DB) */
+ SDpnt->type = -1;
+
+ /*
+ * Assume that the device will have handshaking problems, and then fix this
+ * field later if it turns out it doesn't
+ */
+ SDpnt->borken = 1;
+ SDpnt->was_reset = 0;
+ SDpnt->expecting_cc_ua = 0;
+
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[4] = scsi_cmd[5] = 0;
+
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT + 4 * HZ, 5);
+ down (&sem);
+ }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: scan_scsis_single id %d lun %d. Return code 0x%08x\n",
+ dev, lun, SCpnt->result);
+ print_driverbyte(SCpnt->result); print_hostbyte(SCpnt->result);
+ printk("\n");
+#endif
+
+ if (SCpnt->result) {
+ if (((driver_byte (SCpnt->result) & DRIVER_SENSE) ||
+ (status_byte (SCpnt->result) & CHECK_CONDITION)) &&
+ ((SCpnt->sense_buffer[0] & 0x70) >> 4) == 7) {
+ if (((SCpnt->sense_buffer[2] & 0xf) != NOT_READY) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != UNIT_ATTENTION) &&
+ ((SCpnt->sense_buffer[2] & 0xf) != ILLEGAL_REQUEST || lun > 0))
+ return 1;
+ }
+ else
+ return 0;
+ }
+
+#if defined (DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: performing INQUIRY\n");
+#endif
+ /*
+ * Build an INQUIRY command block.
+ */
+ scsi_cmd[0] = INQUIRY;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 255;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result,
+ 256, scan_scsis_done, SCSI_TIMEOUT, 3);
+ down (&sem);
+ }
+
+#if defined(DEBUG) || defined(DEBUG_INIT)
+ printk ("scsi: INQUIRY %s with code 0x%x\n",
+ SCpnt->result ? "failed" : "successful", SCpnt->result);
+#endif
+
+ if (SCpnt->result)
+ return 0; /* assume no peripheral if any sort of error */
+
+ /*
+ * It would seem some TOSHIBA CDROM gets things wrong
+ */
+ if (!strncmp (scsi_result + 8, "TOSHIBA", 7) &&
+ !strncmp (scsi_result + 16, "CD-ROM", 6) &&
+ scsi_result[0] == TYPE_DISK) {
+ scsi_result[0] = TYPE_ROM;
+ scsi_result[1] |= 0x80; /* removable */
+ }
+
+ if (!strncmp (scsi_result + 8, "NEC", 3)) {
+ if (!strncmp (scsi_result + 16, "CD-ROM DRIVE:84 ", 16) ||
+ !strncmp (scsi_result + 16, "CD-ROM DRIVE:25", 15))
+ SDpnt->manufacturer = SCSI_MAN_NEC_OLDCDR;
+ else
+ SDpnt->manufacturer = SCSI_MAN_NEC;
+ }
+ else if (!strncmp (scsi_result + 8, "TOSHIBA", 7))
+ SDpnt->manufacturer = SCSI_MAN_TOSHIBA;
+ else if (!strncmp (scsi_result + 8, "SONY", 4))
+ SDpnt->manufacturer = SCSI_MAN_SONY;
+ else if (!strncmp (scsi_result + 8, "PIONEER", 7))
+ SDpnt->manufacturer = SCSI_MAN_PIONEER;
+ else
+ SDpnt->manufacturer = SCSI_MAN_UNKNOWN;
+
+ memcpy (SDpnt->vendor, scsi_result + 8, 8);
+ memcpy (SDpnt->model, scsi_result + 16, 16);
+ memcpy (SDpnt->rev, scsi_result + 32, 4);
+
+ SDpnt->removable = (0x80 & scsi_result[1]) >> 7;
+ SDpnt->lockable = SDpnt->removable;
+ SDpnt->changed = 0;
+ SDpnt->access_count = 0;
+ SDpnt->busy = 0;
+ SDpnt->has_cmdblocks = 0;
+ /*
+ * Currently, all sequential devices are assumed to be tapes, all random
+ * devices disk, with the appropriate read only flags set for ROM / WORM
+ * treated as RO.
+ */
+ switch (type = (scsi_result[0] & 0x1f)) {
+ case TYPE_TAPE:
+ case TYPE_DISK:
+ case TYPE_MOD:
+ case TYPE_PROCESSOR:
+ case TYPE_SCANNER:
+ SDpnt->writeable = 1;
+ break;
+ case TYPE_WORM:
+ case TYPE_ROM:
+ SDpnt->writeable = 0;
+ break;
+ default:
+ printk ("scsi: unknown type %d\n", type);
+ }
+
+ SDpnt->single_lun = 0;
+ SDpnt->soft_reset =
+ (scsi_result[7] & 1) && ((scsi_result[3] & 7) == 2);
+ SDpnt->random = (type == TYPE_TAPE) ? 0 : 1;
+ SDpnt->type = (type & 0x1f);
+
+ print_inquiry (scsi_result);
+
+ for (sdtpnt = scsi_devicelist; sdtpnt;
+ sdtpnt = sdtpnt->next)
+ if (sdtpnt->detect)
+ SDpnt->attached +=
+ (*sdtpnt->detect) (SDpnt);
+
+ SDpnt->scsi_level = scsi_result[2] & 0x07;
+ if (SDpnt->scsi_level >= 2 ||
+ (SDpnt->scsi_level == 1 &&
+ (scsi_result[3] & 0x0f) == 1))
+ SDpnt->scsi_level++;
+
+ /*
+ * Set the tagged_queue flag for SCSI-II devices that purport to support
+ * tagged queuing in the INQUIRY data.
+ */
+ SDpnt->tagged_queue = 0;
+ if ((SDpnt->scsi_level >= SCSI_2) &&
+ (scsi_result[7] & 2)) {
+ SDpnt->tagged_supported = 1;
+ SDpnt->current_tag = 0;
+ }
+
+ /*
+ * Accommodate drivers that want to sleep when they should be in a polling
+ * loop.
+ */
+ SDpnt->disconnect = 0;
+
+ /*
+ * Get any flags for this device.
+ */
+ bflags = get_device_flags (scsi_result);
+
+ /*
+ * Some revisions of the Texel CD ROM drives have handshaking problems when
+ * used with the Seagate controllers. Before we know what type of device
+ * we're talking to, we assume it's borken and then change it here if it
+ * turns out that it isn't a TEXEL drive.
+ */
+ if ((bflags & BLIST_BORKEN) == 0)
+ SDpnt->borken = 0;
+
+ /*
+ * These devices need this "key" to unlock the devices so we can use it
+ */
+ if ((bflags & BLIST_KEY) != 0) {
+ printk ("Unlocked floptical drive.\n");
+ SDpnt->lockable = 0;
+ scsi_cmd[0] = MODE_SENSE;
+ scsi_cmd[1] = (lun << 5) & 0xe0;
+ scsi_cmd[2] = 0x2e;
+ scsi_cmd[3] = 0;
+ scsi_cmd[4] = 0x2a;
+ scsi_cmd[5] = 0;
+ SCpnt->cmd_len = 0;
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt, (void *) scsi_cmd,
+ (void *) scsi_result, 0x2a,
+ scan_scsis_done, SCSI_TIMEOUT, 3);
+ down (&sem);
+ }
+ }
+ /* Add this device to the linked list at the end */
+ if (SDtail)
+ SDtail->next = SDpnt;
+ else
+ scsi_devices = SDpnt;
+ SDtail = SDpnt;
+
+ SDpnt = (Scsi_Device *) scsi_init_malloc (sizeof (Scsi_Device), GFP_ATOMIC);
+ *SDpnt2=SDpnt;
+ if (!SDpnt)
+ printk ("scsi: scan_scsis_single: Cannot malloc\n");
+
+
+ /*
+ * Some scsi devices cannot be polled for lun != 0 due to firmware bugs
+ */
+ if (bflags & BLIST_NOLUN)
+ return 0; /* break; */
+
+ /*
+ * If we want to only allow I/O to one of the luns attached to this device
+ * at a time, then we set this flag.
+ */
+ if (bflags & BLIST_SINGLELUN)
+ SDpnt->single_lun = 1;
+
+ /*
+ * If this device is known to support multiple units, override the other
+ * settings, and scan all of them.
+ */
+ if (bflags & BLIST_FORCELUN)
+ *max_dev_lun = 8;
+ /*
+ * We assume the device can't handle lun!=0 if: - it reports scsi-0 (ANSI
+ * SCSI Revision 0) (old drives like MAXTOR XT-3280) or - it reports scsi-1
+ * (ANSI SCSI Revision 1) and Response Data Format 0
+ */
+ if (((scsi_result[2] & 0x07) == 0)
+ ||
+ ((scsi_result[2] & 0x07) == 1 &&
+ (scsi_result[3] & 0x0f) == 0))
+ return 0;
+ return 1;
+}
+
+/*
+ * Flag bits for the internal_timeout array
+ */
+#define NORMAL_TIMEOUT 0
+#define IN_ABORT 1
+#define IN_RESET 2
+
+/*
+ * This is our time out function, called when the timer expires for a
+ * given host adapter. It will attempt to abort the currently executing
+ * command, that failing perform a kernel panic.
+ */
+
+static void scsi_times_out (Scsi_Cmnd * SCpnt, int pid)
+{
+
+ switch (SCpnt->internal_timeout & (IN_ABORT | IN_RESET))
+ {
+ case NORMAL_TIMEOUT:
+ {
+#ifdef DEBUG_TIMEOUT
+ scsi_dump_status();
+#endif
+ }
+
+ if (!scsi_abort (SCpnt, DID_TIME_OUT, pid))
+ return;
+ case IN_ABORT:
+ printk("SCSI host %d abort (pid %ld) timed out - resetting\n",
+ SCpnt->host->host_no, SCpnt->pid);
+ if (!scsi_reset (SCpnt, FALSE))
+ return;
+ case IN_RESET:
+ case (IN_ABORT | IN_RESET):
+ /* This might be controversial, but if there is a bus hang,
+ * you might conceivably want the machine up and running
+ * esp if you have an ide disk.
+ */
+ printk("Unable to reset scsi host %d - ", SCpnt->host->host_no);
+ printk("probably a SCSI bus hang.\n");
+ SCpnt->internal_timeout &= ~IN_RESET;
+ scsi_reset (SCpnt, TRUE);
+ return;
+
+ default:
+ INTERNAL_ERROR;
+ }
+
+}
+
+
+/* This function takes a quick look at a request, and decides if it
+ * can be queued now, or if there would be a stall while waiting for
+ * something else to finish. This routine assumes that interrupts are
+ * turned off when entering the routine. It is the responsibility
+ * of the calling code to ensure that this is the case.
+ */
+
+Scsi_Cmnd * request_queueable (struct request * req, Scsi_Device * device)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ int tablesize;
+ Scsi_Cmnd * found = NULL;
+ struct buffer_head * bh, *bhp;
+
+ if (!device)
+ panic ("No device passed to request_queueable().\n");
+
+ if (req && req->rq_status == RQ_INACTIVE)
+ panic("Inactive in request_queueable");
+
+ SCpnt = device->host->host_queue;
+
+ /*
+ * Look for a free command block. If we have been instructed not to queue
+ * multiple commands to multi-lun devices, then check to see what else is
+ * going for this device first.
+ */
+
+ SCpnt = device->host->host_queue;
+ if (!device->single_lun) {
+ while(SCpnt){
+ if(SCpnt->target == device->id &&
+ SCpnt->lun == device->lun) {
+ if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+ }
+ SCpnt = SCpnt->next;
+ }
+ } else {
+ while(SCpnt){
+ if(SCpnt->target == device->id) {
+ if (SCpnt->lun == device->lun) {
+ if(found == NULL
+ && SCpnt->request.rq_status == RQ_INACTIVE)
+ {
+ found=SCpnt;
+ }
+ }
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ /*
+ * I think that we should really limit things to one
+ * outstanding command per device - this is what tends
+ * to trip up buggy firmware.
+ */
+ return NULL;
+ }
+ }
+ SCpnt = SCpnt->next;
+ }
+ SCpnt = found;
+ }
+
+ if (!SCpnt) return NULL;
+
+ if (SCSI_BLOCK(device->host)) return NULL;
+
+ if (req) {
+ memcpy(&SCpnt->request, req, sizeof(struct request));
+ tablesize = device->host->sg_tablesize;
+ bhp = bh = req->bh;
+ if(!tablesize) bh = NULL;
+ /* Take a quick look through the table to see how big it is.
+ * We already have our copy of req, so we can mess with that
+ * if we want to.
+ */
+ while(req->nr_sectors && bh){
+ bhp = bhp->b_reqnext;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ if(!tablesize) break;
+ bh = bhp;
+ }
+ if(req->nr_sectors && bh && bh->b_reqnext){ /* Any leftovers? */
+ SCpnt->request.bhtail = bh;
+ req->bh = bh->b_reqnext; /* Divide request */
+ bh->b_reqnext = NULL;
+ bh = req->bh;
+
+ /* Now reset things so that req looks OK */
+ SCpnt->request.nr_sectors -= req->nr_sectors;
+ req->current_nr_sectors = bh->b_size >> 9;
+ req->buffer = bh->b_data;
+ SCpnt->request.sem = NULL; /* Wait until whole thing done */
+ } else {
+ req->rq_status = RQ_INACTIVE;
+ wake_up(&wait_for_request);
+ }
+ } else {
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Busy, but no request */
+ SCpnt->request.sem = NULL; /* And no one is waiting for the device
+ * either */
+ }
+
+ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
+ SCpnt->old_use_sg = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->underflow = 0;
+ SCpnt->cmd_len = 0;
+
+/* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */
+ SCpnt->channel = device->channel;
+ SCpnt->lun = device->lun;
+ SCpnt->target = device->id;
+
+ return SCpnt;
+}
+
+/* This function returns a structure pointer that will be valid for
+ * the device. The wait parameter tells us whether we should wait for
+ * the unit to become free or not. We are also able to tell this routine
+ * not to return a descriptor if the host is unable to accept any more
+ * commands for the time being. We need to keep in mind that there is no
+ * guarantee that the host remain not busy. Keep in mind the
+ * request_queueable function also knows the internal allocation scheme
+ * of the packets for each device
+ */
+
+Scsi_Cmnd * allocate_device (struct request ** reqp, Scsi_Device * device,
+ int wait)
+{
+ kdev_t dev;
+ struct request * req = NULL;
+ int tablesize;
+ unsigned int flags;
+ struct buffer_head * bh, *bhp;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+ Scsi_Cmnd * SCwait = NULL;
+ Scsi_Cmnd * found = NULL;
+
+ if (!device)
+ panic ("No device passed to allocate_device().\n");
+
+ if (reqp) req = *reqp;
+
+ /* See if this request has already been queued by an interrupt routine */
+ if (req) {
+ if(req->rq_status == RQ_INACTIVE) return NULL;
+ dev = req->rq_dev;
+ } else
+ dev = 0; /* unused */
+
+ host = device->host;
+
+ if (intr_count && SCSI_BLOCK(host)) return NULL;
+
+ while (1==1){
+ SCpnt = device->host->host_queue;
+ if (!device->single_lun) {
+ while(SCpnt){
+ if(SCpnt->target == device->id &&
+ SCpnt->lun == device->lun) {
+ SCwait = SCpnt;
+ if(SCpnt->request.rq_status == RQ_INACTIVE) break;
+ }
+ SCpnt = SCpnt->next;
+ }
+ } else {
+ while(SCpnt){
+ if(SCpnt->target == device->id) {
+ if (SCpnt->lun == device->lun) {
+ SCwait = SCpnt;
+ if(found == NULL
+ && SCpnt->request.rq_status == RQ_INACTIVE)
+ {
+ found=SCpnt;
+ }
+ }
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ /*
+ * I think that we should really limit things to one
+ * outstanding command per device - this is what tends
+ * to trip up buggy firmware.
+ */
+ found = NULL;
+ break;
+ }
+ }
+ SCpnt = SCpnt->next;
+ }
+ SCpnt = found;
+ }
+
+ save_flags(flags);
+ cli();
+ /* See if this request has already been queued by an interrupt routine
+ */
+ if (req && (req->rq_status == RQ_INACTIVE || req->rq_dev != dev)) {
+ restore_flags(flags);
+ return NULL;
+ }
+ if (!SCpnt || SCpnt->request.rq_status != RQ_INACTIVE) /* Might have changed */
+ {
+ restore_flags(flags);
+ if(!wait) return NULL;
+ if (!SCwait) {
+ printk("Attempt to allocate device channel %d, target %d, "
+ "lun %d\n", device->channel, device->id, device->lun);
+ panic("No device found in allocate_device\n");
+ }
+ SCSI_SLEEP(&device->device_wait,
+ (SCwait->request.rq_status != RQ_INACTIVE));
+ } else {
+ if (req) {
+ memcpy(&SCpnt->request, req, sizeof(struct request));
+ tablesize = device->host->sg_tablesize;
+ bhp = bh = req->bh;
+ if(!tablesize) bh = NULL;
+ /* Take a quick look through the table to see how big it is.
+ * We already have our copy of req, so we can mess with that
+ * if we want to.
+ */
+ while(req->nr_sectors && bh){
+ bhp = bhp->b_reqnext;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bh,bhp)) tablesize--;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ if(!tablesize) break;
+ bh = bhp;
+ }
+ if(req->nr_sectors && bh && bh->b_reqnext){/* Any leftovers? */
+ SCpnt->request.bhtail = bh;
+ req->bh = bh->b_reqnext; /* Divide request */
+ bh->b_reqnext = NULL;
+ bh = req->bh;
+ /* Now reset things so that req looks OK */
+ SCpnt->request.nr_sectors -= req->nr_sectors;
+ req->current_nr_sectors = bh->b_size >> 9;
+ req->buffer = bh->b_data;
+ SCpnt->request.sem = NULL; /* Wait until whole thing done*/
+ }
+ else
+ {
+ req->rq_status = RQ_INACTIVE;
+ *reqp = req->next;
+ wake_up(&wait_for_request);
+ }
+ } else {
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = NULL; /* And no one is waiting for this
+ * to complete */
+ }
+ restore_flags(flags);
+ break;
+ }
+ }
+
+ SCpnt->use_sg = 0; /* Reset the scatter-gather flag */
+ SCpnt->old_use_sg = 0;
+ SCpnt->transfersize = 0; /* No default transfer size */
+ SCpnt->cmd_len = 0;
+
+ SCpnt->underflow = 0; /* Do not flag underflow conditions */
+
+ /* Since not everyone seems to set the device info correctly
+ * before Scsi_Cmnd gets send out to scsi_do_command, we do it here.
+ */
+ SCpnt->channel = device->channel;
+ SCpnt->lun = device->lun;
+ SCpnt->target = device->id;
+
+ return SCpnt;
+}
+
+/*
+ * This is inline because we have stack problemes if we recurse to deeply.
+ */
+
+inline void internal_cmnd (Scsi_Cmnd * SCpnt)
+{
+ int temp;
+ struct Scsi_Host * host;
+ unsigned int flags;
+#ifdef DEBUG_DELAY
+ int clock;
+#endif
+
+ host = SCpnt->host;
+
+ /*
+ * We will wait MIN_RESET_DELAY clock ticks after the last reset so
+ * we can avoid the drive not being ready.
+ */
+ save_flags(flags);
+ sti();
+ temp = host->last_reset + MIN_RESET_DELAY;
+ while (jiffies < temp);
+ restore_flags(flags);
+
+ update_timeout(SCpnt, SCpnt->timeout_per_command);
+
+ /*
+ * We will use a queued command if possible, otherwise we will emulate the
+ * queuing and calling of completion function ourselves.
+ */
+#ifdef DEBUG
+ printk("internal_cmnd (host = %d, channel = %d, target = %d, "
+ "command = %p, buffer = %p, \nbufflen = %d, done = %p)\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->target, SCpnt->cmnd,
+ SCpnt->buffer, SCpnt->bufflen, SCpnt->done);
+#endif
+
+ if (host->can_queue)
+ {
+#ifdef DEBUG
+ printk("queuecommand : routine at %p\n",
+ host->hostt->queuecommand);
+#endif
+ /* This locking tries to prevent all sorts of races between
+ * queuecommand and the interrupt code. In effect,
+ * we are only allowed to be in queuecommand once at
+ * any given time, and we can only be in the interrupt
+ * handler and the queuecommand function at the same time
+ * when queuecommand is called while servicing the
+ * interrupt.
+ */
+
+ if(!intr_count && SCpnt->host->irq)
+ disable_irq(SCpnt->host->irq);
+
+ host->hostt->queuecommand (SCpnt, scsi_done);
+
+ if(!intr_count && SCpnt->host->irq)
+ enable_irq(SCpnt->host->irq);
+ }
+ else
+ {
+
+#ifdef DEBUG
+ printk("command() : routine at %p\n", host->hostt->command);
+#endif
+ temp=host->hostt->command (SCpnt);
+ SCpnt->result = temp;
+#ifdef DEBUG_DELAY
+ clock = jiffies + 4 * HZ;
+ while (jiffies < clock);
+ printk("done(host = %d, result = %04x) : routine at %p\n",
+ host->host_no, temp, host->hostt->command);
+#endif
+ scsi_done(SCpnt);
+ }
+#ifdef DEBUG
+ printk("leaving internal_cmnd()\n");
+#endif
+}
+
+static void scsi_request_sense (Scsi_Cmnd * SCpnt)
+{
+ unsigned int flags;
+
+ save_flags(flags);
+ cli();
+ SCpnt->flags |= WAS_SENSE | ASKED_FOR_SENSE;
+ update_timeout(SCpnt, SENSE_TIMEOUT);
+ restore_flags(flags);
+
+
+ memcpy ((void *) SCpnt->cmnd , (void *) generic_sense,
+ sizeof(generic_sense));
+
+ SCpnt->cmnd[1] = SCpnt->lun << 5;
+ SCpnt->cmnd[4] = sizeof(SCpnt->sense_buffer);
+
+ SCpnt->request_buffer = &SCpnt->sense_buffer;
+ SCpnt->request_bufflen = sizeof(SCpnt->sense_buffer);
+ SCpnt->use_sg = 0;
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ internal_cmnd (SCpnt);
+}
+
+
+
+/*
+ * scsi_do_cmd sends all the commands out to the low-level driver. It
+ * handles the specifics required for each low level driver - ie queued
+ * or non queued. It also prevents conflicts when different high level
+ * drivers go for the same host at the same time.
+ */
+
+void scsi_do_cmd (Scsi_Cmnd * SCpnt, const void *cmnd ,
+ void *buffer, unsigned bufflen, void (*done)(Scsi_Cmnd *),
+ int timeout, int retries)
+{
+ unsigned long flags;
+ struct Scsi_Host * host = SCpnt->host;
+
+#ifdef DEBUG
+ {
+ int i;
+ int target = SCpnt->target;
+ printk ("scsi_do_cmd (host = %d, channel = %d target = %d, "
+ "buffer =%p, bufflen = %d, done = %p, timeout = %d, "
+ "retries = %d)\n"
+ "command : " , host->host_no, SCpnt->channel, target, buffer,
+ bufflen, done, timeout, retries);
+ for (i = 0; i < 10; ++i)
+ printk ("%02x ", ((unsigned char *) cmnd)[i]);
+ printk("\n");
+ }
+#endif
+
+ if (!host)
+ {
+ panic ("Invalid or not present host.\n");
+ }
+
+
+ /*
+ * We must prevent reentrancy to the lowlevel host driver. This prevents
+ * it - we enter a loop until the host we want to talk to is not busy.
+ * Race conditions are prevented, as interrupts are disabled in between the
+ * time we check for the host being not busy, and the time we mark it busy
+ * ourselves.
+ */
+
+ save_flags(flags);
+ cli();
+ SCpnt->pid = scsi_pid++;
+
+ while (SCSI_BLOCK(host)) {
+ restore_flags(flags);
+ SCSI_SLEEP(&host->host_wait, SCSI_BLOCK(host));
+ cli();
+ }
+
+ if (host->block) host_active = host;
+
+ host->host_busy++;
+ restore_flags(flags);
+
+ /*
+ * Our own function scsi_done (which marks the host as not busy, disables
+ * the timeout counter, etc) will be called by us or by the
+ * scsi_hosts[host].queuecommand() function needs to also call
+ * the completion function for the high level driver.
+ */
+
+ memcpy ((void *) SCpnt->data_cmnd , (const void *) cmnd, 12);
+#if 0
+ SCpnt->host = host;
+ SCpnt->channel = channel;
+ SCpnt->target = target;
+ SCpnt->lun = (SCpnt->data_cmnd[1] >> 5);
+#endif
+ SCpnt->bufflen = bufflen;
+ SCpnt->buffer = buffer;
+ SCpnt->flags=0;
+ SCpnt->retries=0;
+ SCpnt->allowed=retries;
+ SCpnt->done = done;
+ SCpnt->timeout_per_command = timeout;
+
+ memcpy ((void *) SCpnt->cmnd , (const void *) cmnd, 12);
+ /* Zero the sense buffer. Some host adapters automatically request
+ * sense on error. 0 is not a valid sense code.
+ */
+ memset ((void *) SCpnt->sense_buffer, 0, sizeof SCpnt->sense_buffer);
+ SCpnt->request_buffer = buffer;
+ SCpnt->request_bufflen = bufflen;
+ SCpnt->old_use_sg = SCpnt->use_sg;
+ if (SCpnt->cmd_len == 0)
+ SCpnt->cmd_len = COMMAND_SIZE(SCpnt->cmnd[0]);
+ SCpnt->old_cmd_len = SCpnt->cmd_len;
+
+ /* Start the timer ticking. */
+
+ SCpnt->internal_timeout = 0;
+ SCpnt->abort_reason = 0;
+ internal_cmnd (SCpnt);
+
+#ifdef DEBUG
+ printk ("Leaving scsi_do_cmd()\n");
+#endif
+}
+
+static int check_sense (Scsi_Cmnd * SCpnt)
+{
+ /* If there is no sense information, request it. If we have already
+ * requested it, there is no point in asking again - the firmware must
+ * be confused.
+ */
+ if (((SCpnt->sense_buffer[0] & 0x70) >> 4) != 7) {
+ if(!(SCpnt->flags & ASKED_FOR_SENSE))
+ return SUGGEST_SENSE;
+ else
+ return SUGGEST_RETRY;
+ }
+
+ SCpnt->flags &= ~ASKED_FOR_SENSE;
+
+#ifdef DEBUG_INIT
+ printk("scsi%d, channel%d : ", SCpnt->host->host_no, SCpnt->channel);
+ print_sense("", SCpnt);
+ printk("\n");
+#endif
+ if (SCpnt->sense_buffer[2] & 0xe0)
+ return SUGGEST_ABORT;
+
+ switch (SCpnt->sense_buffer[2] & 0xf)
+ {
+ case NO_SENSE:
+ return 0;
+ case RECOVERED_ERROR:
+ return SUGGEST_IS_OK;
+
+ case ABORTED_COMMAND:
+ return SUGGEST_RETRY;
+ case NOT_READY:
+ case UNIT_ATTENTION:
+ /*
+ * If we are expecting a CC/UA because of a bus reset that we
+ * performed, treat this just as a retry. Otherwise this is
+ * information that we should pass up to the upper-level driver
+ * so that we can deal with it there.
+ */
+ if( SCpnt->device->expecting_cc_ua )
+ {
+ SCpnt->device->expecting_cc_ua = 0;
+ return SUGGEST_RETRY;
+ }
+ return SUGGEST_ABORT;
+
+ /* these three are not supported */
+ case COPY_ABORTED:
+ case VOLUME_OVERFLOW:
+ case MISCOMPARE:
+
+ case MEDIUM_ERROR:
+ return SUGGEST_REMAP;
+ case BLANK_CHECK:
+ case DATA_PROTECT:
+ case HARDWARE_ERROR:
+ case ILLEGAL_REQUEST:
+ default:
+ return SUGGEST_ABORT;
+ }
+}
+
+/* This function is the mid-level interrupt routine, which decides how
+ * to handle error conditions. Each invocation of this function must
+ * do one and *only* one of the following:
+ *
+ * (1) Call last_cmnd[host].done. This is done for fatal errors and
+ * normal completion, and indicates that the handling for this
+ * request is complete.
+ * (2) Call internal_cmnd to requeue the command. This will result in
+ * scsi_done being called again when the retry is complete.
+ * (3) Call scsi_request_sense. This asks the host adapter/drive for
+ * more information about the error condition. When the information
+ * is available, scsi_done will be called again.
+ * (4) Call reset(). This is sort of a last resort, and the idea is that
+ * this may kick things loose and get the drive working again. reset()
+ * automatically calls scsi_request_sense, and thus scsi_done will be
+ * called again once the reset is complete.
+ *
+ * If none of the above actions are taken, the drive in question
+ * will hang. If more than one of the above actions are taken by
+ * scsi_done, then unpredictable behavior will result.
+ */
+static void scsi_done (Scsi_Cmnd * SCpnt)
+{
+ int status=0;
+ int exit=0;
+ int checked;
+ int oldto;
+ struct Scsi_Host * host = SCpnt->host;
+ int result = SCpnt->result;
+ oldto = update_timeout(SCpnt, 0);
+
+#ifdef DEBUG_TIMEOUT
+ if(result) printk("Non-zero result in scsi_done %x %d:%d\n",
+ result, SCpnt->target, SCpnt->lun);
+#endif
+
+ /* If we requested an abort, (and we got it) then fix up the return
+ * status to say why
+ */
+ if(host_byte(result) == DID_ABORT && SCpnt->abort_reason)
+ SCpnt->result = result = (result & 0xff00ffff) |
+ (SCpnt->abort_reason << 16);
+
+
+#define FINISHED 0
+#define MAYREDO 1
+#define REDO 3
+#define PENDING 4
+
+#ifdef DEBUG
+ printk("In scsi_done(host = %d, result = %06x)\n", host->host_no, result);
+#endif
+
+ if(SCpnt->flags & WAS_SENSE)
+ {
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ }
+
+ switch (host_byte(result))
+ {
+ case DID_OK:
+ if (status_byte(result) && (SCpnt->flags & WAS_SENSE))
+ /* Failed to obtain sense information */
+ {
+ SCpnt->flags &= ~WAS_SENSE;
+ SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+
+ if (!(SCpnt->flags & WAS_RESET))
+ {
+ printk("scsi%d : channel %d target %d lun %d request sense"
+ " failed, performing reset.\n",
+ SCpnt->host->host_no, SCpnt->channel, SCpnt->target,
+ SCpnt->lun);
+ scsi_reset(SCpnt, FALSE);
+ return;
+ }
+ else
+ {
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ status = FINISHED;
+ }
+ }
+ else switch(msg_byte(result))
+ {
+ case COMMAND_COMPLETE:
+ switch (status_byte(result))
+ {
+ case GOOD:
+ if (SCpnt->flags & WAS_SENSE)
+ {
+#ifdef DEBUG
+ printk ("In scsi_done, GOOD status, COMMAND COMPLETE, parsing sense information.\n");
+#endif
+ SCpnt->flags &= ~WAS_SENSE;
+ SCpnt->internal_timeout &= ~SENSE_TIMEOUT;
+
+ switch (checked = check_sense(SCpnt))
+ {
+ case SUGGEST_SENSE:
+ case 0:
+#ifdef DEBUG
+ printk("NO SENSE. status = REDO\n");
+#endif
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_IS_OK:
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+#ifdef DEBUG
+ printk("SENSE SUGGEST REMAP or SUGGEST RETRY - status = MAYREDO\n");
+#endif
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+#ifdef DEBUG
+ printk("SENSE SUGGEST ABORT - status = FINISHED");
+#endif
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ default:
+ printk ("Internal error %s %d \n", __FILE__,
+ __LINE__);
+ }
+ } /* end WAS_SENSE */
+ else
+ {
+#ifdef DEBUG
+ printk("COMMAND COMPLETE message returned, status = FINISHED. \n");
+#endif
+ exit = DRIVER_OK;
+ status = FINISHED;
+ }
+ break;
+
+ case CHECK_CONDITION:
+ switch (check_sense(SCpnt))
+ {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ }
+ break;
+
+ case CONDITION_GOOD:
+ case INTERMEDIATE_GOOD:
+ case INTERMEDIATE_C_GOOD:
+ break;
+
+ case BUSY:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+
+ case RESERVATION_CONFLICT:
+ printk("scsi%d, channel %d : RESERVATION CONFLICT performing"
+ " reset.\n", SCpnt->host->host_no, SCpnt->channel);
+ scsi_reset(SCpnt, FALSE);
+ return;
+#if 0
+ exit = DRIVER_SOFT | SUGGEST_ABORT;
+ status = MAYREDO;
+ break;
+#endif
+ default:
+ printk ("Internal error %s %d \n"
+ "status byte = %d \n", __FILE__,
+ __LINE__, status_byte(result));
+
+ }
+ break;
+ default:
+ panic("scsi: unsupported message byte %d received\n",
+ msg_byte(result));
+ }
+ break;
+ case DID_TIME_OUT:
+#ifdef DEBUG
+ printk("Host returned DID_TIME_OUT - ");
+#endif
+
+ if (SCpnt->flags & WAS_TIMEDOUT)
+ {
+#ifdef DEBUG
+ printk("Aborting\n");
+#endif
+ /*
+ Allow TEST_UNIT_READY and INQUIRY commands to timeout early
+ without causing resets. All other commands should be retried.
+ */
+ if (SCpnt->cmnd[0] != TEST_UNIT_READY &&
+ SCpnt->cmnd[0] != INQUIRY)
+ status = MAYREDO;
+ exit = (DRIVER_TIMEOUT | SUGGEST_ABORT);
+ }
+ else
+ {
+#ifdef DEBUG
+ printk ("Retrying.\n");
+#endif
+ SCpnt->flags |= WAS_TIMEDOUT;
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ status = REDO;
+ }
+ break;
+ case DID_BUS_BUSY:
+ case DID_PARITY:
+ status = REDO;
+ break;
+ case DID_NO_CONNECT:
+#ifdef DEBUG
+ printk("Couldn't connect.\n");
+#endif
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_ERROR:
+ status = MAYREDO;
+ exit = (DRIVER_HARD | SUGGEST_ABORT);
+ break;
+ case DID_BAD_TARGET:
+ case DID_ABORT:
+ exit = (DRIVER_INVALID | SUGGEST_ABORT);
+ break;
+ case DID_RESET:
+ if (SCpnt->flags & IS_RESETTING)
+ {
+ SCpnt->flags &= ~IS_RESETTING;
+ status = REDO;
+ break;
+ }
+
+ if(msg_byte(result) == GOOD &&
+ status_byte(result) == CHECK_CONDITION) {
+ switch (check_sense(SCpnt)) {
+ case 0:
+ update_timeout(SCpnt, oldto);
+ status = REDO;
+ break;
+ case SUGGEST_REMAP:
+ case SUGGEST_RETRY:
+ status = MAYREDO;
+ exit = DRIVER_SENSE | SUGGEST_RETRY;
+ break;
+ case SUGGEST_ABORT:
+ status = FINISHED;
+ exit = DRIVER_SENSE | SUGGEST_ABORT;
+ break;
+ case SUGGEST_SENSE:
+ scsi_request_sense (SCpnt);
+ status = PENDING;
+ break;
+ }
+ } else {
+ status=REDO;
+ exit = SUGGEST_RETRY;
+ }
+ break;
+ default :
+ exit = (DRIVER_ERROR | SUGGEST_DIE);
+ }
+
+ switch (status)
+ {
+ case FINISHED:
+ case PENDING:
+ break;
+ case MAYREDO:
+#ifdef DEBUG
+ printk("In MAYREDO, allowing %d retries, have %d\n",
+ SCpnt->allowed, SCpnt->retries);
+#endif
+ if ((++SCpnt->retries) < SCpnt->allowed)
+ {
+ if ((SCpnt->retries >= (SCpnt->allowed >> 1))
+ && !(jiffies < SCpnt->host->last_reset + MIN_RESET_PERIOD)
+ && !(SCpnt->flags & WAS_RESET))
+ {
+ printk("scsi%d channel %d : resetting for second half of retries.\n",
+ SCpnt->host->host_no, SCpnt->channel);
+ scsi_reset(SCpnt, FALSE);
+ break;
+ }
+
+ }
+ else
+ {
+ status = FINISHED;
+ break;
+ }
+ /* fall through to REDO */
+
+ case REDO:
+
+ if (SCpnt->flags & WAS_SENSE)
+ scsi_request_sense(SCpnt);
+ else
+ {
+ memcpy ((void *) SCpnt->cmnd,
+ (void*) SCpnt->data_cmnd,
+ sizeof(SCpnt->data_cmnd));
+ SCpnt->request_buffer = SCpnt->buffer;
+ SCpnt->request_bufflen = SCpnt->bufflen;
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ internal_cmnd (SCpnt);
+ }
+ break;
+ default:
+ INTERNAL_ERROR;
+ }
+
+ if (status == FINISHED) {
+#ifdef DEBUG
+ printk("Calling done function - at address %p\n", SCpnt->done);
+#endif
+ host->host_busy--; /* Indicate that we are free */
+
+ if (host->block && host->host_busy == 0) {
+ host_active = NULL;
+
+ /* For block devices "wake_up" is done in end_scsi_request */
+ if (MAJOR(SCpnt->request.rq_dev) != SCSI_DISK_MAJOR &&
+ MAJOR(SCpnt->request.rq_dev) != SCSI_CDROM_MAJOR) {
+ struct Scsi_Host * next;
+
+ for (next = host->block; next != host; next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ }
+
+ wake_up(&host->host_wait);
+ SCpnt->result = result | ((exit & 0xff) << 24);
+ SCpnt->use_sg = SCpnt->old_use_sg;
+ SCpnt->cmd_len = SCpnt->old_cmd_len;
+ SCpnt->done (SCpnt);
+ }
+
+#undef FINISHED
+#undef REDO
+#undef MAYREDO
+#undef PENDING
+}
+
+/*
+ * The scsi_abort function interfaces with the abort() function of the host
+ * we are aborting, and causes the current command to not complete. The
+ * caller should deal with any error messages or status returned on the
+ * next call.
+ *
+ * This will not be called reentrantly for a given host.
+ */
+
+/*
+ * Since we're nice guys and specified that abort() and reset()
+ * can be non-reentrant. The internal_timeout flags are used for
+ * this.
+ */
+
+
+int scsi_abort (Scsi_Cmnd * SCpnt, int why, int pid)
+{
+ int oldto;
+ unsigned long flags;
+ struct Scsi_Host * host = SCpnt->host;
+
+ while(1)
+ {
+ save_flags(flags);
+ cli();
+
+ /*
+ * Protect against races here. If the command is done, or we are
+ * on a different command forget it.
+ */
+ if (SCpnt->request.rq_status == RQ_INACTIVE || pid != SCpnt->pid) {
+ restore_flags(flags);
+ return 0;
+ }
+
+ if (SCpnt->internal_timeout & IN_ABORT)
+ {
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_ABORT)
+ barrier();
+ }
+ else
+ {
+ SCpnt->internal_timeout |= IN_ABORT;
+ oldto = update_timeout(SCpnt, ABORT_TIMEOUT);
+
+ if ((SCpnt->flags & IS_RESETTING) && SCpnt->device->soft_reset) {
+ /* OK, this command must have died when we did the
+ * reset. The device itself must have lied.
+ */
+ printk("Stale command on %d %d:%d appears to have died when"
+ " the bus was reset\n",
+ SCpnt->channel, SCpnt->target, SCpnt->lun);
+ }
+
+ restore_flags(flags);
+ if (!host->host_busy) {
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, oldto);
+ return 0;
+ }
+ printk("scsi : aborting command due to timeout : pid %lu, scsi%d,"
+ " channel %d, id %d, lun %d ",
+ SCpnt->pid, SCpnt->host->host_no, (int) SCpnt->channel,
+ (int) SCpnt->target, (int) SCpnt->lun);
+ print_command (SCpnt->cmnd);
+ if (SCpnt->request.rq_status == RQ_INACTIVE || pid != SCpnt->pid)
+ return 0;
+ SCpnt->abort_reason = why;
+ switch(host->hostt->abort(SCpnt)) {
+ /* We do not know how to abort. Try waiting another
+ * time increment and see if this helps. Set the
+ * WAS_TIMEDOUT flag set so we do not try this twice
+ */
+ case SCSI_ABORT_BUSY: /* Tough call - returning 1 from
+ * this is too severe
+ */
+ case SCSI_ABORT_SNOOZE:
+ if(why == DID_TIME_OUT) {
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ if(SCpnt->flags & WAS_TIMEDOUT) {
+ restore_flags(flags);
+ return 1; /* Indicate we cannot handle this.
+ * We drop down into the reset handler
+ * and try again
+ */
+ } else {
+ SCpnt->flags |= WAS_TIMEDOUT;
+ oldto = SCpnt->timeout_per_command;
+ update_timeout(SCpnt, oldto);
+ }
+ restore_flags(flags);
+ }
+ return 0;
+ case SCSI_ABORT_PENDING:
+ if(why != DID_TIME_OUT) {
+ save_flags(flags);
+ cli();
+ update_timeout(SCpnt, oldto);
+ restore_flags(flags);
+ }
+ return 0;
+ case SCSI_ABORT_SUCCESS:
+ /* We should have already aborted this one. No
+ * need to adjust timeout
+ */
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ return 0;
+ case SCSI_ABORT_NOT_RUNNING:
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ update_timeout(SCpnt, 0);
+ return 0;
+ case SCSI_ABORT_ERROR:
+ default:
+ SCpnt->internal_timeout &= ~IN_ABORT;
+ return 1;
+ }
+ }
+ }
+}
+
+
+/* Mark a single SCSI Device as having been reset. */
+
+static inline void scsi_mark_device_reset(Scsi_Device *Device)
+{
+ Device->was_reset = 1;
+ Device->expecting_cc_ua = 1;
+}
+
+
+/* Mark all SCSI Devices on a specific Host as having been reset. */
+
+void scsi_mark_host_bus_reset(struct Scsi_Host *Host)
+{
+ Scsi_Cmnd *SCpnt;
+ for(SCpnt = Host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ scsi_mark_device_reset(SCpnt->device);
+}
+
+
+int scsi_reset (Scsi_Cmnd * SCpnt, int bus_reset_flag)
+{
+ int temp, oldto;
+ unsigned long flags;
+ Scsi_Cmnd * SCpnt1;
+ struct Scsi_Host * host = SCpnt->host;
+
+ printk("SCSI bus is being reset for host %d.\n",
+ host->host_no);
+
+ /*
+ * First of all, we need to make a recommendation to the low-level
+ * driver as to whether a BUS_DEVICE_RESET should be performed,
+ * or whether we should do a full BUS_RESET. There is no simple
+ * algorithm here - we basically use a series of heuristics
+ * to determine what we should do.
+ */
+ SCpnt->host->suggest_bus_reset = FALSE;
+
+ /*
+ * First see if all of the active devices on the bus have
+ * been jammed up so that we are attempting resets. If so,
+ * then suggest a bus reset. Forcing a bus reset could
+ * result in some race conditions, but no more than
+ * you would usually get with timeouts. We will cross
+ * that bridge when we come to it.
+ */
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if( SCpnt1->request.rq_status != RQ_INACTIVE
+ && (SCpnt1->flags & (WAS_RESET | IS_RESETTING)) == 0 )
+ break;
+ SCpnt1 = SCpnt1->next;
+ }
+ if( SCpnt1 == NULL ) {
+ SCpnt->host->suggest_bus_reset = TRUE;
+ }
+
+
+ /*
+ * If the code that called us is suggesting a hard reset, then
+ * definitely request it. This usually occurs because a
+ * BUS_DEVICE_RESET times out.
+ */
+ if( bus_reset_flag ) {
+ SCpnt->host->suggest_bus_reset = TRUE;
+ }
+
+ while (1) {
+ save_flags(flags);
+ cli();
+ if (SCpnt->internal_timeout & IN_RESET)
+ {
+ restore_flags(flags);
+ while (SCpnt->internal_timeout & IN_RESET)
+ barrier();
+ }
+ else
+ {
+ SCpnt->internal_timeout |= IN_RESET;
+ oldto = update_timeout(SCpnt, RESET_TIMEOUT);
+
+ if (host->host_busy)
+ {
+ restore_flags(flags);
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if (SCpnt1->request.rq_status != RQ_INACTIVE) {
+#if 0
+ if (!(SCpnt1->flags & IS_RESETTING) &&
+ !(SCpnt1->internal_timeout & IN_ABORT))
+ scsi_abort(SCpnt1, DID_RESET, SCpnt->pid);
+#endif
+ SCpnt1->flags |= (WAS_RESET | IS_RESETTING);
+ }
+ SCpnt1 = SCpnt1->next;
+ }
+
+ host->last_reset = jiffies;
+ temp = host->hostt->reset(SCpnt);
+ host->last_reset = jiffies;
+ }
+ else
+ {
+ if (!host->block) host->host_busy++;
+ restore_flags(flags);
+ host->last_reset = jiffies;
+ SCpnt->flags |= (WAS_RESET | IS_RESETTING);
+ temp = host->hostt->reset(SCpnt);
+ host->last_reset = jiffies;
+ if (!host->block) host->host_busy--;
+ }
+
+#ifdef DEBUG
+ printk("scsi reset function returned %d\n", temp);
+#endif
+
+ /*
+ * Now figure out what we need to do, based upon
+ * what the low level driver said that it did.
+ * If the result is SCSI_RESET_SUCCESS, SCSI_RESET_PENDING,
+ * or SCSI_RESET_WAKEUP, then the low level driver did a
+ * bus device reset or bus reset, so we should go through
+ * and mark one or all of the devices on that bus
+ * as having been reset.
+ */
+ switch(temp & SCSI_RESET_ACTION) {
+ case SCSI_RESET_SUCCESS:
+ if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_host_bus_reset(host);
+ else scsi_mark_device_reset(SCpnt->device);
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~IN_RESET;
+ update_timeout(SCpnt, oldto);
+ restore_flags(flags);
+ return 0;
+ case SCSI_RESET_PENDING:
+ if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_host_bus_reset(host);
+ else scsi_mark_device_reset(SCpnt->device);
+ return 0;
+ case SCSI_RESET_PUNT:
+ SCpnt->internal_timeout &= ~IN_RESET;
+ scsi_request_sense (SCpnt);
+ return 0;
+ case SCSI_RESET_WAKEUP:
+ if (temp & SCSI_RESET_BUS_RESET)
+ scsi_mark_host_bus_reset(host);
+ else scsi_mark_device_reset(SCpnt->device);
+ SCpnt->internal_timeout &= ~IN_RESET;
+ scsi_request_sense (SCpnt);
+ /*
+ * Since a bus reset was performed, we
+ * need to wake up each and every command
+ * that was active on the bus.
+ */
+ if( temp & SCSI_RESET_BUS_RESET )
+ {
+ SCpnt1 = host->host_queue;
+ while(SCpnt1) {
+ if( SCpnt->request.rq_status != RQ_INACTIVE
+ && SCpnt1 != SCpnt)
+ scsi_request_sense (SCpnt);
+ SCpnt1 = SCpnt1->next;
+ }
+ }
+ return 0;
+ case SCSI_RESET_SNOOZE:
+ /* In this case, we set the timeout field to 0
+ * so that this command does not time out any more,
+ * and we return 1 so that we get a message on the
+ * screen.
+ */
+ save_flags(flags);
+ cli();
+ SCpnt->internal_timeout &= ~IN_RESET;
+ update_timeout(SCpnt, 0);
+ restore_flags(flags);
+ /* If you snooze, you lose... */
+ case SCSI_RESET_ERROR:
+ default:
+ return 1;
+ }
+
+ return temp;
+ }
+ }
+}
+
+
+static void scsi_main_timeout(void)
+{
+ /*
+ * We must not enter update_timeout with a timeout condition still pending.
+ */
+
+ int timed_out, pid;
+ unsigned long flags;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+
+ do {
+ save_flags(flags);
+ cli();
+
+ update_timeout(NULL, 0);
+ /*
+ * Find all timers such that they have 0 or negative (shouldn't happen)
+ * time remaining on them.
+ */
+
+ timed_out = 0;
+ for(host = scsi_hostlist; host; host = host->next) {
+ for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->timeout == -1)
+ {
+ SCpnt->timeout = 0;
+ pid = SCpnt->pid;
+ restore_flags(flags);
+ scsi_times_out(SCpnt, pid);
+ ++timed_out;
+ save_flags(flags);
+ cli();
+ }
+ }
+ } while (timed_out);
+ restore_flags(flags);
+}
+
+/*
+ * The strategy is to cause the timer code to call scsi_times_out()
+ * when the soonest timeout is pending.
+ * The arguments are used when we are queueing a new command, because
+ * we do not want to subtract the time used from this time, but when we
+ * set the timer, we want to take this value into account.
+ */
+
+static int update_timeout(Scsi_Cmnd * SCset, int timeout)
+{
+ unsigned int least, used;
+ unsigned int oldto;
+ unsigned long flags;
+ struct Scsi_Host * host;
+ Scsi_Cmnd * SCpnt = NULL;
+
+ save_flags(flags);
+ cli();
+
+ /*
+ * Figure out how much time has passed since the last time the timeouts
+ * were updated
+ */
+ used = (time_start) ? (jiffies - time_start) : 0;
+
+ /*
+ * Find out what is due to timeout soonest, and adjust all timeouts for
+ * the amount of time that has passed since the last time we called
+ * update_timeout.
+ */
+
+ oldto = 0;
+
+ if(SCset){
+ oldto = SCset->timeout - used;
+ SCset->timeout = timeout + used;
+ }
+
+ least = 0xffffffff;
+
+ for(host = scsi_hostlist; host; host = host->next)
+ for(SCpnt = host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if (SCpnt->timeout > 0) {
+ SCpnt->timeout -= used;
+ if(SCpnt->timeout <= 0) SCpnt->timeout = -1;
+ if(SCpnt->timeout > 0 && SCpnt->timeout < least)
+ least = SCpnt->timeout;
+ }
+
+ /*
+ * If something is due to timeout again, then we will set the next timeout
+ * interrupt to occur. Otherwise, timeouts are disabled.
+ */
+
+ if (least != 0xffffffff)
+ {
+ time_start = jiffies;
+ timer_table[SCSI_TIMER].expires = (time_elapsed = least) + jiffies;
+ timer_active |= 1 << SCSI_TIMER;
+ }
+ else
+ {
+ timer_table[SCSI_TIMER].expires = time_start = time_elapsed = 0;
+ timer_active &= ~(1 << SCSI_TIMER);
+ }
+ restore_flags(flags);
+ return oldto;
+}
+
+#ifdef CONFIG_MODULES
+static int scsi_register_host(Scsi_Host_Template *);
+static void scsi_unregister_host(Scsi_Host_Template *);
+#endif
+
+void *scsi_malloc(unsigned int len)
+{
+ unsigned int nbits, mask;
+ unsigned long flags;
+ int i, j;
+ if(len % SECTOR_SIZE != 0 || len > PAGE_SIZE)
+ return NULL;
+
+ save_flags(flags);
+ cli();
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ for(i=0;i < dma_sectors / SECTORS_PER_PAGE; i++)
+ for(j=0; j<=SECTORS_PER_PAGE - nbits; j++){
+ if ((dma_malloc_freelist[i] & (mask << j)) == 0){
+ dma_malloc_freelist[i] |= (mask << j);
+ restore_flags(flags);
+ dma_free_sectors -= nbits;
+#ifdef DEBUG
+ printk("SMalloc: %d %p\n",len, dma_malloc_pages[i] + (j << 9));
+#endif
+ return (void *) ((unsigned long) dma_malloc_pages[i] + (j << 9));
+ }
+ }
+ restore_flags(flags);
+ return NULL; /* Nope. No more */
+}
+
+int scsi_free(void *obj, unsigned int len)
+{
+ unsigned int page, sector, nbits, mask;
+ unsigned long flags;
+
+#ifdef DEBUG
+ printk("scsi_free %p %d\n",obj, len);
+#endif
+
+ for (page = 0; page < dma_sectors / SECTORS_PER_PAGE; page++) {
+ unsigned long page_addr = (unsigned long) dma_malloc_pages[page];
+ if ((unsigned long) obj >= page_addr &&
+ (unsigned long) obj < page_addr + PAGE_SIZE)
+ {
+ sector = (((unsigned long) obj) - page_addr) >> 9;
+
+ nbits = len >> 9;
+ mask = (1 << nbits) - 1;
+
+ if ((mask << sector) >= (1 << SECTORS_PER_PAGE))
+ panic ("scsi_free:Bad memory alignment");
+
+ save_flags(flags);
+ cli();
+ if((dma_malloc_freelist[page] & (mask << sector)) != (mask<<sector))
+ panic("scsi_free:Trying to free unused memory");
+
+ dma_free_sectors += nbits;
+ dma_malloc_freelist[page] &= ~(mask << sector);
+ restore_flags(flags);
+ return 0;
+ }
+ }
+ panic("scsi_free:Bad offset");
+}
+
+
+int scsi_loadable_module_flag; /* Set after we scan builtin drivers */
+
+void * scsi_init_malloc(unsigned int size, int priority)
+{
+ void * retval;
+
+ /*
+ * For buffers used by the DMA pool, we assume page aligned
+ * structures.
+ */
+ if ((size % PAGE_SIZE) == 0) {
+ int order, a_size;
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ retval = (void *) __get_dma_pages(priority & GFP_LEVEL_MASK,
+ order);
+ } else
+ retval = kmalloc(size, priority);
+
+ if (retval)
+ memset(retval, 0, size);
+ return retval;
+}
+
+
+void scsi_init_free(char * ptr, unsigned int size)
+{
+ /*
+ * We need this special code here because the DMA pool assumes
+ * page aligned data. Besides, it is wasteful to allocate
+ * page sized chunks with kmalloc.
+ */
+ if ((size % PAGE_SIZE) == 0) {
+ int order, a_size;
+
+ for (order = 0, a_size = PAGE_SIZE;
+ a_size < size; order++, a_size <<= 1)
+ ;
+ free_pages((unsigned long)ptr, order);
+ } else
+ kfree(ptr);
+}
+
+void scsi_build_commandblocks(Scsi_Device * SDpnt)
+{
+ int j;
+ Scsi_Cmnd * SCpnt;
+ struct Scsi_Host * host = NULL;
+
+ for(j=0;j<SDpnt->host->cmd_per_lun;j++){
+ SCpnt = (Scsi_Cmnd *) scsi_init_malloc(sizeof(Scsi_Cmnd), GFP_ATOMIC);
+ SCpnt->host = SDpnt->host;
+ SCpnt->device = SDpnt;
+ SCpnt->target = SDpnt->id;
+ SCpnt->lun = SDpnt->lun;
+ SCpnt->channel = SDpnt->channel;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ SCpnt->use_sg = 0;
+ SCpnt->old_use_sg = 0;
+ SCpnt->old_cmd_len = 0;
+ SCpnt->timeout = 0;
+ SCpnt->underflow = 0;
+ SCpnt->transfersize = 0;
+ SCpnt->host_scribble = NULL;
+ host = SDpnt->host;
+ if(host->host_queue)
+ host->host_queue->prev = SCpnt;
+ SCpnt->next = host->host_queue;
+ SCpnt->prev = NULL;
+ host->host_queue = SCpnt;
+ }
+ SDpnt->has_cmdblocks = 1;
+}
+
+/*
+ * scsi_dev_init() is our initialization routine, which in turn calls host
+ * initialization, bus scanning, and sd/st initialization routines.
+ */
+
+int scsi_dev_init(void)
+{
+ Scsi_Device * SDpnt;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Device_Template * sdtpnt;
+#ifdef FOO_ON_YOU
+ return;
+#endif
+
+ /* Yes we're here... */
+ dispatch_scsi_info_ptr = dispatch_scsi_info;
+
+ /* Init a few things so we can "malloc" memory. */
+ scsi_loadable_module_flag = 0;
+
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+
+#ifdef CONFIG_MODULES
+ register_symtab(&scsi_symbol_table);
+#endif
+
+ /* Register the /proc/scsi/scsi entry */
+#if CONFIG_PROC_FS
+ proc_scsi_register(0, &proc_scsi_scsi);
+#endif
+
+ /* initialize all hosts */
+ scsi_init();
+
+ scsi_devices = (Scsi_Device *) NULL;
+
+ for (shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ scan_scsis(shpnt,0,0,0,0); /* scan for scsi devices */
+
+ printk("scsi : detected ");
+ for (sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if (sdtpnt->dev_noticed && sdtpnt->name)
+ printk("%d SCSI %s%s ", sdtpnt->dev_noticed, sdtpnt->name,
+ (sdtpnt->dev_noticed != 1) ? "s" : "");
+ printk("total.\n");
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ SDpnt->scsi_request_fn = NULL;
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+
+
+ /*
+ * This should build the DMA pool.
+ */
+ resize_dma_pool();
+
+ /*
+ * OK, now we finish the initialization by doing spin-up, read
+ * capacity, etc, etc
+ */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+
+ scsi_loadable_module_flag = 1;
+
+ return 0;
+}
+
+static void print_inquiry(unsigned char *data)
+{
+ int i;
+
+ printk(" Vendor: ");
+ for (i = 8; i < 16; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Model: ");
+ for (i = 16; i < 32; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk(" Rev: ");
+ for (i = 32; i < 36; i++)
+ {
+ if (data[i] >= 0x20 && i < data[4] + 5)
+ printk("%c", data[i]);
+ else
+ printk(" ");
+ }
+
+ printk("\n");
+
+ i = data[0] & 0x1f;
+
+ printk(" Type: %s ",
+ i < MAX_SCSI_DEVICE_CODE ? scsi_device_types[i] : "Unknown " );
+ printk(" ANSI SCSI revision: %02x", data[2] & 0x07);
+ if ((data[2] & 0x07) == 1 && (data[3] & 0x0f) == 1)
+ printk(" CCS\n");
+ else
+ printk("\n");
+}
+
+
+#ifdef CONFIG_PROC_FS
+int scsi_proc_info(char *buffer, char **start, off_t offset, int length,
+ int hostno, int inout)
+{
+ Scsi_Device *scd;
+ struct Scsi_Host *HBA_ptr;
+ int parameter[4];
+ char *p;
+ int i,size, len = 0;
+ off_t begin = 0;
+ off_t pos = 0;
+
+ scd = scsi_devices;
+ HBA_ptr = scsi_hostlist;
+
+ if(inout == 0) {
+ size = sprintf(buffer+len,"Attached devices: %s\n", (scd)?"":"none");
+ len += size;
+ pos = begin + len;
+ while (HBA_ptr) {
+#if 0
+ size += sprintf(buffer+len,"scsi%2d: %s\n", (int) HBA_ptr->host_no,
+ HBA_ptr->hostt->procname);
+ len += size;
+ pos = begin + len;
+#endif
+ scd = scsi_devices;
+ while (scd) {
+ if (scd->host == HBA_ptr) {
+ proc_print_scsidevice(scd, buffer, &size, len);
+ len += size;
+ pos = begin + len;
+
+ if (pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+ if (pos > offset + length)
+ goto stop_output;
+ }
+ scd = scd->next;
+ }
+ HBA_ptr = HBA_ptr->next;
+ }
+
+ stop_output:
+ *start=buffer+(offset-begin); /* Start of wanted data */
+ len-=(offset-begin); /* Start slop */
+ if(len>length)
+ len = length; /* Ending slop */
+ return (len);
+ }
+
+ /*
+ * Usage: echo "scsi singledevice 0 1 2 3" >/proc/scsi/scsi
+ * with "0 1 2 3" replaced by your "Host Channel Id Lun".
+ * Consider this feature BETA.
+ * CAUTION: This is not for hotplugging your peripherals. As
+ * SCSI was not designed for this you could damage your
+ * hardware !
+ * However perhaps it is legal to switch on an
+ * already connected device. It is perhaps not
+ * guaranteed this device doesn't corrupt an ongoing data transfer.
+ */
+ if(!buffer || length < 25 || strncmp("scsi", buffer, 4))
+ return(-EINVAL);
+
+ if(!strncmp("singledevice", buffer + 5, 12)) {
+ p = buffer + 17;
+
+ for(i=0; i<4; i++) {
+ p++;
+ parameter[i] = simple_strtoul(p, &p, 0);
+ }
+ printk("scsi singledevice %d %d %d %d\n", parameter[0], parameter[1],
+ parameter[2], parameter[3]);
+
+ while(scd && (scd->host->host_no != parameter[0]
+ || scd->channel != parameter[1]
+ || scd->id != parameter[2]
+ || scd->lun != parameter[3])) {
+ scd = scd->next;
+ }
+ if(scd)
+ return(-ENOSYS); /* We do not yet support unplugging */
+ while(HBA_ptr && HBA_ptr->host_no != parameter[0])
+ HBA_ptr = HBA_ptr->next;
+
+ if(!HBA_ptr)
+ return(-ENXIO);
+
+ scan_scsis (HBA_ptr, 1, parameter[1], parameter[2], parameter[3]);
+ return(length);
+ }
+ return(-EINVAL);
+}
+#endif
+
+/*
+ * Go through the device list and recompute the most appropriate size
+ * for the dma pool. Then grab more memory (as required).
+ */
+static void resize_dma_pool(void)
+{
+ int i;
+ unsigned long size;
+ struct Scsi_Host * shpnt;
+ struct Scsi_Host * host = NULL;
+ Scsi_Device * SDpnt;
+ unsigned long flags;
+ FreeSectorBitmap * new_dma_malloc_freelist = NULL;
+ unsigned int new_dma_sectors = 0;
+ unsigned int new_need_isa_buffer = 0;
+ unsigned char ** new_dma_malloc_pages = NULL;
+
+ if( !scsi_devices )
+ {
+ /*
+ * Free up the DMA pool.
+ */
+ if( dma_free_sectors != dma_sectors )
+ panic("SCSI DMA pool memory leak %d %d\n",dma_free_sectors,dma_sectors);
+
+ for(i=0; i < dma_sectors / SECTORS_PER_PAGE; i++)
+ scsi_init_free(dma_malloc_pages[i], PAGE_SIZE);
+ if (dma_malloc_pages)
+ scsi_init_free((char *) dma_malloc_pages,
+ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages));
+ dma_malloc_pages = NULL;
+ if (dma_malloc_freelist)
+ scsi_init_free((char *) dma_malloc_freelist,
+ (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_freelist));
+ dma_malloc_freelist = NULL;
+ dma_sectors = 0;
+ dma_free_sectors = 0;
+ return;
+ }
+ /* Next, check to see if we need to extend the DMA buffer pool */
+
+ new_dma_sectors = 2*SECTORS_PER_PAGE; /* Base value we use */
+
+ if (high_memory-1 > ISA_DMA_THRESHOLD)
+ scsi_need_isa_bounce_buffers = 1;
+ else
+ scsi_need_isa_bounce_buffers = 0;
+
+ if (scsi_devicelist)
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ new_dma_sectors += SECTORS_PER_PAGE; /* Increment for each host */
+
+ for (SDpnt=scsi_devices; SDpnt; SDpnt = SDpnt->next) {
+ host = SDpnt->host;
+
+ if(SDpnt->type != TYPE_TAPE)
+ new_dma_sectors += ((host->sg_tablesize *
+ sizeof(struct scatterlist) + 511) >> 9) *
+ host->cmd_per_lun;
+
+ if(host->unchecked_isa_dma &&
+ scsi_need_isa_bounce_buffers &&
+ SDpnt->type != TYPE_TAPE) {
+ new_dma_sectors += (PAGE_SIZE >> 9) * host->sg_tablesize *
+ host->cmd_per_lun;
+ new_need_isa_buffer++;
+ }
+ }
+
+ /* limit DMA memory to 32MB: */
+ new_dma_sectors = (new_dma_sectors + 15) & 0xfff0;
+
+ /*
+ * We never shrink the buffers - this leads to
+ * race conditions that I would rather not even think
+ * about right now.
+ */
+ if( new_dma_sectors < dma_sectors )
+ new_dma_sectors = dma_sectors;
+
+ if (new_dma_sectors)
+ {
+ size = (new_dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ new_dma_malloc_freelist = (FreeSectorBitmap *) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(new_dma_malloc_freelist, 0, size);
+
+ size = (new_dma_sectors / SECTORS_PER_PAGE)*sizeof(*new_dma_malloc_pages);
+ new_dma_malloc_pages = (unsigned char **) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(new_dma_malloc_pages, 0, size);
+ }
+
+ /*
+ * If we need more buffers, expand the list.
+ */
+ if( new_dma_sectors > dma_sectors ) {
+ for(i=dma_sectors / SECTORS_PER_PAGE; i< new_dma_sectors / SECTORS_PER_PAGE; i++)
+ new_dma_malloc_pages[i] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+ }
+
+ /* When we dick with the actual DMA list, we need to
+ * protect things
+ */
+ save_flags(flags);
+ cli();
+ if (dma_malloc_freelist)
+ {
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ memcpy(new_dma_malloc_freelist, dma_malloc_freelist, size);
+ scsi_init_free((char *) dma_malloc_freelist, size);
+ }
+ dma_malloc_freelist = new_dma_malloc_freelist;
+
+ if (dma_malloc_pages)
+ {
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages);
+ memcpy(new_dma_malloc_pages, dma_malloc_pages, size);
+ scsi_init_free((char *) dma_malloc_pages, size);
+ }
+
+ dma_free_sectors += new_dma_sectors - dma_sectors;
+ dma_malloc_pages = new_dma_malloc_pages;
+ dma_sectors = new_dma_sectors;
+ need_isa_buffer = new_need_isa_buffer;
+ restore_flags(flags);
+}
+
+#ifdef CONFIG_MODULES /* a big #ifdef block... */
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a low level scsi driver to the system.
+ */
+static int scsi_register_host(Scsi_Host_Template * tpnt)
+{
+ int pcount;
+ struct Scsi_Host * shpnt;
+ Scsi_Device * SDpnt;
+ struct Scsi_Device_Template * sdtpnt;
+ const char * name;
+
+ if (tpnt->next || !tpnt->detect) return 1;/* Must be already loaded, or
+ * no detect routine available
+ */
+ pcount = next_scsi_host;
+ if ((tpnt->present = tpnt->detect(tpnt)))
+ {
+ if(pcount == next_scsi_host) {
+ if(tpnt->present > 1) {
+ printk("Failure to register low-level scsi driver");
+ scsi_unregister_host(tpnt);
+ return 1;
+ }
+ /* The low-level driver failed to register a driver. We
+ * can do this now.
+ */
+ scsi_register(tpnt,0);
+ }
+ tpnt->next = scsi_hosts; /* Add to the linked list */
+ scsi_hosts = tpnt;
+
+ /* Add the new driver to /proc/scsi */
+#if CONFIG_PROC_FS
+ build_proc_dir_entries(tpnt);
+#endif
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt)
+ {
+ if(tpnt->info)
+ name = tpnt->info(shpnt);
+ else
+ name = tpnt->name;
+ printk ("scsi%d : %s\n", /* And print a little message */
+ shpnt->host_no, name);
+ }
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+ scsi_make_blocked_list();
+
+ /* The next step is to call scan_scsis here. This generates the
+ * Scsi_Devices entries
+ */
+
+ for(shpnt=scsi_hostlist; shpnt; shpnt = shpnt->next)
+ if(shpnt->hostt == tpnt) scan_scsis(shpnt,0,0,0,0);
+
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->init && sdtpnt->dev_noticed) (*sdtpnt->init)();
+
+ /* Next we create the Scsi_Cmnd structures for this host */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(SDpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->attach) (*sdtpnt->attach)(SDpnt);
+ if(SDpnt->attached) scsi_build_commandblocks(SDpnt);
+ }
+
+ /*
+ * Now that we have all of the devices, resize the DMA pool,
+ * as required. */
+ resize_dma_pool();
+
+
+ /* This does any final handling that is required. */
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->finish && sdtpnt->nr_dev)
+ (*sdtpnt->finish)();
+ }
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+/*
+ * Similarly, this entry point should be called by a loadable module if it
+ * is trying to remove a low level scsi driver from the system.
+ */
+static void scsi_unregister_host(Scsi_Host_Template * tpnt)
+{
+ Scsi_Host_Template * SHT, *SHTp;
+ Scsi_Device *sdpnt, * sdppnt, * sdpnt1;
+ Scsi_Cmnd * SCpnt;
+ unsigned long flags;
+ struct Scsi_Device_Template * sdtpnt;
+ struct Scsi_Host * shpnt, *sh1;
+ int pcount;
+
+ /* First verify that this host adapter is completely free with no pending
+ * commands */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt && sdpnt->host->hostt->usage_count
+ && *sdpnt->host->hostt->usage_count) return;
+
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ {
+ if (shpnt->hostt != tpnt) continue;
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ save_flags(flags);
+ cli();
+ if(SCpnt->request.rq_status != RQ_INACTIVE) {
+ restore_flags(flags);
+ for(SCpnt = shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ if(SCpnt->request.rq_status == RQ_SCSI_DISCONNECTING)
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ printk("Device busy???\n");
+ return;
+ }
+ SCpnt->request.rq_status = RQ_SCSI_DISCONNECTING; /* Mark as busy */
+ restore_flags(flags);
+ }
+ }
+ /* Next we detach the high level drivers from the Scsi_Device structures */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ {
+ for(sdtpnt = scsi_devicelist; sdtpnt; sdtpnt = sdtpnt->next)
+ if(sdtpnt->detach) (*sdtpnt->detach)(sdpnt);
+ /* If something still attached, punt */
+ if (sdpnt->attached) {
+ printk("Attached usage count = %d\n", sdpnt->attached);
+ return;
+ }
+ }
+
+ /* Next we free up the Scsi_Cmnd structures for this host */
+
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt->next)
+ if(sdpnt->host->hostt == tpnt)
+ while (sdpnt->host->host_queue) {
+ SCpnt = sdpnt->host->host_queue->next;
+ scsi_init_free((char *) sdpnt->host->host_queue, sizeof(Scsi_Cmnd));
+ sdpnt->host->host_queue = SCpnt;
+ if (SCpnt) SCpnt->prev = NULL;
+ sdpnt->has_cmdblocks = 0;
+ }
+
+ /* Next free up the Scsi_Device structures for this host */
+
+ sdppnt = NULL;
+ for(sdpnt = scsi_devices; sdpnt; sdpnt = sdpnt1)
+ {
+ sdpnt1 = sdpnt->next;
+ if (sdpnt->host->hostt == tpnt) {
+ if (sdppnt)
+ sdppnt->next = sdpnt->next;
+ else
+ scsi_devices = sdpnt->next;
+ scsi_init_free((char *) sdpnt, sizeof (Scsi_Device));
+ } else
+ sdppnt = sdpnt;
+ }
+
+ /* Next we go through and remove the instances of the individual hosts
+ * that were detected */
+
+ shpnt = scsi_hostlist;
+ while(shpnt) {
+ sh1 = shpnt->next;
+ if(shpnt->hostt == tpnt) {
+ if(shpnt->loaded_as_module) {
+ pcount = next_scsi_host;
+ /* Remove the /proc/scsi directory entry */
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(tpnt->proc_dir,
+ shpnt->host_no + PROC_SCSI_FILE);
+#endif
+ if(tpnt->release)
+ (*tpnt->release)(shpnt);
+ else {
+ /* This is the default case for the release function.
+ * It should do the right thing for most correctly
+ * written host adapters.
+ */
+ if (shpnt->irq) free_irq(shpnt->irq);
+ if (shpnt->dma_channel != 0xff) free_dma(shpnt->dma_channel);
+ if (shpnt->io_port && shpnt->n_io_port)
+ release_region(shpnt->io_port, shpnt->n_io_port);
+ }
+ if(pcount == next_scsi_host) scsi_unregister(shpnt);
+ tpnt->present--;
+ }
+ }
+ shpnt = sh1;
+ }
+
+ /*
+ * If there are absolutely no more hosts left, it is safe
+ * to completely nuke the DMA pool. The resize operation will
+ * do the right thing and free everything.
+ */
+ if( !scsi_devices )
+ resize_dma_pool();
+
+ printk ("scsi : %d host%s.\n", next_scsi_host,
+ (next_scsi_host == 1) ? "" : "s");
+
+#if defined(USE_STATIC_SCSI_MEMORY)
+ printk ("SCSI memory: total %ldKb, used %ldKb, free %ldKb.\n",
+ (scsi_memory_upper_value - scsi_memory_lower_value) / 1024,
+ (scsi_init_memory_start - scsi_memory_lower_value) / 1024,
+ (scsi_memory_upper_value - scsi_init_memory_start) / 1024);
+#endif
+
+ scsi_make_blocked_list();
+
+ /* There were some hosts that were loaded at boot time, so we cannot
+ do any more than this */
+ if (tpnt->present) return;
+
+ /* OK, this is the very last step. Remove this host adapter from the
+ linked list. */
+ for(SHTp=NULL, SHT=scsi_hosts; SHT; SHTp=SHT, SHT=SHT->next)
+ if(SHT == tpnt) {
+ if(SHTp)
+ SHTp->next = SHT->next;
+ else
+ scsi_hosts = SHT->next;
+ SHT->next = NULL;
+ break;
+ }
+
+ /* Rebuild the /proc/scsi directory entries */
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(tpnt->proc_dir, tpnt->proc_dir->low_ino);
+#endif
+ MOD_DEC_USE_COUNT;
+}
+
+/*
+ * This entry point should be called by a loadable module if it is trying
+ * add a high level scsi driver to the system.
+ */
+static int scsi_register_device_module(struct Scsi_Device_Template * tpnt)
+{
+ Scsi_Device * SDpnt;
+
+ if (tpnt->next) return 1;
+
+ scsi_register_device(tpnt);
+ /*
+ * First scan the devices that we know about, and see if we notice them.
+ */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ if(tpnt->detect) SDpnt->attached += (*tpnt->detect)(SDpnt);
+
+ /*
+ * If any of the devices would match this driver, then perform the
+ * init function.
+ */
+ if(tpnt->init && tpnt->dev_noticed)
+ if ((*tpnt->init)()) return 1;
+
+ /*
+ * Now actually connect the devices to the new driver.
+ */
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ {
+ if(tpnt->attach) (*tpnt->attach)(SDpnt);
+ /*
+ * If this driver attached to the device, and we no longer
+ * have anything attached, release the scso command blocks.
+ */
+ if(SDpnt->attached && SDpnt->has_cmdblocks == 0)
+ scsi_build_commandblocks(SDpnt);
+ }
+
+ /*
+ * This does any final handling that is required.
+ */
+ if(tpnt->finish && tpnt->nr_dev) (*tpnt->finish)();
+ MOD_INC_USE_COUNT;
+ return 0;
+}
+
+static int scsi_unregister_device(struct Scsi_Device_Template * tpnt)
+{
+ Scsi_Device * SDpnt;
+ Scsi_Cmnd * SCpnt;
+ struct Scsi_Device_Template * spnt;
+ struct Scsi_Device_Template * prev_spnt;
+
+ /*
+ * If we are busy, this is not going to fly.
+ */
+ if( *tpnt->usage_count != 0) return 0;
+ /*
+ * Next, detach the devices from the driver.
+ */
+
+ for(SDpnt = scsi_devices; SDpnt; SDpnt = SDpnt->next)
+ {
+ if(tpnt->detach) (*tpnt->detach)(SDpnt);
+ if(SDpnt->attached == 0)
+ {
+ /*
+ * Nobody is using this device any more. Free all of the
+ * command structures.
+ */
+ for(SCpnt = SDpnt->host->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ if(SCpnt->device == SDpnt)
+ {
+ if(SCpnt->prev != NULL)
+ SCpnt->prev->next = SCpnt->next;
+ if(SCpnt->next != NULL)
+ SCpnt->next->prev = SCpnt->prev;
+ if(SCpnt == SDpnt->host->host_queue)
+ SDpnt->host->host_queue = SCpnt->next;
+ scsi_init_free((char *) SCpnt, sizeof(*SCpnt));
+ }
+ }
+ SDpnt->has_cmdblocks = 0;
+ }
+ }
+ /*
+ * Extract the template from the linked list.
+ */
+ spnt = scsi_devicelist;
+ prev_spnt = NULL;
+ while(spnt != tpnt)
+ {
+ prev_spnt = spnt;
+ spnt = spnt->next;
+ }
+ if(prev_spnt == NULL)
+ scsi_devicelist = tpnt->next;
+ else
+ prev_spnt->next = spnt->next;
+
+ MOD_DEC_USE_COUNT;
+ /*
+ * Final cleanup for the driver is done in the driver sources in the
+ * cleanup function.
+ */
+ return 0;
+}
+
+
+int scsi_register_module(int module_type, void * ptr)
+{
+ switch(module_type){
+ case MODULE_SCSI_HA:
+ return scsi_register_host((Scsi_Host_Template *) ptr);
+
+ /* Load upper level device handler of some kind */
+ case MODULE_SCSI_DEV:
+ return scsi_register_device_module((struct Scsi_Device_Template *) ptr);
+ /* The rest of these are not yet implemented */
+
+ /* Load constants.o */
+ case MODULE_SCSI_CONST:
+
+ /* Load specialized ioctl handler for some device. Intended for
+ * cdroms that have non-SCSI2 audio command sets. */
+ case MODULE_SCSI_IOCTL:
+
+ default:
+ return 1;
+ }
+}
+
+void scsi_unregister_module(int module_type, void * ptr)
+{
+ switch(module_type) {
+ case MODULE_SCSI_HA:
+ scsi_unregister_host((Scsi_Host_Template *) ptr);
+ break;
+ case MODULE_SCSI_DEV:
+ scsi_unregister_device((struct Scsi_Device_Template *) ptr);
+ break;
+ /* The rest of these are not yet implemented. */
+ case MODULE_SCSI_CONST:
+ case MODULE_SCSI_IOCTL:
+ break;
+ default:
+ }
+ return;
+}
+
+#endif /* CONFIG_MODULES */
+
+#ifdef DEBUG_TIMEOUT
+static void
+scsi_dump_status(void)
+{
+ int i;
+ struct Scsi_Host * shpnt;
+ Scsi_Cmnd * SCpnt;
+ printk("Dump of scsi parameters:\n");
+ i = 0;
+ for(shpnt = scsi_hostlist; shpnt; shpnt = shpnt->next)
+ for(SCpnt=shpnt->host_queue; SCpnt; SCpnt = SCpnt->next)
+ {
+ /* (0) 0:0:0:0 (802 123434 8 8 0) (3 3 2) (%d %d %d) %d %x */
+ printk("(%d) %d:%d:%d:%d (%s %ld %ld %ld %d) (%d %d %x) (%d %d %d) %x %x %x\n",
+ i++, SCpnt->host->host_no,
+ SCpnt->channel,
+ SCpnt->target,
+ SCpnt->lun,
+ kdevname(SCpnt->request.rq_dev),
+ SCpnt->request.sector,
+ SCpnt->request.nr_sectors,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->use_sg,
+ SCpnt->retries,
+ SCpnt->allowed,
+ SCpnt->flags,
+ SCpnt->timeout_per_command,
+ SCpnt->timeout,
+ SCpnt->internal_timeout,
+ SCpnt->cmnd[0],
+ SCpnt->sense_buffer[2],
+ SCpnt->result);
+ }
+ printk("wait_for_request = %p\n", wait_for_request);
+ /* Now dump the request lists for each block device */
+ printk("Dump of pending block device requests\n");
+ for(i=0; i<MAX_BLKDEV; i++)
+ if(blk_dev[i].current_request)
+ {
+ struct request * req;
+ printk("%d: ", i);
+ req = blk_dev[i].current_request;
+ while(req) {
+ printk("(%s %d %ld %ld %ld) ",
+ kdevname(req->rq_dev),
+ req->cmd,
+ req->sector,
+ req->nr_sectors,
+ req->current_nr_sectors);
+ req = req->next;
+ }
+ printk("\n");
+ }
+}
+#endif
+
+#ifdef MODULE
+
+int init_module(void) {
+ unsigned long size;
+
+ /*
+ * This makes /proc/scsi visible.
+ */
+ dispatch_scsi_info_ptr = dispatch_scsi_info;
+
+ timer_table[SCSI_TIMER].fn = scsi_main_timeout;
+ timer_table[SCSI_TIMER].expires = 0;
+ register_symtab(&scsi_symbol_table);
+ scsi_loadable_module_flag = 1;
+
+ /* Register the /proc/scsi/scsi entry */
+#if CONFIG_PROC_FS
+ proc_scsi_register(0, &proc_scsi_scsi);
+#endif
+
+
+ dma_sectors = PAGE_SIZE / SECTOR_SIZE;
+ dma_free_sectors= dma_sectors;
+ /*
+ * Set up a minimal DMA buffer list - this will be used during scan_scsis
+ * in some cases.
+ */
+
+ /* One bit per sector to indicate free/busy */
+ size = (dma_sectors / SECTORS_PER_PAGE)*sizeof(FreeSectorBitmap);
+ dma_malloc_freelist = (unsigned char *) scsi_init_malloc(size, GFP_ATOMIC);
+ memset(dma_malloc_freelist, 0, size);
+
+ /* One pointer per page for the page list */
+ dma_malloc_pages = (unsigned char **)
+ scsi_init_malloc((dma_sectors / SECTORS_PER_PAGE)*sizeof(*dma_malloc_pages), GFP_ATOMIC);
+ dma_malloc_pages[0] = (unsigned char *)
+ scsi_init_malloc(PAGE_SIZE, GFP_ATOMIC | GFP_DMA);
+ return 0;
+}
+
+void cleanup_module( void)
+{
+#if CONFIG_PROC_FS
+ proc_scsi_unregister(0, PROC_SCSI_SCSI);
+#endif
+
+ /* No, we're not here anymore. Don't show the /proc/scsi files. */
+ dispatch_scsi_info_ptr = 0L;
+
+ /*
+ * Free up the DMA pool.
+ */
+ resize_dma_pool();
+
+ timer_table[SCSI_TIMER].fn = NULL;
+ timer_table[SCSI_TIMER].expires = 0;
+}
+#endif /* MODULE */
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/scsi.h b/i386/i386at/gpl/linux/scsi/scsi.h
new file mode 100644
index 00000000..fefe1c73
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi.h
@@ -0,0 +1,618 @@
+/*
+ * scsi.h Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ * generic SCSI package header file by
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+
+#ifndef _SCSI_H
+#define _SCSI_H
+
+/*
+ * Some of the public constants are being moved to this file.
+ * We include it here so that what came from where is transparent.
+ */
+#include <linux/scsi.h>
+
+
+/*
+ * Some defs, in case these are not defined elsewhere.
+ */
+#ifndef TRUE
+# define TRUE 1
+#endif
+#ifndef FALSE
+# define FALSE 0
+#endif
+
+#ifdef MACH
+#ifndef LINUX_SCSI_DEBUG
+#undef DEBUG
+#endif
+#endif
+
+extern void scsi_make_blocked_list(void);
+extern volatile int in_scan_scsis;
+extern const unsigned char scsi_command_size[8];
+#define COMMAND_SIZE(opcode) scsi_command_size[((opcode) >> 5) & 7]
+
+#define IDENTIFY_BASE 0x80
+#define IDENTIFY(can_disconnect, lun) (IDENTIFY_BASE |\
+ ((can_disconnect) ? 0x40 : 0) |\
+ ((lun) & 0x07))
+
+
+
+/*
+ * the return of the status word will be in the following format :
+ * The low byte is the status returned by the SCSI command,
+ * with vendor specific bits masked.
+ *
+ * The next byte is the message which followed the SCSI status.
+ * This allows a stos to be used, since the Intel is a little
+ * endian machine.
+ *
+ * The final byte is a host return code, which is one of the following.
+ *
+ * IE
+ * lsb msb
+ * status msg host code
+ *
+ * Our errors returned by OUR driver, NOT SCSI message. Or'd with
+ * SCSI message passed back to driver <IF any>.
+ */
+
+
+#define DID_OK 0x00 /* NO error */
+#define DID_NO_CONNECT 0x01 /* Couldn't connect before timeout period */
+#define DID_BUS_BUSY 0x02 /* BUS stayed busy through time out period */
+#define DID_TIME_OUT 0x03 /* TIMED OUT for other reason */
+#define DID_BAD_TARGET 0x04 /* BAD target. */
+#define DID_ABORT 0x05 /* Told to abort for some other reason */
+#define DID_PARITY 0x06 /* Parity error */
+#define DID_ERROR 0x07 /* Internal error */
+#define DID_RESET 0x08 /* Reset by somebody. */
+#define DID_BAD_INTR 0x09 /* Got an interrupt we weren't expecting. */
+#define DRIVER_OK 0x00 /* Driver status */
+
+/*
+ * These indicate the error that occurred, and what is available.
+ */
+
+#define DRIVER_BUSY 0x01
+#define DRIVER_SOFT 0x02
+#define DRIVER_MEDIA 0x03
+#define DRIVER_ERROR 0x04
+
+#define DRIVER_INVALID 0x05
+#define DRIVER_TIMEOUT 0x06
+#define DRIVER_HARD 0x07
+#define DRIVER_SENSE 0x08
+
+#define SUGGEST_RETRY 0x10
+#define SUGGEST_ABORT 0x20
+#define SUGGEST_REMAP 0x30
+#define SUGGEST_DIE 0x40
+#define SUGGEST_SENSE 0x80
+#define SUGGEST_IS_OK 0xff
+
+#define DRIVER_MASK 0x0f
+#define SUGGEST_MASK 0xf0
+
+#define MAX_COMMAND_SIZE 12
+
+/*
+ * SCSI command sets
+ */
+
+#define SCSI_UNKNOWN 0
+#define SCSI_1 1
+#define SCSI_1_CCS 2
+#define SCSI_2 3
+
+/*
+ * Every SCSI command starts with a one byte OP-code.
+ * The next byte's high three bits are the LUN of the
+ * device. Any multi-byte quantities are stored high byte
+ * first, and may have a 5 bit MSB in the same byte
+ * as the LUN.
+ */
+
+/*
+ * Manufacturers list
+ */
+
+#define SCSI_MAN_UNKNOWN 0
+#define SCSI_MAN_NEC 1
+#define SCSI_MAN_TOSHIBA 2
+#define SCSI_MAN_NEC_OLDCDR 3
+#define SCSI_MAN_SONY 4
+#define SCSI_MAN_PIONEER 5
+
+/*
+ * As the scsi do command functions are intelligent, and may need to
+ * redo a command, we need to keep track of the last command
+ * executed on each one.
+ */
+
+#define WAS_RESET 0x01
+#define WAS_TIMEDOUT 0x02
+#define WAS_SENSE 0x04
+#define IS_RESETTING 0x08
+#define IS_ABORTING 0x10
+#define ASKED_FOR_SENSE 0x20
+
+/*
+ * The scsi_device struct contains what we know about each given scsi
+ * device.
+ */
+
+typedef struct scsi_device {
+ struct scsi_device * next; /* Used for linked list */
+
+ unsigned char id, lun, channel;
+
+ unsigned int manufacturer; /* Manufacturer of device, for using
+ * vendor-specific cmd's */
+ int attached; /* # of high level drivers attached to
+ * this */
+ int access_count; /* Count of open channels/mounts */
+ struct wait_queue * device_wait;/* Used to wait if device is busy */
+ struct Scsi_Host * host;
+ void (*scsi_request_fn)(void); /* Used to jumpstart things after an
+ * ioctl */
+ void *hostdata; /* available to low-level driver */
+ char type;
+ char scsi_level;
+ char vendor[8], model[16], rev[4];
+ unsigned char current_tag; /* current tag */
+ unsigned char sync_min_period; /* Not less than this period */
+ unsigned char sync_max_offset; /* Not greater than this offset */
+
+ unsigned writeable:1;
+ unsigned removable:1;
+ unsigned random:1;
+ unsigned has_cmdblocks:1;
+ unsigned changed:1; /* Data invalid due to media change */
+ unsigned busy:1; /* Used to prevent races */
+ unsigned lockable:1; /* Able to prevent media removal */
+ unsigned borken:1; /* Tell the Seagate driver to be
+ * painfully slow on this device */
+ unsigned tagged_supported:1; /* Supports SCSI-II tagged queuing */
+ unsigned tagged_queue:1; /* SCSI-II tagged queuing enabled */
+ unsigned disconnect:1; /* can disconnect */
+ unsigned soft_reset:1; /* Uses soft reset option */
+ unsigned sync:1; /* Negotiate for sync transfers */
+ unsigned single_lun:1; /* Indicates we should only allow I/O to
+ one of the luns for the device at a time. */
+ unsigned was_reset:1; /* There was a bus reset on the bus for this
+ device */
+ unsigned expecting_cc_ua:1; /* Expecting a CHECK_CONDITION/UNIT_ATTN
+ because we did a bus reset. */
+} Scsi_Device;
+
+/*
+ * Use these to separate status msg and our bytes
+ */
+
+#define status_byte(result) (((result) >> 1) & 0xf)
+#define msg_byte(result) (((result) >> 8) & 0xff)
+#define host_byte(result) (((result) >> 16) & 0xff)
+#define driver_byte(result) (((result) >> 24) & 0xff)
+#define suggestion(result) (driver_byte(result) & SUGGEST_MASK)
+
+#define sense_class(sense) (((sense) >> 4) & 0x7)
+#define sense_error(sense) ((sense) & 0xf)
+#define sense_valid(sense) ((sense) & 0x80);
+
+/*
+ * These are the SCSI devices available on the system.
+ */
+
+extern Scsi_Device * scsi_devices;
+
+/*
+ * Initializes all SCSI devices. This scans all scsi busses.
+ */
+
+extern int scsi_dev_init (void);
+
+struct scatterlist {
+ char * address; /* Location data is to be transferred to */
+ char * alt_address; /* Location of actual if address is a
+ * dma indirect buffer. NULL otherwise */
+ unsigned int length;
+};
+
+#ifdef __alpha__
+# define ISA_DMA_THRESHOLD (~0UL)
+#else
+# define ISA_DMA_THRESHOLD (0x00ffffff)
+#endif
+#define CONTIGUOUS_BUFFERS(X,Y) ((X->b_data+X->b_size) == Y->b_data)
+
+
+/*
+ * These are the return codes for the abort and reset functions. The mid-level
+ * code uses these to decide what to do next. Each of the low level abort
+ * and reset functions must correctly indicate what it has done.
+ * The descriptions are written from the point of view of the mid-level code,
+ * so that the return code is telling the mid-level drivers exactly what
+ * the low level driver has already done, and what remains to be done.
+ */
+
+/* We did not do anything.
+ * Wait some more for this command to complete, and if this does not work,
+ * try something more serious. */
+#define SCSI_ABORT_SNOOZE 0
+
+/* This means that we were able to abort the command. We have already
+ * called the mid-level done function, and do not expect an interrupt that
+ * will lead to another call to the mid-level done function for this command */
+#define SCSI_ABORT_SUCCESS 1
+
+/* We called for an abort of this command, and we should get an interrupt
+ * when this succeeds. Thus we should not restore the timer for this
+ * command in the mid-level abort function. */
+#define SCSI_ABORT_PENDING 2
+
+/* Unable to abort - command is currently on the bus. Grin and bear it. */
+#define SCSI_ABORT_BUSY 3
+
+/* The command is not active in the low level code. Command probably
+ * finished. */
+#define SCSI_ABORT_NOT_RUNNING 4
+
+/* Something went wrong. The low level driver will indicate the correct
+ * error condition when it calls scsi_done, so the mid-level abort function
+ * can simply wait until this comes through */
+#define SCSI_ABORT_ERROR 5
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * Anyway, just wait a little more for the command in question, and hope that
+ * it eventually finishes. If it never finishes, the SCSI device could
+ * hang, so use this with caution. */
+#define SCSI_RESET_SNOOZE 0
+
+/* We do not know how to reset the bus, or we do not want to. Bummer.
+ * We have given up on this ever completing. The mid-level code will
+ * request sense information to decide how to proceed from here. */
+#define SCSI_RESET_PUNT 1
+
+/* This means that we were able to reset the bus. We have restarted all of
+ * the commands that should be restarted, and we should be able to continue
+ * on normally from here. We do not expect any interrupts that will return
+ * DID_RESET to any of the other commands in the host_queue, and the mid-level
+ * code does not need to do anything special to keep the commands alive.
+ * If a hard reset was performed then all outstanding commands on the
+ * bus have been restarted. */
+#define SCSI_RESET_SUCCESS 2
+
+/* We called for a reset of this bus, and we should get an interrupt
+ * when this succeeds. Each command should get its own status
+ * passed up to scsi_done, but this has not happened yet.
+ * If a hard reset was performed, then we expect an interrupt
+ * for *each* of the outstanding commands that will have the
+ * effect of restarting the commands.
+ */
+#define SCSI_RESET_PENDING 3
+
+/* We did a reset, but do not expect an interrupt to signal DID_RESET.
+ * This tells the upper level code to request the sense info, and this
+ * should keep the command alive. */
+#define SCSI_RESET_WAKEUP 4
+
+/* Something went wrong, and we do not know how to fix it. */
+#define SCSI_RESET_ERROR 5
+
+/*
+ * This is a bitmask that is ored with one of the above codes.
+ * It tells the mid-level code that we did a hard reset.
+ */
+#define SCSI_RESET_BUS_RESET 0x100
+/*
+ * Used to mask off bits and to obtain the basic action that was
+ * performed.
+ */
+#define SCSI_RESET_ACTION 0xff
+
+void * scsi_malloc(unsigned int);
+int scsi_free(void *, unsigned int);
+extern unsigned int dma_free_sectors; /* How much room do we have left */
+extern unsigned int need_isa_buffer; /* True if some devices need indirection
+ * buffers */
+
+/*
+ * The Scsi_Cmnd structure is used by scsi.c internally, and for communication
+ * with low level drivers that support multiple outstanding commands.
+ */
+typedef struct scsi_pointer {
+ char * ptr; /* data pointer */
+ int this_residual; /* left in this buffer */
+ struct scatterlist *buffer; /* which buffer */
+ int buffers_residual; /* how many buffers left */
+
+ volatile int Status;
+ volatile int Message;
+ volatile int have_data_in;
+ volatile int sent_command;
+ volatile int phase;
+} Scsi_Pointer;
+
+typedef struct scsi_cmnd {
+ struct Scsi_Host * host;
+ Scsi_Device * device;
+ unsigned char target, lun, channel;
+ unsigned char cmd_len;
+ unsigned char old_cmd_len;
+ struct scsi_cmnd *next, *prev;
+
+ /* These elements define the operation we are about to perform */
+ unsigned char cmnd[12];
+ unsigned request_bufflen; /* Actual request size */
+
+ void * request_buffer; /* Actual requested buffer */
+
+ /* These elements define the operation we ultimately want to perform */
+ unsigned char data_cmnd[12];
+ unsigned short old_use_sg; /* We save use_sg here when requesting
+ * sense info */
+ unsigned short use_sg; /* Number of pieces of scatter-gather */
+ unsigned short sglist_len; /* size of malloc'd scatter-gather list */
+ unsigned short abort_reason;/* If the mid-level code requests an
+ * abort, this is the reason. */
+ unsigned bufflen; /* Size of data buffer */
+ void *buffer; /* Data buffer */
+
+ unsigned underflow; /* Return error if less than this amount is
+ * transfered */
+
+ unsigned transfersize; /* How much we are guaranteed to transfer with
+ * each SCSI transfer (ie, between disconnect /
+ * reconnects. Probably == sector size */
+
+
+ struct request request; /* A copy of the command we are working on */
+
+ unsigned char sense_buffer[16]; /* Sense for this command, if needed */
+
+
+ int retries;
+ int allowed;
+ int timeout_per_command, timeout_total, timeout;
+
+ /*
+ * We handle the timeout differently if it happens when a reset,
+ * abort, etc are in process.
+ */
+ unsigned volatile char internal_timeout;
+
+ unsigned flags;
+
+ /* These variables are for the cdrom only. Once we have variable size
+ * buffers in the buffer cache, they will go away. */
+ int this_count;
+ /* End of special cdrom variables */
+
+ /* Low-level done function - can be used by low-level driver to point
+ * to completion function. Not used by mid/upper level code. */
+ void (*scsi_done)(struct scsi_cmnd *);
+ void (*done)(struct scsi_cmnd *); /* Mid-level done function */
+
+ /*
+ * The following fields can be written to by the host specific code.
+ * Everything else should be left alone.
+ */
+
+ Scsi_Pointer SCp; /* Scratchpad used by some host adapters */
+
+ unsigned char * host_scribble; /* The host adapter is allowed to
+ * call scsi_malloc and get some memory
+ * and hang it here. The host adapter
+ * is also expected to call scsi_free
+ * to release this memory. (The memory
+ * obtained by scsi_malloc is guaranteed
+ * to be at an address < 16Mb). */
+
+ int result; /* Status code from lower level driver */
+
+ unsigned char tag; /* SCSI-II queued command tag */
+ unsigned long pid; /* Process ID, starts at 0 */
+} Scsi_Cmnd;
+
+/*
+ * scsi_abort aborts the current command that is executing on host host.
+ * The error code, if non zero is returned in the host byte, otherwise
+ * DID_ABORT is returned in the hostbyte.
+ */
+
+extern int scsi_abort (Scsi_Cmnd *, int code, int pid);
+
+extern void scsi_do_cmd (Scsi_Cmnd *, const void *cmnd ,
+ void *buffer, unsigned bufflen,
+ void (*done)(struct scsi_cmnd *),
+ int timeout, int retries);
+
+
+extern Scsi_Cmnd * allocate_device(struct request **, Scsi_Device *, int);
+
+extern Scsi_Cmnd * request_queueable(struct request *, Scsi_Device *);
+extern int scsi_reset (Scsi_Cmnd *, int);
+
+extern int max_scsi_hosts;
+
+extern void proc_print_scsidevice(Scsi_Device *, char *, int *, int);
+
+extern void print_command(unsigned char *);
+extern void print_sense(const char *, Scsi_Cmnd *);
+extern void print_driverbyte(int scsiresult);
+extern void print_hostbyte(int scsiresult);
+
+extern void scsi_mark_host_bus_reset(struct Scsi_Host *Host);
+
+#if defined(MAJOR_NR) && (MAJOR_NR != SCSI_TAPE_MAJOR)
+#include "hosts.h"
+
+static Scsi_Cmnd * end_scsi_request(Scsi_Cmnd * SCpnt, int uptodate, int sectors)
+{
+ struct request * req;
+ struct buffer_head * bh;
+
+ req = &SCpnt->request;
+ if (!uptodate) {
+ printk(DEVICE_NAME " I/O error: dev %s, sector %lu\n",
+ kdevname(req->rq_dev), req->sector);
+#ifdef MACH
+ req->errors = 1;
+ while (req->bh) {
+ bh = req->bh;
+ req->bh = bh->b_reqnext;
+ mark_buffer_uptodate(bh, 0);
+ unlock_buffer(bh);
+ }
+ goto done;
+#endif
+ }
+
+ do {
+ if ((bh = req->bh) != NULL) {
+ req->bh = bh->b_reqnext;
+ req->nr_sectors -= bh->b_size >> 9;
+ req->sector += bh->b_size >> 9;
+ bh->b_reqnext = NULL;
+ mark_buffer_uptodate(bh, uptodate);
+ unlock_buffer(bh);
+ sectors -= bh->b_size >> 9;
+ if ((bh = req->bh) != NULL) {
+ req->current_nr_sectors = bh->b_size >> 9;
+ if (req->nr_sectors < req->current_nr_sectors) {
+ req->nr_sectors = req->current_nr_sectors;
+ printk("end_scsi_request: buffer-list destroyed\n");
+ }
+ }
+ }
+ } while(sectors && bh);
+ if (req->bh){
+ req->buffer = bh->b_data;
+ return SCpnt;
+ };
+#ifdef MACH
+ req->errors = 0;
+
+done:
+#endif
+ DEVICE_OFF(req->rq_dev);
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+
+ if (SCpnt->host->block) {
+ struct Scsi_Host * next;
+
+ for (next = SCpnt->host->block; next != SCpnt->host;
+ next = next->block)
+ wake_up(&next->host_wait);
+ }
+
+ req->rq_status = RQ_INACTIVE;
+#ifndef MACH
+ wake_up(&wait_for_request);
+#endif
+ wake_up(&SCpnt->device->device_wait);
+#ifdef MACH
+ {
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ (*blk_dev[MAJOR(req->rq_dev)].request_fn)();
+ restore_flags(flags);
+ }
+#endif
+ return NULL;
+}
+
+
+/* This is just like INIT_REQUEST, but we need to be aware of the fact
+ * that an interrupt may start another request, so we run this with interrupts
+ * turned off
+ */
+#define INIT_SCSI_REQUEST \
+ if (!CURRENT) { \
+ CLEAR_INTR; \
+ restore_flags(flags); \
+ return; \
+ } \
+ if (MAJOR(CURRENT->rq_dev) != MAJOR_NR) \
+ panic(DEVICE_NAME ": request list destroyed");\
+ if (CURRENT->bh) { \
+ if (!buffer_locked(CURRENT->bh)) \
+ panic(DEVICE_NAME ": block not locked"); \
+ }
+#endif
+
+#ifdef MACH
+#define SCSI_SLEEP(QUEUE, CONDITION) { \
+ if (CONDITION) { \
+ struct wait_queue wait = { NULL, NULL}; \
+ add_wait_queue(QUEUE, &wait); \
+ for(;;) { \
+ if (CONDITION) { \
+ if (intr_count) \
+ panic("scsi: trying to call schedule() in interrupt" \
+ ", file %s, line %d.\n", __FILE__, __LINE__); \
+ schedule(); \
+ } \
+ else \
+ break; \
+ } \
+ remove_wait_queue(QUEUE, &wait);\
+ }; }
+#else /* ! MACH */
+#define SCSI_SLEEP(QUEUE, CONDITION) { \
+ if (CONDITION) { \
+ struct wait_queue wait = { current, NULL}; \
+ add_wait_queue(QUEUE, &wait); \
+ for(;;) { \
+ current->state = TASK_UNINTERRUPTIBLE; \
+ if (CONDITION) { \
+ if (intr_count) \
+ panic("scsi: trying to call schedule() in interrupt" \
+ ", file %s, line %d.\n", __FILE__, __LINE__); \
+ schedule(); \
+ } \
+ else \
+ break; \
+ } \
+ remove_wait_queue(QUEUE, &wait);\
+ current->state = TASK_RUNNING; \
+ }; }
+#endif /* ! MACH */
+
+#endif
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/scsi_debug.c b/i386/i386at/gpl/linux/scsi/scsi_debug.c
new file mode 100644
index 00000000..e98f53e2
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi_debug.c
@@ -0,0 +1,710 @@
+/* $Id: scsi_debug.c,v 1.1.1.1 1997/02/25 21:27:51 thomas Exp $
+ * linux/kernel/scsi_debug.c
+ *
+ * Copyright (C) 1992 Eric Youngdale
+ * Simulate a host adapter with 2 disks attached. Do a lot of checking
+ * to make sure that we are not getting blocks mixed up, and panic if
+ * anything out of the ordinary is seen.
+ */
+
+#include <linux/module.h>
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/errno.h>
+#include <linux/timer.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/genhd.h>
+#include <linux/fs.h>
+#include <linux/proc_fs.h>
+
+#include <asm/system.h>
+#include <asm/io.h>
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#include "sd.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_scsi_debug = {
+ PROC_SCSI_SCSI_DEBUG, 10, "scsi_debug",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+/* A few options that we want selected */
+
+/* Do not attempt to use a timer to simulate a real disk with latency */
+/* Only use this in the actual kernel, not in the simulator. */
+#define IMMEDIATE
+
+/* Skip some consistency checking. Good for benchmarking */
+#define SPEEDY
+
+/* Number of real scsi disks that will be detected ahead of time */
+static int NR_REAL=-1;
+
+#define NR_BLK_DEV 12
+#ifndef MAJOR_NR
+#define MAJOR_NR 8
+#endif
+#define START_PARTITION 4
+#define SCSI_DEBUG_TIMER 20
+/* Number of jiffies to wait before completing a command */
+#define DISK_SPEED 10
+#define CAPACITY (0x80000)
+
+static int starts[] = {4, 1000, 50000, CAPACITY, 0};
+static int npart = 0;
+
+#include "scsi_debug.h"
+#ifdef DEBUG
+#define DEB(x) x
+#else
+#define DEB(x)
+#endif
+
+#ifdef SPEEDY
+#define VERIFY1_DEBUG(RW) 1
+#define VERIFY_DEBUG(RW) 1
+#else
+
+#define VERIFY1_DEBUG(RW) \
+ if (bufflen != 1024) {printk("%d", bufflen); panic("(1)Bad bufflen");}; \
+ start = 0; \
+ if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1]; \
+ if (bh){ \
+ if (bh->b_size != 1024) panic ("Wrong bh size"); \
+ if ((bh->b_blocknr << 1) + start != block) \
+ { printk("Wrong bh block# %d %d ",bh->b_blocknr, block); \
+ panic ("Wrong bh block#"); \
+ }; \
+ if (bh->b_dev != SCpnt->request.rq_dev) \
+ panic ("Bad bh target"); \
+ };
+
+#define VERIFY_DEBUG(RW) \
+ if (bufflen != 1024 && (!SCpnt->use_sg)) {printk("%x %d\n ",bufflen, SCpnt->use_sg); panic("Bad bufflen");}; \
+ start = 0; \
+ if ((MINOR(SCpnt->request.rq_dev) & 0xf) > npart) panic ("Bad partition"); \
+ if ((MINOR(SCpnt->request.rq_dev) & 0xf) != 0) start = starts[(MINOR(SCpnt->request.rq_dev) & 0xf) - 1]; \
+ if (SCpnt->request.cmd != RW) panic ("Wrong operation"); \
+ if (SCpnt->request.sector + start != block) panic("Wrong block."); \
+ if (SCpnt->request.current_nr_sectors != 2 && (!SCpnt->use_sg)) panic ("Wrong # blocks"); \
+ if (SCpnt->request.bh){ \
+ if (SCpnt->request.bh->b_size != 1024) panic ("Wrong bh size"); \
+ if ((SCpnt->request.bh->b_blocknr << 1) + start != block) \
+ { printk("Wrong bh block# %d %d ",SCpnt->request.bh->b_blocknr, block); \
+ panic ("Wrong bh block#"); \
+ }; \
+ if (SCpnt->request.bh->b_dev != SCpnt->request.rq_dev) \
+ panic ("Bad bh target");\
+ };
+#endif
+
+static volatile void (*do_done[SCSI_DEBUG_MAILBOXES])(Scsi_Cmnd *) = {NULL, };
+extern void scsi_debug_interrupt();
+
+volatile Scsi_Cmnd * SCint[SCSI_DEBUG_MAILBOXES] = {NULL,};
+static char SCrst[SCSI_DEBUG_MAILBOXES] = {0,};
+static volatile unsigned int timeout[8] ={0,};
+
+/*
+ * Semaphore used to simulate bus lockups.
+ */
+static int scsi_debug_lockup = 0;
+
+static char sense_buffer[128] = {0,};
+
+static void scsi_dump(Scsi_Cmnd * SCpnt, int flag){
+ int i;
+#if 0
+ unsigned char * pnt;
+#endif
+ unsigned int * lpnt;
+ struct scatterlist * sgpnt = NULL;
+ printk("use_sg: %d",SCpnt->use_sg);
+ if (SCpnt->use_sg){
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ lpnt = (int *) sgpnt[i].alt_address;
+ printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+ if (lpnt) printk(" (Alt %x) ",lpnt[15]);
+ };
+ } else {
+ printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+ lpnt = (int *) SCpnt->request.buffer;
+ if (lpnt) printk(" (Alt %x) ",lpnt[15]);
+ };
+ lpnt = (unsigned int *) SCpnt;
+ for (i=0;i<sizeof(Scsi_Cmnd)/4+1; i++) {
+ if ((i & 7) == 0) printk("\n");
+ printk("%x ",*lpnt++);
+ };
+ printk("\n");
+ if (flag == 0) return;
+ lpnt = (unsigned int *) sgpnt[0].alt_address;
+ for (i=0;i<sizeof(Scsi_Cmnd)/4+1; i++) {
+ if ((i & 7) == 0) printk("\n");
+ printk("%x ",*lpnt++);
+ };
+#if 0
+ printk("\n");
+ lpnt = (unsigned int *) sgpnt[0].address;
+ for (i=0;i<sizeof(Scsi_Cmnd)/4+1; i++) {
+ if ((i & 7) == 0) printk("\n");
+ printk("%x ",*lpnt++);
+ };
+ printk("\n");
+#endif
+ printk("DMA free %d sectors.\n", dma_free_sectors);
+}
+
+int scsi_debug_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ unchar *cmd = (unchar *) SCpnt->cmnd;
+ struct partition * p;
+ int block, start;
+ struct buffer_head * bh = NULL;
+ unsigned char * buff;
+ int nbytes, sgcount;
+ int scsi_debug_errsts;
+ struct scatterlist * sgpnt;
+ int target = SCpnt->target;
+ int bufflen = SCpnt->request_bufflen;
+ unsigned long flags;
+ int i;
+ sgcount = 0;
+ sgpnt = NULL;
+
+ DEB(if (target > 1) { SCpnt->result = DID_TIME_OUT << 16;done(SCpnt);return 0;});
+
+ buff = (unsigned char *) SCpnt->request_buffer;
+
+ if(target>=1 || SCpnt->lun != 0) {
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ };
+
+ if( SCrst[target] != 0 && !scsi_debug_lockup )
+ {
+ SCrst[target] = 0;
+ memset(SCpnt->sense_buffer, 0, sizeof(SCpnt->sense_buffer));
+ SCpnt->sense_buffer[0] = 0x70;
+ SCpnt->sense_buffer[2] = UNIT_ATTENTION;
+ SCpnt->result = (CHECK_CONDITION << 1);
+ done(SCpnt);
+ }
+ switch(*cmd){
+ case REQUEST_SENSE:
+ printk("Request sense...\n");
+#ifndef DEBUG
+ {
+ int i;
+ printk("scsi_debug: Requesting sense buffer (%x %x %x %d):", SCpnt, buff, done, bufflen);
+ for(i=0;i<12;i++) printk("%d ",sense_buffer[i]);
+ printk("\n");
+ };
+#endif
+ memset(buff, 0, bufflen);
+ memcpy(buff, sense_buffer, bufflen);
+ memset(sense_buffer, 0, sizeof(sense_buffer));
+ SCpnt->result = 0;
+ done(SCpnt);
+ return 0;
+ case ALLOW_MEDIUM_REMOVAL:
+ if(cmd[4]) printk("Medium removal inhibited...");
+ else printk("Medium removal enabled...");
+ scsi_debug_errsts = 0;
+ break;
+ case INQUIRY:
+ printk("Inquiry...(%x %d)\n", buff, bufflen);
+ memset(buff, 0, bufflen);
+ buff[0] = TYPE_DISK;
+ buff[1] = 0x80; /* Removable disk */
+ buff[2] = 1;
+ buff[4] = 33 - 5;
+ memcpy(&buff[8],"Foo Inc",7);
+ memcpy(&buff[16],"XYZZY",5);
+ memcpy(&buff[32],"1",1);
+ scsi_debug_errsts = 0;
+ break;
+ case TEST_UNIT_READY:
+ printk("Test unit ready(%x %d)\n", buff, bufflen);
+ if (buff)
+ memset(buff, 0, bufflen);
+ scsi_debug_errsts = 0;
+ break;
+ case READ_CAPACITY:
+ printk("Read Capacity\n");
+ if(NR_REAL < 0) NR_REAL = (MINOR(SCpnt->request.rq_dev) >> 4) & 0x0f;
+ memset(buff, 0, bufflen);
+ buff[0] = (CAPACITY >> 24);
+ buff[1] = (CAPACITY >> 16) & 0xff;
+ buff[2] = (CAPACITY >> 8) & 0xff;
+ buff[3] = CAPACITY & 0xff;
+ buff[6] = 2; /* 512 byte sectors */
+ scsi_debug_errsts = 0;
+ break;
+ case READ_10:
+ case READ_6:
+#ifdef DEBUG
+ printk("Read...");
+#endif
+ if ((*cmd) == READ_10)
+ block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
+ else
+ block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
+ VERIFY_DEBUG(READ);
+#if defined(SCSI_SETUP_LATENCY) || defined(SCSI_DATARATE)
+ {
+ int delay = SCSI_SETUP_LATENCY;
+ double usec;
+
+ usec = 0.0;
+ usec = (SCpnt->request.nr_sectors << 9) * 1.0e6 / SCSI_DATARATE;
+ delay += usec;
+ if(delay) usleep(delay);
+ };
+#endif
+
+#ifdef DEBUG
+ printk("(r%d)",SCpnt->request.nr_sectors);
+#endif
+ nbytes = bufflen;
+ if(SCpnt->use_sg){
+ sgcount = 0;
+ sgpnt = (struct scatterlist *) buff;
+ buff = sgpnt[sgcount].address;
+ bufflen = sgpnt[sgcount].length;
+ bh = SCpnt->request.bh;
+ };
+ scsi_debug_errsts = 0;
+ do{
+ VERIFY1_DEBUG(READ);
+ /* For the speedy test, we do not even want to fill the buffer with anything */
+#ifndef SPEEDY
+ memset(buff, 0, bufflen);
+#endif
+ /* If this is block 0, then we want to read the partition table for this
+ * device. Let's make one up */
+ if(block == 0 && target == 0) {
+ memset(buff, 0, bufflen);
+ *((unsigned short *) (buff+510)) = 0xAA55;
+ p = (struct partition* ) (buff + 0x1be);
+ npart = 0;
+ while(starts[npart+1]){
+ p->start_sect = starts[npart];
+ p->nr_sects = starts[npart+1] - starts [npart];
+ p->sys_ind = 0x81; /* Linux partition */
+ p++;
+ npart++;
+ };
+ scsi_debug_errsts = 0;
+ break;
+ };
+#ifdef DEBUG
+ if (SCpnt->use_sg) printk("Block %x (%d %d)\n",block, SCpnt->request.nr_sectors,
+ SCpnt->request.current_nr_sectors);
+#endif
+
+#if 0
+ /* Simulate a disk change */
+ if(block == 0xfff0) {
+ sense_buffer[0] = 0x70;
+ sense_buffer[2] = UNIT_ATTENTION;
+ starts[0] += 10;
+ starts[1] += 10;
+ starts[2] += 10;
+
+#ifdef DEBUG
+ {
+ int i;
+ printk("scsi_debug: Filling sense buffer:");
+ for(i=0;i<12;i++) printk("%d ",sense_buffer[i]);
+ printk("\n");
+ };
+#endif
+ scsi_debug_errsts = (COMMAND_COMPLETE << 8) | (CHECK_CONDITION << 1);
+ break;
+ } /* End phony disk change code */
+#endif
+
+#ifndef SPEEDY
+ memcpy(buff, &target, sizeof(target));
+ memcpy(buff+sizeof(target), cmd, 24);
+ memcpy(buff+60, &block, sizeof(block));
+ memcpy(buff+64, SCpnt, sizeof(Scsi_Cmnd));
+#endif
+ nbytes -= bufflen;
+ if(SCpnt->use_sg){
+#ifndef SPEEDY
+ memcpy(buff+128, bh, sizeof(struct buffer_head));
+#endif
+ block += bufflen >> 9;
+ bh = bh->b_reqnext;
+ sgcount++;
+ if (nbytes) {
+ if(!bh) panic("Too few blocks for linked request.");
+ buff = sgpnt[sgcount].address;
+ bufflen = sgpnt[sgcount].length;
+ };
+ }
+ } while(nbytes);
+
+ SCpnt->result = 0;
+ (done)(SCpnt);
+ return;
+
+ if (SCpnt->use_sg && !scsi_debug_errsts)
+ if(bh) scsi_dump(SCpnt, 0);
+ break;
+ case WRITE_10:
+ case WRITE_6:
+#ifdef DEBUG
+ printk("Write\n");
+#endif
+ if ((*cmd) == WRITE_10)
+ block = cmd[5] + (cmd[4] << 8) + (cmd[3] << 16) + (cmd[2] << 24);
+ else
+ block = cmd[3] + (cmd[2] << 8) + ((cmd[1] & 0x1f) << 16);
+ VERIFY_DEBUG(WRITE);
+ /* printk("(w%d)",SCpnt->request.nr_sectors); */
+ if (SCpnt->use_sg){
+ if ((bufflen >> 9) != SCpnt->request.nr_sectors)
+ panic ("Trying to write wrong number of blocks\n");
+ sgpnt = (struct scatterlist *) buff;
+ buff = sgpnt[sgcount].address;
+ };
+#if 0
+ if (block != *((unsigned long *) (buff+60))) {
+ printk("%x %x :",block, *((unsigned long *) (buff+60)));
+ scsi_dump(SCpnt,1);
+ panic("Bad block written.\n");
+ };
+#endif
+ scsi_debug_errsts = 0;
+ break;
+ default:
+ printk("Unknown command %d\n",*cmd);
+ SCpnt->result = DID_NO_CONNECT << 16;
+ done(SCpnt);
+ return 0;
+ };
+
+ save_flags(flags);
+ cli();
+ for(i=0;i<SCSI_DEBUG_MAILBOXES; i++){
+ if (SCint[i] == 0) break;
+ };
+
+ if (i >= SCSI_DEBUG_MAILBOXES || SCint[i] != 0)
+ panic("Unable to find empty SCSI_DEBUG command slot.\n");
+
+ SCint[i] = SCpnt;
+
+ if (done) {
+ DEB(printk("scsi_debug_queuecommand: now waiting for interrupt "););
+ if (do_done[i])
+ printk("scsi_debug_queuecommand: Two concurrent queuecommand?\n");
+ else
+ do_done[i] = done;
+ }
+ else
+ printk("scsi_debug_queuecommand: done cant be NULL\n");
+
+#ifdef IMMEDIATE
+ if( !scsi_debug_lockup )
+ {
+ SCpnt->result = scsi_debug_errsts;
+ scsi_debug_intr_handle(); /* No timer - do this one right away */
+ }
+ restore_flags(flags);
+#else
+ timeout[i] = jiffies+DISK_SPEED;
+
+ /* If no timers active, then set this one */
+ if ((timer_active & (1 << SCSI_DEBUG_TIMER)) == 0) {
+ timer_table[SCSI_DEBUG_TIMER].expires = timeout[i];
+ timer_active |= 1 << SCSI_DEBUG_TIMER;
+ };
+
+ SCpnt->result = scsi_debug_errsts;
+ restore_flags(flags);
+
+#if 0
+ printk("Sending command (%d %x %d %d)...", i, done, timeout[i],jiffies);
+#endif
+#endif
+
+ return 0;
+}
+
+volatile static int internal_done_flag = 0;
+volatile static int internal_done_errcode = 0;
+static void internal_done(Scsi_Cmnd * SCpnt)
+{
+ internal_done_errcode = SCpnt->result;
+ ++internal_done_flag;
+}
+
+int scsi_debug_command(Scsi_Cmnd * SCpnt)
+{
+ DEB(printk("scsi_debug_command: ..calling scsi_debug_queuecommand\n"));
+ scsi_debug_queuecommand(SCpnt, internal_done);
+
+ while (!internal_done_flag);
+ internal_done_flag = 0;
+ return internal_done_errcode;
+}
+
+/* A "high" level interrupt handler. This should be called once per jiffy
+ * to simulate a regular scsi disk. We use a timer to do this. */
+
+static void scsi_debug_intr_handle(void)
+{
+ Scsi_Cmnd * SCtmp;
+ int i, pending;
+ void (*my_done)(Scsi_Cmnd *);
+ unsigned long flags;
+ int to;
+
+#ifndef IMMEDIATE
+ timer_table[SCSI_DEBUG_TIMER].expires = 0;
+ timer_active &= ~(1 << SCSI_DEBUG_TIMER);
+#endif
+
+ repeat:
+ save_flags(flags);
+ cli();
+ for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
+ if (SCint[i] == 0) continue;
+#ifndef IMMEDIATE
+ if (timeout[i] == 0) continue;
+ if (timeout[i] <= jiffies) break;
+#else
+ break;
+#endif
+ };
+
+ if(i == SCSI_DEBUG_MAILBOXES){
+#ifndef IMMEDIATE
+ pending = INT_MAX;
+ for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
+ if (SCint[i] == 0) continue;
+ if (timeout[i] == 0) continue;
+ if (timeout[i] <= jiffies) {restore_flags(flags); goto repeat;};
+ if (timeout[i] > jiffies) {
+ if (pending > timeout[i]) pending = timeout[i];
+ continue;
+ };
+ };
+ if (pending && pending != INT_MAX) {
+ timer_table[SCSI_DEBUG_TIMER].expires =
+ (pending <= jiffies ? jiffies+1 : pending);
+ timer_active |= 1 << SCSI_DEBUG_TIMER;
+ };
+ restore_flags(flags);
+#endif
+ return;
+ };
+
+ if(i < SCSI_DEBUG_MAILBOXES){
+ timeout[i] = 0;
+ my_done = do_done[i];
+ do_done[i] = NULL;
+ to = timeout[i];
+ timeout[i] = 0;
+ SCtmp = (Scsi_Cmnd *) SCint[i];
+ SCint[i] = NULL;
+ restore_flags(flags);
+
+ if (!my_done) {
+ printk("scsi_debug_intr_handle: Unexpected interrupt\n");
+ return;
+ }
+
+#ifdef DEBUG
+ printk("In intr_handle...");
+ printk("...done %d %x %d %d\n",i , my_done, to, jiffies);
+ printk("In intr_handle: %d %x %x\n",i, SCtmp, my_done);
+#endif
+
+ my_done(SCtmp);
+#ifdef DEBUG
+ printk("Called done.\n");
+#endif
+ };
+ goto repeat;
+}
+
+
+int scsi_debug_detect(Scsi_Host_Template * tpnt)
+{
+ tpnt->proc_dir = &proc_scsi_scsi_debug;
+#ifndef IMMEDIATE
+ timer_table[SCSI_DEBUG_TIMER].fn = scsi_debug_intr_handle;
+ timer_table[SCSI_DEBUG_TIMER].expires = 0;
+#endif
+ return 1;
+}
+
+int scsi_debug_abort(Scsi_Cmnd * SCpnt)
+{
+ int j;
+ void (*my_done)(Scsi_Cmnd *);
+ unsigned long flags;
+
+ DEB(printk("scsi_debug_abort\n"));
+#if 0
+ SCpnt->result = SCpnt->abort_reason << 16;
+ for(j=0;j<SCSI_DEBUG_MAILBOXES; j++) {
+ if(SCpnt == SCint[j]) {
+ my_done = do_done[j];
+ my_done(SCpnt);
+ save_flags(flags);
+ cli();
+ timeout[j] = 0;
+ SCint[j] = NULL;
+ do_done[j] = NULL;
+ restore_flags(flags);
+ };
+ };
+#endif
+ return SCSI_ABORT_SNOOZE;
+}
+
+int scsi_debug_biosparam(Disk * disk, kdev_t dev, int* info){
+ int size = disk->capacity;
+ info[0] = 32;
+ info[1] = 64;
+ info[2] = (size + 2047) >> 11;
+ if (info[2] >= 1024) info[2] = 1024;
+ return 0;
+}
+
+int scsi_debug_reset(Scsi_Cmnd * SCpnt)
+{
+ int i;
+ unsigned long flags;
+
+ void (*my_done)(Scsi_Cmnd *);
+ printk("Bus unlocked by reset(%d)\n", SCpnt->host->suggest_bus_reset);
+ scsi_debug_lockup = 0;
+ DEB(printk("scsi_debug_reset called\n"));
+ for(i=0;i<SCSI_DEBUG_MAILBOXES; i++) {
+ if (SCint[i] == NULL) continue;
+ SCint[i]->result = DID_RESET << 16;
+ my_done = do_done[i];
+ my_done(SCint[i]);
+ save_flags(flags);
+ cli();
+ SCint[i] = NULL;
+ do_done[i] = NULL;
+ timeout[i] = 0;
+ restore_flags(flags);
+ }
+ return SCSI_RESET_SUCCESS;
+}
+
+const char *scsi_debug_info(void)
+{
+ static char buffer[] = " "; /* looks nicer without anything here */
+ return buffer;
+}
+
+/* scsi_debug_proc_info
+ * Used if the driver currently has no own support for /proc/scsi
+ */
+int scsi_debug_proc_info(char *buffer, char **start, off_t offset,
+ int length, int inode, int inout)
+{
+ int len, pos, begin;
+ int orig_length;
+
+ if(inout == 1)
+ {
+ /* First check for the Signature */
+ if (length >= 10 && strncmp(buffer, "scsi_debug", 10) == 0) {
+ buffer += 11;
+ length -= 11;
+ /*
+ * OK, we are getting some kind of command. Figure out
+ * what we are supposed to do here. Simulate bus lockups
+ * to test our reset capability.
+ */
+ if( length == 6 && strncmp(buffer, "lockup", length) == 0 )
+ {
+ scsi_debug_lockup = 1;
+ return length;
+ }
+
+ if( length == 6 && strncmp(buffer, "unlock", length) == 0 )
+ {
+ scsi_debug_lockup = 0;
+ return length;
+ }
+
+ printk("Unknown command:%s\n", buffer);
+ } else
+ printk("Wrong Signature:%10s\n", (char *) ((ulong)buffer-11));
+
+ return -EINVAL;
+
+ }
+
+ begin = 0;
+ pos = len = sprintf(buffer,
+ "This driver is not a real scsi driver, but it plays one on TV.\n"
+ "It is very handy for debugging specific problems because you\n"
+ "can simulate a variety of error conditions\n");
+ if(pos < offset)
+ {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+
+ return(len);
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = SCSI_DEBUG;
+
+#include "scsi_module.c"
+#endif
+
+/*
+ * Overrides for Emacs so that we almost 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/i386/i386at/gpl/linux/scsi/scsi_debug.h b/i386/i386at/gpl/linux/scsi/scsi_debug.h
new file mode 100644
index 00000000..87ae155f
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi_debug.h
@@ -0,0 +1,30 @@
+#ifndef _SCSI_DEBUG_H
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+int scsi_debug_detect(Scsi_Host_Template *);
+int scsi_debug_command(Scsi_Cmnd *);
+int scsi_debug_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int scsi_debug_abort(Scsi_Cmnd *);
+int scsi_debug_biosparam(Disk *, kdev_t, int[]);
+int scsi_debug_reset(Scsi_Cmnd *);
+int scsi_debug_proc_info(char *, char **, off_t, int, int, int);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+
+#define SCSI_DEBUG_MAILBOXES 8
+
+#define SCSI_DEBUG {NULL, NULL, NULL, scsi_debug_proc_info, \
+ "SCSI DEBUG", scsi_debug_detect, NULL, \
+ NULL, scsi_debug_command, \
+ scsi_debug_queuecommand, \
+ scsi_debug_abort, \
+ scsi_debug_reset, \
+ NULL, \
+ scsi_debug_biosparam, \
+ SCSI_DEBUG_MAILBOXES, 7, SG_ALL, 1, 0, 1, ENABLE_CLUSTERING}
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/scsi_ioctl.c b/i386/i386at/gpl/linux/scsi/scsi_ioctl.c
new file mode 100644
index 00000000..11d57bae
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi_ioctl.c
@@ -0,0 +1,397 @@
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/segment.h>
+#include <asm/system.h>
+
+#include <linux/errno.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "scsi_ioctl.h"
+
+#define MAX_RETRIES 5
+#define MAX_TIMEOUT 900
+#define MAX_BUF 4096
+
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+/*
+ * If we are told to probe a host, we will return 0 if the host is not
+ * present, 1 if the host is present, and will return an identifying
+ * string at *arg, if arg is non null, filling to the length stored at
+ * (int *) arg
+ */
+
+static int ioctl_probe(struct Scsi_Host * host, void *buffer)
+{
+ int temp, result;
+ unsigned int len,slen;
+ const char * string;
+
+ if ((temp = host->hostt->present) && buffer) {
+ result = verify_area(VERIFY_READ, buffer, sizeof(long));
+ if (result) return result;
+
+ len = get_user ((unsigned int *) buffer);
+ if(host->hostt->info)
+ string = host->hostt->info(host);
+ else
+ string = host->hostt->name;
+ if(string) {
+ slen = strlen(string);
+ if (len > slen)
+ len = slen + 1;
+ result = verify_area(VERIFY_WRITE, buffer, len);
+ if (result) return result;
+
+ memcpy_tofs (buffer, string, len);
+ }
+ }
+ return temp;
+}
+
+/*
+ *
+ * The SCSI_IOCTL_SEND_COMMAND ioctl sends a command out to the SCSI host.
+ * The MAX_TIMEOUT and MAX_RETRIES variables are used.
+ *
+ * dev is the SCSI device struct ptr, *(int *) arg is the length of the
+ * input data, if any, not including the command string & counts,
+ * *((int *)arg + 1) is the output buffer size in bytes.
+ *
+ * *(char *) ((int *) arg)[2] the actual command byte.
+ *
+ * Note that no more than MAX_BUF data bytes will be transfered. Since
+ * SCSI block device size is 512 bytes, I figured 1K was good.
+ * but (WDE) changed it to 8192 to handle large bad track buffers.
+ * ERY: I changed this to a dynamic allocation using scsi_malloc - we were
+ * getting a kernel stack overflow which was crashing the system when we
+ * were using 8192 bytes.
+ *
+ * This size *does not* include the initial lengths that were passed.
+ *
+ * The SCSI command is read from the memory location immediately after the
+ * length words, and the input data is right after the command. The SCSI
+ * routines know the command size based on the opcode decode.
+ *
+ * The output area is then filled in starting from the command byte.
+ */
+
+static void scsi_ioctl_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+static int ioctl_internal_command(Scsi_Device *dev, char * cmd)
+{
+ int result;
+ Scsi_Cmnd * SCpnt;
+
+ SCpnt = allocate_device(NULL, dev, 1);
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd(SCpnt, cmd, NULL, 0,
+ scsi_ioctl_done, MAX_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ if(driver_byte(SCpnt->result) != 0)
+ switch(SCpnt->sense_buffer[2] & 0xf) {
+ case ILLEGAL_REQUEST:
+ if(cmd[0] == ALLOW_MEDIUM_REMOVAL) dev->lockable = 0;
+ else printk("SCSI device (ioctl) reports ILLEGAL REQUEST.\n");
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ if(dev->removable){
+ printk(KERN_INFO "Device not ready. Make sure there is a disc in the drive.\n");
+ break;
+ };
+ case UNIT_ATTENTION:
+ if (dev->removable){
+ dev->changed = 1;
+ SCpnt->result = 0; /* This is no longer considered an error */
+ printk(KERN_INFO "Disc change detected.\n");
+ break;
+ };
+ default: /* Fall through for non-removable media */
+ printk("SCSI error: host %d id %d lun %d return code = %x\n",
+ dev->host->host_no,
+ dev->id,
+ dev->lun,
+ SCpnt->result);
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(SCpnt->sense_buffer[0]),
+ sense_error(SCpnt->sense_buffer[0]),
+ SCpnt->sense_buffer[2] & 0xf);
+
+ };
+
+ result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE;
+ wake_up(&SCpnt->device->device_wait);
+ return result;
+}
+
+/*
+ * This interface is depreciated - users should use the scsi generics
+ * interface instead, as this is a more flexible approach to performing
+ * generic SCSI commands on a device.
+ */
+static int ioctl_command(Scsi_Device *dev, void *buffer)
+{
+ char * buf;
+ char cmd[12];
+ char * cmd_in;
+ Scsi_Cmnd * SCpnt;
+ unsigned char opcode;
+ int inlen, outlen, cmdlen;
+ int needed, buf_needed;
+ int result;
+
+ if (!buffer)
+ return -EINVAL;
+
+
+ /*
+ * Verify that we can read at least this much.
+ */
+ result = verify_area(VERIFY_READ, buffer, 2*sizeof(long) + 1);
+ if (result) return result;
+
+ /*
+ * The structure that we are passed should look like:
+ *
+ * struct sdata{
+ * int inlen;
+ * int outlen;
+ * char cmd[]; # However many bytes are used for cmd.
+ * char data[];
+ */
+ inlen = get_user((unsigned int *) buffer);
+ outlen = get_user( ((unsigned int *) buffer) + 1);
+
+ /*
+ * We do not transfer more than MAX_BUF with this interface.
+ * If the user needs to transfer more data than this, they
+ * should use scsi_generics instead.
+ */
+ if( inlen > MAX_BUF ) inlen = MAX_BUF;
+ if( outlen > MAX_BUF ) outlen = MAX_BUF;
+
+ cmd_in = (char *) ( ((int *)buffer) + 2);
+ opcode = get_user(cmd_in);
+
+ needed = buf_needed = (inlen > outlen ? inlen : outlen);
+ if(buf_needed){
+ buf_needed = (buf_needed + 511) & ~511;
+ if (buf_needed > MAX_BUF) buf_needed = MAX_BUF;
+ buf = (char *) scsi_malloc(buf_needed);
+ if (!buf) return -ENOMEM;
+ memset(buf, 0, buf_needed);
+ } else
+ buf = NULL;
+
+ /*
+ * Obtain the command from the user's address space.
+ */
+ cmdlen = COMMAND_SIZE(opcode);
+
+ result = verify_area(VERIFY_READ, cmd_in,
+ cmdlen + inlen > MAX_BUF ? MAX_BUF : inlen);
+ if (result) return result;
+
+ memcpy_fromfs ((void *) cmd, cmd_in, cmdlen);
+
+ /*
+ * Obtain the data to be sent to the device (if any).
+ */
+ memcpy_fromfs ((void *) buf,
+ (void *) (cmd_in + cmdlen),
+ inlen);
+
+ /*
+ * Set the lun field to the correct value.
+ */
+ cmd[1] = ( cmd[1] & 0x1f ) | (dev->lun << 5);
+
+#ifndef DEBUG_NO_CMD
+
+ SCpnt = allocate_device(NULL, dev, 1);
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd(SCpnt, cmd, buf, needed, scsi_ioctl_done, MAX_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ /*
+ * If there was an error condition, pass the info back to the user.
+ */
+ if(SCpnt->result) {
+ result = verify_area(VERIFY_WRITE,
+ cmd_in,
+ sizeof(SCpnt->sense_buffer));
+ if (result) return result;
+ memcpy_tofs((void *) cmd_in,
+ SCpnt->sense_buffer,
+ sizeof(SCpnt->sense_buffer));
+ } else {
+ result = verify_area(VERIFY_WRITE, cmd_in, outlen);
+ if (result) return result;
+ memcpy_tofs ((void *) cmd_in, buf, outlen);
+ }
+ result = SCpnt->result;
+
+ SCpnt->request.rq_status = RQ_INACTIVE;
+
+ if (buf) scsi_free(buf, buf_needed);
+
+ if(SCpnt->device->scsi_request_fn)
+ (*SCpnt->device->scsi_request_fn)();
+
+ wake_up(&SCpnt->device->device_wait);
+ return result;
+#else
+ {
+ int i;
+ printk("scsi_ioctl : device %d. command = ", dev->id);
+ for (i = 0; i < 12; ++i)
+ printk("%02x ", cmd[i]);
+ printk("\nbuffer =");
+ for (i = 0; i < 20; ++i)
+ printk("%02x ", buf[i]);
+ printk("\n");
+ printk("inlen = %d, outlen = %d, cmdlen = %d\n",
+ inlen, outlen, cmdlen);
+ printk("buffer = %d, cmd_in = %d\n", buffer, cmd_in);
+ }
+ return 0;
+#endif
+}
+
+/*
+ * the scsi_ioctl() function differs from most ioctls in that it does
+ * not take a major/minor number as the dev filed. Rather, it takes
+ * a pointer to a scsi_devices[] element, a structure.
+ */
+int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg)
+{
+ int result;
+ char scsi_cmd[12];
+
+ /* No idea how this happens.... */
+ if (!dev) return -ENXIO;
+
+ switch (cmd) {
+ case SCSI_IOCTL_GET_IDLUN:
+ result = verify_area(VERIFY_WRITE, (void *) arg, 2*sizeof(long));
+ if (result) return result;
+
+ put_user(dev->id
+ + (dev->lun << 8)
+ + (dev->channel << 16)
+ + ((dev->host->hostt->proc_dir->low_ino & 0xff) << 24),
+ (unsigned long *) arg);
+ put_user( dev->host->unique_id, (unsigned long *) arg+1);
+ return 0;
+ case SCSI_IOCTL_TAGGED_ENABLE:
+ if(!suser()) return -EACCES;
+ if(!dev->tagged_supported) return -EINVAL;
+ dev->tagged_queue = 1;
+ dev->current_tag = 1;
+ break;
+ case SCSI_IOCTL_TAGGED_DISABLE:
+ if(!suser()) return -EACCES;
+ if(!dev->tagged_supported) return -EINVAL;
+ dev->tagged_queue = 0;
+ dev->current_tag = 0;
+ break;
+ case SCSI_IOCTL_PROBE_HOST:
+ return ioctl_probe(dev->host, arg);
+ case SCSI_IOCTL_SEND_COMMAND:
+ if(!suser()) return -EACCES;
+ return ioctl_command((Scsi_Device *) dev, arg);
+ case SCSI_IOCTL_DOORLOCK:
+ if (!dev->removable || !dev->lockable) return 0;
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = SCSI_REMOVAL_PREVENT;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+ break;
+ case SCSI_IOCTL_DOORUNLOCK:
+ if (!dev->removable || !dev->lockable) return 0;
+ scsi_cmd[0] = ALLOW_MEDIUM_REMOVAL;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = SCSI_REMOVAL_ALLOW;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+ case SCSI_IOCTL_TEST_UNIT_READY:
+ scsi_cmd[0] = TEST_UNIT_READY;
+ scsi_cmd[1] = dev->lun << 5;
+ scsi_cmd[2] = scsi_cmd[3] = scsi_cmd[5] = 0;
+ scsi_cmd[4] = 0;
+ return ioctl_internal_command((Scsi_Device *) dev, scsi_cmd);
+ break;
+ default :
+ return -EINVAL;
+ }
+ return -EINVAL;
+}
+
+/*
+ * Just like scsi_ioctl, only callable from kernel space with no
+ * fs segment fiddling.
+ */
+
+int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg) {
+ unsigned long oldfs;
+ int tmp;
+ oldfs = get_fs();
+ set_fs(get_ds());
+ tmp = scsi_ioctl (dev, cmd, arg);
+ set_fs(oldfs);
+ return tmp;
+}
+
+/*
+ * Overrides for Emacs so that we almost 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/i386/i386at/gpl/linux/scsi/scsi_ioctl.h b/i386/i386at/gpl/linux/scsi/scsi_ioctl.h
new file mode 100644
index 00000000..a42fed00
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi_ioctl.h
@@ -0,0 +1,21 @@
+#ifndef _SCSI_IOCTL_H
+#define _SCSI_IOCTL_H
+
+#define SCSI_IOCTL_SEND_COMMAND 1
+#define SCSI_IOCTL_TEST_UNIT_READY 2
+#define SCSI_IOCTL_BENCHMARK_COMMAND 3
+#define SCSI_IOCTL_SYNC 4 /* Request synchronous parameters */
+/* The door lock/unlock constants are compatible with Sun constants for
+ the cdrom */
+#define SCSI_IOCTL_DOORLOCK 0x5380 /* lock the eject mechanism */
+#define SCSI_IOCTL_DOORUNLOCK 0x5381 /* unlock the mechanism */
+
+#define SCSI_REMOVAL_PREVENT 1
+#define SCSI_REMOVAL_ALLOW 0
+
+extern int scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+extern int kernel_scsi_ioctl (Scsi_Device *dev, int cmd, void *arg);
+
+#endif
+
+
diff --git a/i386/i386at/gpl/linux/scsi/scsi_proc.c b/i386/i386at/gpl/linux/scsi/scsi_proc.c
new file mode 100644
index 00000000..6650cec9
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsi_proc.c
@@ -0,0 +1,317 @@
+/*
+ * linux/drivers/scsi/scsi_proc.c
+ *
+ * The functions in this file provide an interface between
+ * the PROC file system and the SCSI device drivers
+ * It is mainly used for debugging, statistics and to pass
+ * information directly to the lowlevel driver.
+ *
+ * (c) 1995 Michael Neuffer neuffer@goofy.zdv.uni-mainz.de
+ * Version: 0.99.8 last change: 95/09/13
+ *
+ * generic command parser provided by:
+ * Andreas Heilwagen <crashcar@informatik.uni-koblenz.de>
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/string.h>
+#include <linux/mm.h>
+#include <linux/malloc.h>
+#include <linux/proc_fs.h>
+#include <linux/errno.h>
+#include <linux/stat.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+
+#ifndef TRUE
+#define TRUE 1
+#define FALSE 0
+#endif
+
+extern int scsi_proc_info(char *, char **, off_t, int, int, int);
+
+struct scsi_dir {
+ struct proc_dir_entry entry;
+ char name[4];
+};
+
+
+/* generic_proc_info
+ * Used if the driver currently has no own support for /proc/scsi
+ */
+int generic_proc_info(char *buffer, char **start, off_t offset,
+ int length, int inode, int inout)
+{
+ int len, pos, begin;
+
+ if(inout == TRUE)
+ return(-ENOSYS); /* This is a no-op */
+
+ begin = 0;
+ pos = len = sprintf(buffer,
+ "The driver does not yet support the proc-fs\n");
+ if(pos < offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin); /* Start of wanted data */
+ len -= (offset - begin);
+ if(len > length)
+ len = length;
+
+ return(len);
+}
+
+/* dispatch_scsi_info is the central dispatcher
+ * It is the interface between the proc-fs and the SCSI subsystem code
+ */
+extern int dispatch_scsi_info(int ino, char *buffer, char **start,
+ off_t offset, int length, int func)
+{
+ struct Scsi_Host *hpnt = scsi_hostlist;
+
+ if(ino == PROC_SCSI_SCSI) {
+ /*
+ * This is for the scsi core, rather than any specific
+ * lowlevel driver.
+ */
+ return(scsi_proc_info(buffer, start, offset, length, 0, func));
+ }
+
+ while(hpnt) {
+ if (ino == (hpnt->host_no + PROC_SCSI_FILE)) {
+ if(hpnt->hostt->proc_info == NULL)
+ return generic_proc_info(buffer, start, offset, length,
+ hpnt->host_no, func);
+ else
+ return(hpnt->hostt->proc_info(buffer, start, offset,
+ length, hpnt->host_no, func));
+ }
+ hpnt = hpnt->next;
+ }
+ return(-EBADF);
+}
+
+void build_proc_dir_entries(Scsi_Host_Template *tpnt)
+{
+ struct Scsi_Host *hpnt;
+
+ struct scsi_dir *scsi_hba_dir;
+
+ proc_scsi_register(0, tpnt->proc_dir);
+
+ hpnt = scsi_hostlist;
+ while (hpnt) {
+ if (tpnt == hpnt->hostt) {
+ scsi_hba_dir = scsi_init_malloc(sizeof(struct scsi_dir), GFP_KERNEL);
+ if(scsi_hba_dir == NULL)
+ panic("Not enough memory to register SCSI HBA in /proc/scsi !\n");
+ memset(scsi_hba_dir, 0, sizeof(struct scsi_dir));
+ scsi_hba_dir->entry.low_ino = PROC_SCSI_FILE + hpnt->host_no;
+ scsi_hba_dir->entry.namelen = sprintf(scsi_hba_dir->name,"%d",
+ hpnt->host_no);
+ scsi_hba_dir->entry.name = scsi_hba_dir->name;
+ scsi_hba_dir->entry.mode = S_IFREG | S_IRUGO | S_IWUSR;
+ proc_scsi_register(tpnt->proc_dir, &scsi_hba_dir->entry);
+ }
+ hpnt = hpnt->next;
+ }
+}
+
+/*
+ * parseHandle *parseInit(char *buf, char *cmdList, int cmdNum);
+ * gets a pointer to a null terminated data buffer
+ * and a list of commands with blanks as delimiter
+ * in between.
+ * The commands have to be alphanumerically sorted.
+ * cmdNum has to contain the number of commands.
+ * On success, a pointer to a handle structure
+ * is returned, NULL on failure
+ *
+ * int parseOpt(parseHandle *handle, char **param);
+ * processes the next parameter. On success, the
+ * index of the appropriate command in the cmdList
+ * is returned, starting with zero.
+ * param points to the null terminated parameter string.
+ * On failure, -1 is returned.
+ *
+ * The databuffer buf may only contain pairs of commands
+ * options, separated by blanks:
+ * <Command> <Parameter> [<Command> <Parameter>]*
+ */
+
+typedef struct
+{
+ char *buf, /* command buffer */
+ *cmdList, /* command list */
+ *bufPos, /* actual position */
+ **cmdPos, /* cmdList index */
+ cmdNum; /* cmd number */
+} parseHandle;
+
+
+inline int parseFree (parseHandle *handle) /* free memory */
+{
+ kfree (handle->cmdPos);
+ kfree (handle);
+
+ return(-1);
+}
+
+
+parseHandle *parseInit(char *buf, char *cmdList, int cmdNum)
+{
+ char *ptr; /* temp pointer */
+ parseHandle *handle; /* new handle */
+
+ if (!buf || !cmdList) /* bad input ? */
+ return(NULL);
+ if ((handle = (parseHandle*) kmalloc(sizeof(parseHandle), 1)) == 0)
+ return(NULL); /* out of memory */
+ if ((handle->cmdPos = (char**) kmalloc(sizeof(int), cmdNum)) == 0) {
+ kfree(handle);
+ return(NULL); /* out of memory */
+ }
+
+ handle->buf = handle->bufPos = buf; /* init handle */
+ handle->cmdList = cmdList;
+ handle->cmdNum = cmdNum;
+
+ handle->cmdPos[cmdNum = 0] = cmdList;
+ for (ptr = cmdList; *ptr; ptr++) { /* scan command string */
+ if(*ptr == ' ') { /* and insert zeroes */
+ *ptr++ = 0;
+ handle->cmdPos[++cmdNum] = ptr++;
+ }
+ }
+ return(handle);
+}
+
+
+int parseOpt(parseHandle *handle, char **param)
+{
+ int cmdIndex = 0,
+ cmdLen = 0;
+ char *startPos;
+
+ if (!handle) /* invalid handle */
+ return(parseFree(handle));
+ /* skip spaces */
+ for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
+ if (!*(handle->bufPos))
+ return(parseFree(handle)); /* end of data */
+
+ startPos = handle->bufPos; /* store cmd start */
+ for (; handle->cmdPos[cmdIndex][cmdLen] && *(handle->bufPos); handle->bufPos++)
+ { /* no string end? */
+ for (;;)
+ {
+ if (*(handle->bufPos) == handle->cmdPos[cmdIndex][cmdLen])
+ break; /* char matches ? */
+ else
+ if (memcmp(startPos, (char*)(handle->cmdPos[++cmdIndex]), cmdLen))
+ return(parseFree(handle)); /* unknown command */
+
+ if (cmdIndex >= handle->cmdNum)
+ return(parseFree(handle)); /* unknown command */
+ }
+
+ cmdLen++; /* next char */
+ }
+
+ /* Get param. First skip all blanks, then insert zero after param */
+
+ for (; *(handle->bufPos) && *(handle->bufPos) == ' '; handle->bufPos++);
+ *param = handle->bufPos;
+
+ for (; *(handle->bufPos) && *(handle->bufPos) != ' '; handle->bufPos++);
+ *(handle->bufPos++) = 0;
+
+ return(cmdIndex);
+}
+
+#define MAX_SCSI_DEVICE_CODE 10
+const char *const scsi_dev_types[MAX_SCSI_DEVICE_CODE] =
+{
+ "Direct-Access ",
+ "Sequential-Access",
+ "Printer ",
+ "Processor ",
+ "WORM ",
+ "CD-ROM ",
+ "Scanner ",
+ "Optical Device ",
+ "Medium Changer ",
+ "Communications "
+};
+
+void proc_print_scsidevice(Scsi_Device *scd, char *buffer, int *size, int len)
+{
+ int x, y = *size;
+
+ y = sprintf(buffer + len,
+ "Host: scsi%d Channel: %02d Id: %02d Lun: %02d\n Vendor: ",
+ scd->host->host_no, scd->channel, scd->id, scd->lun);
+ for (x = 0; x < 8; x++) {
+ if (scd->vendor[x] >= 0x20)
+ y += sprintf(buffer + len + y, "%c", scd->vendor[x]);
+ else
+ y += sprintf(buffer + len + y," ");
+ }
+ y += sprintf(buffer + len + y, " Model: ");
+ for (x = 0; x < 16; x++) {
+ if (scd->model[x] >= 0x20)
+ y += sprintf(buffer + len + y, "%c", scd->model[x]);
+ else
+ y += sprintf(buffer + len + y, " ");
+ }
+ y += sprintf(buffer + len + y, " Rev: ");
+ for (x = 0; x < 4; x++) {
+ if (scd->rev[x] >= 0x20)
+ y += sprintf(buffer + len + y, "%c", scd->rev[x]);
+ else
+ y += sprintf(buffer + len + y, " ");
+ }
+ y += sprintf(buffer + len + y, "\n");
+
+ y += sprintf(buffer + len + y, " Type: %s ",
+ scd->type < MAX_SCSI_DEVICE_CODE ?
+ scsi_dev_types[(int)scd->type] : "Unknown " );
+ y += sprintf(buffer + len + y, " ANSI"
+ " SCSI revision: %02x", (scd->scsi_level < 3)?1:2);
+ if (scd->scsi_level == 2)
+ y += sprintf(buffer + len + y, " CCS\n");
+ else
+ y += sprintf(buffer + len + y, "\n");
+
+ *size = y;
+ return;
+}
+
+/*
+ * Overrides for Emacs so that we get a uniform 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/i386/i386at/gpl/linux/scsi/scsicam.c b/i386/i386at/gpl/linux/scsi/scsicam.c
new file mode 100644
index 00000000..e4e4e764
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/scsicam.c
@@ -0,0 +1,214 @@
+/*
+ * scsicam.c - SCSI CAM support functions, use for HDIO_GETGEO, etc.
+ *
+ * Copyright 1993, 1994 Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@Colorado.EDU
+ * +1 (303) 786-7975
+ *
+ * For more information, please consult the SCSI-CAM draft.
+ */
+
+/*
+ * Don't import our own symbols, as this would severely mess up our
+ * symbol tables.
+ */
+#define _SCSI_SYMS_VER_
+#define __NO_VERSION__
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/genhd.h>
+#include <linux/kernel.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+ unsigned int *cyls, unsigned int *hds, unsigned int *secs);
+static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
+ unsigned int *secs);
+
+/*
+ * Function : int scsicam_bios_param (Disk *disk, int dev, int *ip)
+ *
+ * Purpose : to determine the BIOS mapping used for a drive in a
+ * SCSI-CAM system, storing the results in ip as required
+ * by the HDIO_GETGEO ioctl().
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+int scsicam_bios_param (Disk *disk, /* SCSI disk */
+ kdev_t dev, /* Device major, minor */
+ int *ip /* Heads, sectors, cylinders in that order */) {
+
+ struct buffer_head *bh;
+ int ret_code;
+ int size = disk->capacity;
+
+ if (!(bh = bread(MKDEV(MAJOR(dev), MINOR(dev)&~0xf), 0, 1024)))
+ return -1;
+
+#ifdef DEBUG
+ printk ("scsicam_bios_param : trying existing mapping\n");
+#endif
+ ret_code = partsize (bh, (unsigned long) size, (unsigned int *) ip + 2,
+ (unsigned int *) ip + 0, (unsigned int *) ip + 1);
+ brelse (bh);
+
+ if (ret_code == -1) {
+#ifdef DEBUG
+ printk ("scsicam_bios_param : trying optimal mapping\n");
+#endif
+ ret_code = setsize ((unsigned long) size, (unsigned int *) ip + 2,
+ (unsigned int *) ip + 0, (unsigned int *) ip + 1);
+ }
+
+ return ret_code;
+}
+
+/*
+ * Function : static int partsize(struct buffer_head *bh, unsigned long
+ * capacity,unsigned int *cyls, unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine the BIOS mapping used to create the partition
+ * table, storing the results in *cyls, *hds, and *secs
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ */
+
+static int partsize(struct buffer_head *bh, unsigned long capacity,
+ unsigned int *cyls, unsigned int *hds, unsigned int *secs) {
+ struct partition *p, *largest = NULL;
+ int i, largest_cyl;
+ int cyl, ext_cyl, end_head, end_cyl, end_sector;
+ unsigned int logical_end, physical_end, ext_physical_end;
+
+
+ if (*(unsigned short *) (bh->b_data+510) == 0xAA55) {
+ for (largest_cyl = -1, p = (struct partition *)
+ (0x1BE + bh->b_data), i = 0; i < 4; ++i, ++p) {
+ if (!p->sys_ind)
+ continue;
+#ifdef DEBUG
+ printk ("scsicam_bios_param : partition %d has system \n",
+ i);
+#endif
+ cyl = p->cyl + ((p->sector & 0xc0) << 2);
+ if (cyl > largest_cyl) {
+ largest_cyl = cyl;
+ largest = p;
+ }
+ }
+ }
+
+ if (largest) {
+ end_cyl = largest->end_cyl + ((largest->end_sector & 0xc0) << 2);
+ end_head = largest->end_head;
+ end_sector = largest->end_sector & 0x3f;
+
+#ifdef DEBUG
+ printk ("scsicam_bios_param : end at h = %d, c = %d, s = %d\n",
+ end_head, end_cyl, end_sector);
+#endif
+
+ physical_end = end_cyl * (end_head + 1) * end_sector +
+ end_head * end_sector + end_sector;
+
+ /* This is the actual _sector_ number at the end */
+ logical_end = largest->start_sect + largest->nr_sects;
+
+ /* This is for >1023 cylinders */
+ ext_cyl= (logical_end-(end_head * end_sector + end_sector))
+ /(end_head + 1) / end_sector;
+ ext_physical_end = ext_cyl * (end_head + 1) * end_sector +
+ end_head * end_sector + end_sector;
+
+#ifdef DEBUG
+ printk("scsicam_bios_param : logical_end=%d physical_end=%d ext_physical_end=%d ext_cyl=%d\n"
+ ,logical_end,physical_end,ext_physical_end,ext_cyl);
+#endif
+
+ if ((logical_end == physical_end) ||
+ (end_cyl==1023 && ext_physical_end==logical_end)) {
+ *secs = end_sector;
+ *hds = end_head + 1;
+ *cyls = capacity / ((end_head + 1) * end_sector);
+ return 0;
+ }
+
+#ifdef DEBUG
+ printk ("scsicam_bios_param : logical (%u) != physical (%u)\n",
+ logical_end, physical_end);
+#endif
+ }
+ return -1;
+}
+
+/*
+ * Function : static int setsize(unsigned long capacity,unsigned int *cyls,
+ * unsigned int *hds, unsigned int *secs);
+ *
+ * Purpose : to determine a near-optimal int 0x13 mapping for a
+ * SCSI disk in terms of lost space of size capacity, storing
+ * the results in *cyls, *hds, and *secs.
+ *
+ * Returns : -1 on failure, 0 on success.
+ *
+ * Extracted from
+ *
+ * WORKING X3T9.2
+ * DRAFT 792D
+ *
+ *
+ * Revision 6
+ * 10-MAR-94
+ * Information technology -
+ * SCSI-2 Common access method
+ * transport and SCSI interface module
+ *
+ * ANNEX A :
+ *
+ * setsize() converts a read capacity value to int 13h
+ * head-cylinder-sector requirements. It minimizes the value for
+ * number of heads and maximizes the number of cylinders. This
+ * will support rather large disks before the number of heads
+ * will not fit in 4 bits (or 6 bits). This algorithm also
+ * minimizes the number of sectors that will be unused at the end
+ * of the disk while allowing for very large disks to be
+ * accommodated. This algorithm does not use physical geometry.
+ */
+
+static int setsize(unsigned long capacity,unsigned int *cyls,unsigned int *hds,
+ unsigned int *secs) {
+ unsigned int rv = 0;
+ unsigned long heads, sectors, cylinders, temp;
+
+ cylinders = 1024L; /* Set number of cylinders to max */
+ sectors = 62L; /* Maximize sectors per track */
+
+ temp = cylinders * sectors; /* Compute divisor for heads */
+ heads = capacity / temp; /* Compute value for number of heads */
+ if (capacity % temp) { /* If no remainder, done! */
+ heads++; /* Else, increment number of heads */
+ temp = cylinders * heads; /* Compute divisor for sectors */
+ sectors = capacity / temp; /* Compute value for sectors per
+ track */
+ if (capacity % temp) { /* If no remainder, done! */
+ sectors++; /* Else, increment number of sectors */
+ temp = heads * sectors; /* Compute divisor for cylinders */
+ cylinders = capacity / temp;/* Compute number of cylinders */
+ }
+ }
+ if (cylinders == 0) rv=(unsigned)-1;/* Give error if 0 cylinders */
+
+ *cyls = (unsigned int) cylinders; /* Stuff return values */
+ *secs = (unsigned int) sectors;
+ *hds = (unsigned int) heads;
+ return(rv);
+}
diff --git a/i386/i386at/gpl/linux/scsi/sd.c b/i386/i386at/gpl/linux/scsi/sd.c
new file mode 100644
index 00000000..47bf2263
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/sd.c
@@ -0,0 +1,1543 @@
+/*
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * Linux scsi disk driver
+ * Initial versions: Drew Eckhardt
+ * Subsequent revisions: Eric Youngdale
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@cais.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
+ */
+
+#include <linux/module.h>
+#ifdef MODULE
+/*
+ * This is a variable in scsi.c that is set when we are processing something
+ * after boot time. By definition, this is true when we are a loadable module
+ * ourselves.
+ */
+#define MODULE_FLAG 1
+#else
+#define MODULE_FLAG scsi_loadable_module_flag
+#endif /* MODULE */
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_DISK_MAJOR
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include "scsi_ioctl.h"
+#include "constants.h"
+
+#include <linux/genhd.h>
+
+/*
+ * static const char RCSid[] = "$Header:";
+ */
+
+#define MAX_RETRIES 5
+
+/*
+ * Time out in seconds for disks and Magneto-opticals (which are slower).
+ */
+
+#define SD_TIMEOUT (7 * HZ)
+#define SD_MOD_TIMEOUT (8 * HZ)
+
+#define CLUSTERABLE_DEVICE(SC) (SC->host->use_clustering && \
+ SC->device->type != TYPE_MOD)
+
+struct hd_struct * sd;
+
+Scsi_Disk * rscsi_disks = NULL;
+static int * sd_sizes;
+static int * sd_blocksizes;
+static int * sd_hardsizes; /* Hardware sector size */
+
+extern int sd_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+static int check_scsidisk_media_change(kdev_t);
+static int fop_revalidate_scsidisk(kdev_t);
+
+static sd_init_onedisk(int);
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt);
+
+static int sd_init(void);
+static void sd_finish(void);
+static int sd_attach(Scsi_Device *);
+static int sd_detect(Scsi_Device *);
+static void sd_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sd_template =
+{ NULL, "disk", "sd", NULL, TYPE_DISK,
+ SCSI_DISK_MAJOR, 0, 0, 0, 1,
+ sd_detect, sd_init,
+ sd_finish, sd_attach, sd_detach
+};
+
+static int sd_open(struct inode * inode, struct file * filp)
+{
+ int target;
+ target = DEVICE_NR(inode->i_rdev);
+
+ if(target >= sd_template.dev_max || !rscsi_disks[target].device)
+ return -ENXIO; /* No such device */
+
+ /*
+ * Make sure that only one process can do a check_change_disk at one time.
+ * This is also used to lock out further access when the partition table
+ * is being re-read.
+ */
+
+ while (rscsi_disks[target].device->busy)
+ barrier();
+ if(rscsi_disks[target].device->removable) {
+ check_disk_change(inode->i_rdev);
+
+ /*
+ * If the drive is empty, just let the open fail.
+ */
+ if ( !rscsi_disks[target].ready ) {
+ return -ENXIO;
+ }
+
+ /*
+ * Similarily, if the device has the write protect tab set,
+ * have the open fail if the user expects to be able to write
+ * to the thing.
+ */
+ if ( (rscsi_disks[target].write_prot) && (filp->f_mode & 2) ) {
+ return -EROFS;
+ }
+
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ };
+
+ /*
+ * See if we are requesting a non-existent partition. Do this
+ * after checking for disk change.
+ */
+ if(sd_sizes[MINOR(inode->i_rdev)] == 0)
+ return -ENXIO;
+
+ rscsi_disks[target].device->access_count++;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)++;
+ if(sd_template.usage_count) (*sd_template.usage_count)++;
+ return 0;
+}
+
+static void sd_release(struct inode * inode, struct file * file)
+{
+ int target;
+ sync_dev(inode->i_rdev);
+
+ target = DEVICE_NR(inode->i_rdev);
+
+ rscsi_disks[target].device->access_count--;
+ if (rscsi_disks[target].device->host->hostt->usage_count)
+ (*rscsi_disks[target].device->host->hostt->usage_count)--;
+ if(sd_template.usage_count) (*sd_template.usage_count)--;
+
+ if(rscsi_disks[target].device->removable) {
+ if(!rscsi_disks[target].device->access_count)
+ sd_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ }
+}
+
+static void sd_geninit(struct gendisk *);
+
+static struct file_operations sd_fops = {
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sd_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sd_open, /* open code */
+ sd_release, /* release */
+ block_fsync, /* fsync */
+ NULL, /* fasync */
+ check_scsidisk_media_change, /* Disk change */
+ fop_revalidate_scsidisk /* revalidate */
+};
+
+static struct gendisk sd_gendisk = {
+ MAJOR_NR, /* Major number */
+ "sd", /* Major name */
+ 4, /* Bits to shift to get real from partition */
+ 1 << 4, /* Number of partitions per real */
+ 0, /* maximum number of real */
+ sd_geninit, /* init function */
+ NULL, /* hd struct */
+ NULL, /* block sizes */
+ 0, /* number */
+ NULL, /* internal */
+ NULL /* next */
+};
+
+static void sd_geninit (struct gendisk *ignored)
+{
+ int i;
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if(rscsi_disks[i].device)
+ sd[i << 4].nr_sects = rscsi_disks[i].capacity;
+#if 0
+ /* No longer needed - we keep track of this as we attach/detach */
+ sd_gendisk.nr_real = sd_template.dev_max;
+#endif
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver. It will
+ * be notified on the end of a SCSI read / write, and
+ * will take on of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd *SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->bufflen >> 9;
+
+#ifdef DEBUG
+ printk("sd%c : rw_intr(%d, %d)\n", 'a' + MINOR(SCpnt->request.rq_dev),
+ SCpnt->host->host_no, result);
+#endif
+
+ /*
+ * First case : we assume that the command succeeded. One of two things
+ * will happen here. Either we will be finished, or there will be more
+ * sectors that we were unable to read last time.
+ */
+
+ if (!result) {
+
+#ifdef DEBUG
+ printk("sd%c : %d sectors remain.\n", 'a' + MINOR(SCpnt->request.rq_dev),
+ SCpnt->request.nr_sectors);
+ printk("use_sg is %d\n ",SCpnt->use_sg);
+#endif
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+ printk(":%x %x %d\n",sgpnt[i].alt_address, sgpnt[i].address,
+ sgpnt[i].length);
+#endif
+ if (sgpnt[i].alt_address) {
+ if (SCpnt->request.cmd == READ)
+ memcpy(sgpnt[i].alt_address, sgpnt[i].address,
+ sgpnt[i].length);
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ };
+ };
+
+ /* Free list of scatter-gather pointers */
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len);
+ } else {
+ if (SCpnt->buffer != SCpnt->request.buffer) {
+#ifdef DEBUG
+ printk("nosg: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (SCpnt->request.cmd == READ)
+ memcpy(SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ };
+ };
+ /*
+ * If multiple sectors are requested in one buffer, then
+ * they will have been finished off by the first command.
+ * If not, then we have a multi-buffer command.
+ */
+ if (SCpnt->request.nr_sectors > this_count)
+ {
+ SCpnt->request.errors = 0;
+
+ if (!SCpnt->request.bh)
+ {
+#ifdef DEBUG
+ printk("sd%c : handling page request, no buffer\n",
+ 'a' + MINOR(SCpnt->request.rq_dev));
+#endif
+ /*
+ * The SCpnt->request.nr_sectors field is always done in
+ * 512 byte sectors, even if this really isn't the case.
+ */
+ panic("sd.c: linked page request (%lx %x)",
+ SCpnt->request.sector, this_count);
+ }
+ }
+ SCpnt = end_scsi_request(SCpnt, 1, this_count);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+
+ /* Free up any indirection buffers we allocated for DMA purposes. */
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+#ifdef DEBUG
+ printk("err: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (sgpnt[i].alt_address) {
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ };
+ };
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ } else {
+#ifdef DEBUG
+ printk("nosgerr: %x %x %d\n",SCpnt->request.buffer, SCpnt->buffer,
+ SCpnt->bufflen);
+#endif
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ };
+
+ /*
+ * Now, if we were good little boys and girls, Santa left us a request
+ * sense buffer. We can extract information from this, so we
+ * can choose a block to remap, etc.
+ */
+
+ if (driver_byte(result) != 0) {
+ if (suggestion(result) == SUGGEST_REMAP) {
+#ifdef REMAP
+ /*
+ * Not yet implemented. A read will fail after being remapped,
+ * a write will call the strategy routine again.
+ */
+ if rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].remap
+ {
+ result = 0;
+ }
+ else
+#endif
+ }
+
+ if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+ if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ if(rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->removable) {
+ /* detected disc change. set a bit and quietly refuse
+ * further access.
+ */
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ else
+ {
+ /*
+ * Must have been a power glitch, or a bus reset.
+ * Could not have been a media change, so we just retry
+ * the request and see what happens.
+ */
+ requeue_sd_request(SCpnt);
+ return;
+ }
+ }
+ }
+
+
+ /* If we had an ILLEGAL REQUEST returned, then we may have
+ * performed an unsupported command. The only thing this should be
+ * would be a ten byte read where only a six byte read was supported.
+ * Also, on a system where READ CAPACITY failed, we have have read
+ * past the end of the disk.
+ */
+
+ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+ if (rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].ten) {
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;
+ requeue_sd_request(SCpnt);
+ result = 0;
+ } else {
+ /* ???? */
+ }
+ }
+ } /* driver byte != 0 */
+ if (result) {
+ printk("SCSI disk error : host %d channel %d id %d lun %d return code = %x\n",
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->channel,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+ rscsi_disks[DEVICE_NR(SCpnt->request.rq_dev)].device->lun, result);
+
+ if (driver_byte(result) & DRIVER_SENSE)
+ print_sense("sd", SCpnt);
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sd_request(SCpnt);
+ return;
+ }
+}
+
+/*
+ * requeue_sd_request() is the request handler function for the sd driver.
+ * Its function in life is to take block device requests, and translate
+ * them to SCSI commands.
+ */
+
+static void do_sd_request (void)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ Scsi_Device * SDev;
+ struct request * req = NULL;
+ unsigned long flags;
+ int flag = 0;
+
+ save_flags(flags);
+ while (1==1){
+ cli();
+ if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+ restore_flags(flags);
+ return;
+ };
+
+ INIT_SCSI_REQUEST;
+ SDev = rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device;
+
+ /*
+ * I am not sure where the best place to do this is. We need
+ * to hook in a place where we are likely to come if in user
+ * space.
+ */
+ if( SDev->was_reset )
+ {
+ /*
+ * We need to relock the door, but we might
+ * be in an interrupt handler. Only do this
+ * from user space, since we do not want to
+ * sleep from an interrupt.
+ */
+ if( SDev->removable && !intr_count )
+ {
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ }
+ SDev->was_reset = 0;
+ }
+
+ /* We have to be careful here. allocate_device will get a free pointer,
+ * but there is no guarantee that it is queueable. In normal usage,
+ * we want to call this, because other types of devices may have the
+ * host all tied up, and we want to make sure that we have at least
+ * one request pending for this type of device. We can also come
+ * through here while servicing an interrupt, because of the need to
+ * start another command. If we call allocate_device more than once,
+ * then the system can wedge if the command is not queueable. The
+ * request_queueable function is safe because it checks to make sure
+ * that the host is able to take another command before it returns
+ * a pointer.
+ */
+
+ if (flag++ == 0)
+ SCpnt = allocate_device(&CURRENT,
+ rscsi_disks[DEVICE_NR(CURRENT->rq_dev)].device, 0);
+ else SCpnt = NULL;
+
+ /*
+ * The following restore_flags leads to latency problems. FIXME.
+ * Using a "sti()" gets rid of the latency problems but causes
+ * race conditions and crashes.
+ */
+ restore_flags(flags);
+
+ /* This is a performance enhancement. We dig down into the request
+ * list and try and find a queueable request (i.e. device not busy,
+ * and host able to accept another command. If we find one, then we
+ * queue it. This can make a big difference on systems with more than
+ * one disk drive. We want to have the interrupts off when monkeying
+ * with the request list, because otherwise the kernel might try and
+ * slip in a request in between somewhere.
+ */
+
+ if (!SCpnt && sd_template.nr_dev > 1){
+ struct request *req1;
+ req1 = NULL;
+ cli();
+ req = CURRENT;
+ while(req){
+ SCpnt = request_queueable(req, rscsi_disks[DEVICE_NR(req->rq_dev)].device);
+ if(SCpnt) break;
+ req1 = req;
+ req = req->next;
+ };
+ if (SCpnt && req->rq_status == RQ_INACTIVE) {
+ if (req == CURRENT)
+ CURRENT = CURRENT->next;
+ else
+ req1->next = req->next;
+ };
+ restore_flags(flags);
+ };
+
+ if (!SCpnt) return; /* Could not find anything to do */
+
+ /* Queue command */
+ requeue_sd_request(SCpnt);
+ }; /* While */
+}
+
+static void requeue_sd_request (Scsi_Cmnd * SCpnt)
+{
+ int dev, devm, block, this_count;
+ unsigned char cmd[10];
+ int bounce_size, contiguous;
+ int max_sg;
+ struct buffer_head * bh, *bhp;
+ char * buff, *bounce_buffer;
+
+ repeat:
+
+ if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+ do_sd_request();
+ return;
+ }
+
+ devm = MINOR(SCpnt->request.rq_dev);
+ dev = DEVICE_NR(SCpnt->request.rq_dev);
+
+ block = SCpnt->request.sector;
+ this_count = 0;
+
+#ifdef DEBUG
+ printk("Doing sd request, dev = %d, block = %d\n", devm, block);
+#endif
+
+ if (devm >= (sd_template.dev_max << 4) ||
+ !rscsi_disks[dev].device ||
+ block + SCpnt->request.nr_sectors > sd[devm].nr_sects)
+ {
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+ block += sd[devm].start_sect;
+
+ if (rscsi_disks[dev].device->changed)
+ {
+ /*
+ * quietly refuse to do anything to a changed disc until the changed
+ * bit has been reset
+ */
+ /* printk("SCSI disk has been changed. Prohibiting further I/O.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+#ifdef DEBUG
+ printk("sd%c : real dev = /dev/sd%c, block = %d\n",
+ 'a' + devm, dev, block);
+#endif
+
+ /*
+ * If we have a 1K hardware sectorsize, prevent access to single
+ * 512 byte sectors. In theory we could handle this - in fact
+ * the scsi cdrom driver must be able to handle this because
+ * we typically use 1K blocksizes, and cdroms typically have
+ * 2K hardware sectorsizes. Of course, things are simpler
+ * with the cdrom, since it is read-only. For performance
+ * reasons, the filesystems should be able to handle this
+ * and not force the scsi disk driver to use bounce buffers
+ * for this.
+ */
+ if (rscsi_disks[dev].sector_size == 1024)
+ if((block & 1) || (SCpnt->request.nr_sectors & 1)) {
+ printk("sd.c:Bad block number requested");
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+
+ switch (SCpnt->request.cmd)
+ {
+ case WRITE :
+ if (!rscsi_disks[dev].device->writeable)
+ {
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ }
+ cmd[0] = WRITE_6;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ panic ("Unknown sd command %d\n", SCpnt->request.cmd);
+ }
+
+ SCpnt->this_count = 0;
+
+ /* If the host adapter can deal with very large scatter-gather
+ * requests, it is a waste of time to cluster
+ */
+ contiguous = (!CLUSTERABLE_DEVICE(SCpnt) ? 0 :1);
+ bounce_buffer = NULL;
+ bounce_size = (SCpnt->request.nr_sectors << 9);
+
+ /* First see if we need a bounce buffer for this request. If we do, make
+ * sure that we can allocate a buffer. Do not waste space by allocating
+ * a bounce buffer if we are straddling the 16Mb line
+ */
+ if (contiguous && SCpnt->request.bh &&
+ ((long) SCpnt->request.bh->b_data)
+ + (SCpnt->request.nr_sectors << 9) - 1 > ISA_DMA_THRESHOLD
+ && SCpnt->host->unchecked_isa_dma) {
+ if(((long) SCpnt->request.bh->b_data) > ISA_DMA_THRESHOLD)
+ bounce_buffer = (char *) scsi_malloc(bounce_size);
+ if(!bounce_buffer) contiguous = 0;
+ };
+
+ if(contiguous && SCpnt->request.bh && SCpnt->request.bh->b_reqnext)
+ for(bh = SCpnt->request.bh, bhp = bh->b_reqnext; bhp; bh = bhp,
+ bhp = bhp->b_reqnext) {
+ if(!CONTIGUOUS_BUFFERS(bh,bhp)) {
+ if(bounce_buffer) scsi_free(bounce_buffer, bounce_size);
+ contiguous = 0;
+ break;
+ }
+ };
+ if (!SCpnt->request.bh || contiguous) {
+
+ /* case of page request (i.e. raw device), or unlinked buffer */
+ this_count = SCpnt->request.nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+
+ } else if (SCpnt->host->sg_tablesize == 0 ||
+ (need_isa_buffer && dma_free_sectors <= 10)) {
+
+ /* Case of host adapter that cannot scatter-gather. We also
+ * come here if we are running low on DMA buffer memory. We set
+ * a threshold higher than that we would need for this request so
+ * we leave room for other requests. Even though we would not need
+ * it all, we need to be conservative, because if we run low enough
+ * we have no choice but to panic.
+ */
+ if (SCpnt->host->sg_tablesize != 0 &&
+ need_isa_buffer &&
+ dma_free_sectors <= 10)
+ printk("Warning: SCSI DMA buffer space running low. Using non scatter-gather I/O.\n");
+
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+
+ } else {
+
+ /* Scatter-gather capable host adapter */
+ struct scatterlist * sgpnt;
+ int count, this_count_max;
+ int counted;
+
+ bh = SCpnt->request.bh;
+ this_count = 0;
+ this_count_max = (rscsi_disks[dev].ten ? 0xffff : 0xff);
+ count = 0;
+ bhp = NULL;
+ while(bh) {
+ if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+ if(!bhp || !CONTIGUOUS_BUFFERS(bhp,bh) ||
+ !CLUSTERABLE_DEVICE(SCpnt) ||
+ (SCpnt->host->unchecked_isa_dma &&
+ ((unsigned long) bh->b_data-1) == ISA_DMA_THRESHOLD)) {
+ if (count < SCpnt->host->sg_tablesize) count++;
+ else break;
+ };
+ this_count += (bh->b_size >> 9);
+ bhp = bh;
+ bh = bh->b_reqnext;
+ };
+#if 0
+ if(SCpnt->host->unchecked_isa_dma &&
+ ((unsigned int) SCpnt->request.bh->b_data-1) == ISA_DMA_THRESHOLD) count--;
+#endif
+ SCpnt->use_sg = count; /* Number of chains */
+ count = 512;/* scsi_malloc can only allocate in chunks of 512 bytes */
+ while( count < (SCpnt->use_sg * sizeof(struct scatterlist)))
+ count = count << 1;
+ SCpnt->sglist_len = count;
+ max_sg = count / sizeof(struct scatterlist);
+ if(SCpnt->host->sg_tablesize < max_sg)
+ max_sg = SCpnt->host->sg_tablesize;
+ sgpnt = (struct scatterlist * ) scsi_malloc(count);
+ if (!sgpnt) {
+ printk("Warning - running *really* short on DMA buffers\n");
+ SCpnt->use_sg = 0; /* No memory left - bail out */
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ } else {
+ memset(sgpnt, 0, count); /* Zero so it is easy to fill, but only
+ * if memory is available
+ */
+ buff = (char *) sgpnt;
+ counted = 0;
+ for(count = 0, bh = SCpnt->request.bh, bhp = bh->b_reqnext;
+ count < SCpnt->use_sg && bh;
+ count++, bh = bhp) {
+
+ bhp = bh->b_reqnext;
+
+ if(!sgpnt[count].address) sgpnt[count].address = bh->b_data;
+ sgpnt[count].length += bh->b_size;
+ counted += bh->b_size >> 9;
+
+ if (((long) sgpnt[count].address) + sgpnt[count].length - 1 >
+ ISA_DMA_THRESHOLD && (SCpnt->host->unchecked_isa_dma) &&
+ !sgpnt[count].alt_address) {
+ sgpnt[count].alt_address = sgpnt[count].address;
+ /* We try and avoid exhausting the DMA pool, since it is
+ * easier to control usage here. In other places we might
+ * have a more pressing need, and we would be screwed if
+ * we ran out */
+ if(dma_free_sectors < (sgpnt[count].length >> 9) + 10) {
+ sgpnt[count].address = NULL;
+ } else {
+ sgpnt[count].address =
+ (char *) scsi_malloc(sgpnt[count].length);
+ };
+ /* If we start running low on DMA buffers, we abort the
+ * scatter-gather operation, and free all of the memory
+ * we have allocated. We want to ensure that all scsi
+ * operations are able to do at least a non-scatter/gather
+ * operation */
+ if(sgpnt[count].address == NULL){ /* Out of dma memory */
+#if 0
+ printk("Warning: Running low on SCSI DMA buffers");
+ /* Try switching back to a non s-g operation. */
+ while(--count >= 0){
+ if(sgpnt[count].alt_address)
+ scsi_free(sgpnt[count].address,
+ sgpnt[count].length);
+ };
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = SCpnt->request.buffer;
+ SCpnt->use_sg = 0;
+ scsi_free(sgpnt, SCpnt->sglist_len);
+#endif
+ SCpnt->use_sg = count;
+ this_count = counted -= bh->b_size >> 9;
+ break;
+ };
+
+ };
+
+ /* Only cluster buffers if we know that we can supply DMA
+ * buffers large enough to satisfy the request. Do not cluster
+ * a new request if this would mean that we suddenly need to
+ * start using DMA bounce buffers */
+ if(bhp && CONTIGUOUS_BUFFERS(bh,bhp)
+ && CLUSTERABLE_DEVICE(SCpnt)) {
+ char * tmp;
+
+ if (((long) sgpnt[count].address) + sgpnt[count].length +
+ bhp->b_size - 1 > ISA_DMA_THRESHOLD &&
+ (SCpnt->host->unchecked_isa_dma) &&
+ !sgpnt[count].alt_address) continue;
+
+ if(!sgpnt[count].alt_address) {count--; continue; }
+ if(dma_free_sectors > 10)
+ tmp = (char *) scsi_malloc(sgpnt[count].length
+ + bhp->b_size);
+ else {
+ tmp = NULL;
+ max_sg = SCpnt->use_sg;
+ };
+ if(tmp){
+ scsi_free(sgpnt[count].address, sgpnt[count].length);
+ sgpnt[count].address = tmp;
+ count--;
+ continue;
+ };
+
+ /* If we are allowed another sg chain, then increment
+ * counter so we can insert it. Otherwise we will end
+ up truncating */
+
+ if (SCpnt->use_sg < max_sg) SCpnt->use_sg++;
+ }; /* contiguous buffers */
+ }; /* for loop */
+
+ /* This is actually how many we are going to transfer */
+ this_count = counted;
+
+ if(count < SCpnt->use_sg || SCpnt->use_sg
+ > SCpnt->host->sg_tablesize){
+ bh = SCpnt->request.bh;
+ printk("Use sg, count %d %x %d\n",
+ SCpnt->use_sg, count, dma_free_sectors);
+ printk("maxsg = %x, counted = %d this_count = %d\n",
+ max_sg, counted, this_count);
+ while(bh){
+ printk("[%p %lx] ", bh->b_data, bh->b_size);
+ bh = bh->b_reqnext;
+ };
+ if(SCpnt->use_sg < 16)
+ for(count=0; count<SCpnt->use_sg; count++)
+ printk("{%d:%p %p %d} ", count,
+ sgpnt[count].address,
+ sgpnt[count].alt_address,
+ sgpnt[count].length);
+ panic("Ooops");
+ };
+
+ if (SCpnt->request.cmd == WRITE)
+ for(count=0; count<SCpnt->use_sg; count++)
+ if(sgpnt[count].alt_address)
+ memcpy(sgpnt[count].address, sgpnt[count].alt_address,
+ sgpnt[count].length);
+ }; /* Able to malloc sgpnt */
+ }; /* Host adapter capable of scatter-gather */
+
+ /* Now handle the possibility of DMA to addresses > 16Mb */
+
+ if(SCpnt->use_sg == 0){
+ if (((long) buff) + (this_count << 9) - 1 > ISA_DMA_THRESHOLD &&
+ (SCpnt->host->unchecked_isa_dma)) {
+ if(bounce_buffer)
+ buff = bounce_buffer;
+ else
+ buff = (char *) scsi_malloc(this_count << 9);
+ if(buff == NULL) { /* Try backing off a bit if we are low on mem*/
+ this_count = SCpnt->request.current_nr_sectors;
+ buff = (char *) scsi_malloc(this_count << 9);
+ if(!buff) panic("Ran out of DMA buffers.");
+ };
+ if (SCpnt->request.cmd == WRITE)
+ memcpy(buff, (char *)SCpnt->request.buffer, this_count << 9);
+ };
+ };
+#ifdef DEBUG
+ printk("sd%c : %s %d/%d 512 byte blocks.\n",
+ 'a' + devm,
+ (SCpnt->request.cmd == WRITE) ? "writing" : "reading",
+ this_count, SCpnt->request.nr_sectors);
+#endif
+
+ cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+ if (rscsi_disks[dev].sector_size == 1024){
+ if(block & 1) panic("sd.c:Bad block number requested");
+ if(this_count & 1) panic("sd.c:Bad block number requested");
+ block = block >> 1;
+ this_count = this_count >> 1;
+ };
+
+ if (rscsi_disks[dev].sector_size == 256){
+ block = block << 1;
+ this_count = this_count << 1;
+ };
+
+ if (((this_count > 0xff) || (block > 0x1fffff)) && rscsi_disks[dev].ten)
+ {
+ if (this_count > 0xffff)
+ this_count = 0xffff;
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (this_count >> 8) & 0xff;
+ cmd[8] = (unsigned char) this_count & 0xff;
+ }
+ else
+ {
+ if (this_count > 0xff)
+ this_count = 0xff;
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) this_count;
+ cmd[5] = 0;
+ }
+
+ /*
+ * We shouldn't disconnect in the middle of a sector, so with a dumb
+ * host adapter, it's safe to assume that we can at least transfer
+ * this many bytes between each connect / disconnect.
+ */
+
+ SCpnt->transfersize = rscsi_disks[dev].sector_size;
+ SCpnt->underflow = this_count << 9;
+ scsi_do_cmd (SCpnt, (void *) cmd, buff,
+ this_count * rscsi_disks[dev].sector_size,
+ rw_intr,
+ (SCpnt->device->type == TYPE_DISK ?
+ SD_TIMEOUT : SD_MOD_TIMEOUT),
+ MAX_RETRIES);
+}
+
+static int check_scsidisk_media_change(kdev_t full_dev){
+ int retval;
+ int target;
+ struct inode inode;
+ int flag = 0;
+
+ target = DEVICE_NR(full_dev);
+
+ if (target >= sd_template.dev_max ||
+ !rscsi_disks[target].device) {
+ printk("SCSI disk request error: invalid device.\n");
+ return 0;
+ };
+
+ if(!rscsi_disks[target].device->removable) return 0;
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+ retval = sd_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+
+ rscsi_disks[target].ready = 0;
+ rscsi_disks[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ };
+
+ /*
+ * for removable scsi disk ( FLOPTICAL ) we have to recognise the
+ * presence of disk in the drive. This is kept in the Scsi_Disk
+ * struct and tested at open ! Daniel Roche ( dan@lectra.fr )
+ */
+
+ rscsi_disks[target].ready = 1; /* FLOPTICAL */
+
+ retval = rscsi_disks[target].device->changed;
+ if(!flag) rscsi_disks[target].device->changed = 0;
+ return retval;
+}
+
+static void sd_init_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+static int sd_init_onedisk(int i)
+{
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ unsigned long spintime;
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ /* We need to retry the READ_CAPACITY because a UNIT_ATTENTION is
+ * considered a fatal error, and many devices report such an error
+ * just after a scsi bus reset.
+ */
+
+ SCpnt = allocate_device(NULL, rscsi_disks[i].device, 1);
+ buffer = (unsigned char *) scsi_malloc(512);
+
+ spintime = 0;
+
+ /* Spin up drives, as required. Only do this at boot time */
+ if (!MODULE_FLAG){
+ do{
+ retries = 0;
+ while(retries < 3)
+ {
+ cmd[0] = TEST_UNIT_READY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ retries++;
+ if( the_result == 0
+ || SCpnt->sense_buffer[2] != UNIT_ATTENTION)
+ break;
+ }
+
+ /* Look for non-removable devices that return NOT_READY.
+ * Issue command to spin up drive for these cases. */
+ if(the_result && !rscsi_disks[i].device->removable &&
+ SCpnt->sense_buffer[2] == NOT_READY) {
+ int time1;
+ if(!spintime){
+ printk( "sd%c: Spinning up disk...", 'a' + i );
+ cmd[0] = START_STOP;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ cmd[1] |= 1; /* Return immediately */
+ memset ((void *) &cmd[2], 0, 8);
+ cmd[4] = 1; /* Start spin cycle */
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ spintime = jiffies;
+ }
+
+ time1 = jiffies;
+ while(jiffies < time1 + HZ); /* Wait 1 second for next try */
+ printk( "." );
+ };
+ } while(the_result && spintime && spintime+100*HZ > jiffies);
+ if (spintime) {
+ if (the_result)
+ printk( "not responding...\n" );
+ else
+ printk( "ready\n" );
+ }
+ }; /* !MODULE_FLAG */
+
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ memset ((void *) buffer, 0, 8);
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ /* Mark as really busy again */
+ SCpnt->request.rq_status = RQ_SCSI_BUSY;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 8, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem); /* sleep until it is ready */
+ }
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+
+ wake_up(&SCpnt->device->device_wait);
+
+ /* Wake up a process waiting for device */
+
+ /*
+ * The SCSI standard says:
+ * "READ CAPACITY is necessary for self configuring software"
+ * While not mandatory, support of READ CAPACITY is strongly encouraged.
+ * We used to die if we couldn't successfully do a READ CAPACITY.
+ * But, now we go on about our way. The side effects of this are
+ *
+ * 1. We can't know block size with certainty. I have said "512 bytes
+ * is it" as this is most common.
+ *
+ * 2. Recovery from when some one attempts to read past the end of the
+ * raw device will be slower.
+ */
+
+ if (the_result)
+ {
+ printk ("sd%c : READ CAPACITY failed.\n"
+ "sd%c : status = %x, message = %02x, host = %d, driver = %02x \n",
+ 'a' + i, 'a' + i,
+ status_byte(the_result),
+ msg_byte(the_result),
+ host_byte(the_result),
+ driver_byte(the_result)
+ );
+ if (driver_byte(the_result) & DRIVER_SENSE)
+ printk("sd%c : extended sense code = %1x \n",
+ 'a' + i, SCpnt->sense_buffer[2] & 0xf);
+ else
+ printk("sd%c : sense not available. \n", 'a' + i);
+
+ printk("sd%c : block size assumed to be 512 bytes, disk size 1GB. \n",
+ 'a' + i);
+ rscsi_disks[i].capacity = 0x1fffff;
+ rscsi_disks[i].sector_size = 512;
+
+ /* Set dirty bit for removable devices if not ready - sometimes drives
+ * will not report this properly. */
+ if(rscsi_disks[i].device->removable &&
+ SCpnt->sense_buffer[2] == NOT_READY)
+ rscsi_disks[i].device->changed = 1;
+
+ }
+ else
+ {
+ /*
+ * FLOPTICAL , if read_capa is ok , drive is assumed to be ready
+ */
+ rscsi_disks[i].ready = 1;
+
+ rscsi_disks[i].capacity = (buffer[0] << 24) |
+ (buffer[1] << 16) |
+ (buffer[2] << 8) |
+ buffer[3];
+
+ rscsi_disks[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+
+ if (rscsi_disks[i].sector_size == 0) {
+ rscsi_disks[i].sector_size = 512;
+ printk("sd%c : sector size 0 reported, assuming 512.\n", 'a' + i);
+ }
+
+
+ if (rscsi_disks[i].sector_size != 512 &&
+ rscsi_disks[i].sector_size != 1024 &&
+ rscsi_disks[i].sector_size != 256)
+ {
+ printk ("sd%c : unsupported sector size %d.\n",
+ 'a' + i, rscsi_disks[i].sector_size);
+ if(rscsi_disks[i].device->removable){
+ rscsi_disks[i].capacity = 0;
+ } else {
+ printk ("scsi : deleting disk entry.\n");
+ rscsi_disks[i].device = NULL;
+ sd_template.nr_dev--;
+ return i;
+ };
+ }
+ {
+ /*
+ * The msdos fs need to know the hardware sector size
+ * So I have created this table. See ll_rw_blk.c
+ * Jacques Gelinas (Jacques@solucorp.qc.ca)
+ */
+ int m;
+ int hard_sector = rscsi_disks[i].sector_size;
+ /* There is 16 minor allocated for each devices */
+ for (m=i<<4; m<((i+1)<<4); m++){
+ sd_hardsizes[m] = hard_sector;
+ }
+ printk ("SCSI Hardware sector size is %d bytes on device sd%c\n",
+ hard_sector,i+'a');
+ }
+ if(rscsi_disks[i].sector_size == 1024)
+ rscsi_disks[i].capacity <<= 1; /* Change into 512 byte sectors */
+ if(rscsi_disks[i].sector_size == 256)
+ rscsi_disks[i].capacity >>= 1; /* Change into 512 byte sectors */
+ }
+
+
+ /*
+ * Unless otherwise specified, this is not write protected.
+ */
+ rscsi_disks[i].write_prot = 0;
+ if ( rscsi_disks[i].device->removable && rscsi_disks[i].ready ) {
+ /* FLOPTICAL */
+
+ /*
+ * for removable scsi disk ( FLOPTICAL ) we have to recognise
+ * the Write Protect Flag. This flag is kept in the Scsi_Disk struct
+ * and tested at open !
+ * Daniel Roche ( dan@lectra.fr )
+ */
+
+ memset ((void *) &cmd[0], 0, 8);
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (rscsi_disks[i].device->lun << 5) & 0xe0;
+ cmd[2] = 1; /* page code 1 ?? */
+ cmd[4] = 12;
+ SCpnt->cmd_len = 0;
+ SCpnt->sense_buffer[0] = 0;
+ SCpnt->sense_buffer[2] = 0;
+
+ /* same code as READCAPA !! */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy again */
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sd_init_done, SD_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+ wake_up(&SCpnt->device->device_wait);
+
+ if ( the_result ) {
+ printk ("sd%c: test WP failed, assume Write Protected\n",i+'a');
+ rscsi_disks[i].write_prot = 1;
+ } else {
+ rscsi_disks[i].write_prot = ((buffer[2] & 0x80) != 0);
+ printk ("sd%c: Write Protect is %s\n",i+'a',
+ rscsi_disks[i].write_prot ? "on" : "off");
+ }
+
+ } /* check for write protect */
+
+ rscsi_disks[i].ten = 1;
+ rscsi_disks[i].remap = 1;
+ scsi_free(buffer, 512);
+ return i;
+}
+
+/*
+ * The sd_init() function looks at all SCSI drives present, determines
+ * their size, and reads partition table entries for them.
+ */
+
+static int sd_registered = 0;
+
+static int sd_init()
+{
+ int i;
+
+ if (sd_template.dev_noticed == 0) return 0;
+
+ if(!sd_registered) {
+ if (register_blkdev(MAJOR_NR,"sd",&sd_fops)) {
+ printk("Unable to get major %d for SCSI disk\n",MAJOR_NR);
+ return 1;
+ }
+ sd_registered++;
+ }
+
+ /* We do not support attaching loadable devices yet. */
+ if(rscsi_disks) return 0;
+
+ sd_template.dev_max = sd_template.dev_noticed + SD_EXTRA_DEVS;
+
+ rscsi_disks = (Scsi_Disk *)
+ scsi_init_malloc(sd_template.dev_max * sizeof(Scsi_Disk), GFP_ATOMIC);
+ memset(rscsi_disks, 0, sd_template.dev_max * sizeof(Scsi_Disk));
+
+ sd_sizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+ memset(sd_sizes, 0, (sd_template.dev_max << 4) * sizeof(int));
+
+ sd_blocksizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+
+ sd_hardsizes = (int *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(int), GFP_ATOMIC);
+
+ for(i=0;i<(sd_template.dev_max << 4);i++){
+ sd_blocksizes[i] = 1024;
+ sd_hardsizes[i] = 512;
+ }
+ blksize_size[MAJOR_NR] = sd_blocksizes;
+ hardsect_size[MAJOR_NR] = sd_hardsizes;
+ sd = (struct hd_struct *) scsi_init_malloc((sd_template.dev_max << 4) *
+ sizeof(struct hd_struct),
+ GFP_ATOMIC);
+
+
+ sd_gendisk.max_nr = sd_template.dev_max;
+ sd_gendisk.part = sd;
+ sd_gendisk.sizes = sd_sizes;
+ sd_gendisk.real_devices = (void *) rscsi_disks;
+ return 0;
+}
+
+static void sd_finish()
+{
+ int i;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+
+ sd_gendisk.next = gendisk_head;
+ gendisk_head = &sd_gendisk;
+
+ for (i = 0; i < sd_template.dev_max; ++i)
+ if (!rscsi_disks[i].capacity &&
+ rscsi_disks[i].device)
+ {
+ if (MODULE_FLAG
+ && !rscsi_disks[i].has_part_table) {
+ sd_sizes[i << 4] = rscsi_disks[i].capacity;
+ /* revalidate does sd_init_onedisk via MAYBE_REINIT*/
+ revalidate_scsidisk(MKDEV(MAJOR_NR, i << 4), 0);
+ }
+ else
+ i=sd_init_onedisk(i);
+ rscsi_disks[i].has_part_table = 1;
+ }
+
+ /* If our host adapter is capable of scatter-gather, then we increase
+ * the read-ahead to 16 blocks (32 sectors). If not, we use
+ * a two block (4 sector) read ahead.
+ */
+ if(rscsi_disks[0].device && rscsi_disks[0].device->host->sg_tablesize)
+ read_ahead[MAJOR_NR] = 120; /* 120 sector read-ahead */
+ else
+ read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
+
+ return;
+}
+
+static int sd_detect(Scsi_Device * SDp){
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+ printk("Detected scsi disk sd%c at scsi%d, channel %d, id %d, lun %d\n",
+ 'a'+ (sd_template.dev_noticed++),
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+ return 1;
+}
+
+static int sd_attach(Scsi_Device * SDp){
+ Scsi_Disk * dpnt;
+ int i;
+
+ if(SDp->type != TYPE_DISK && SDp->type != TYPE_MOD) return 0;
+
+ if(sd_template.nr_dev >= sd_template.dev_max) {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(!dpnt->device) break;
+
+ if(i >= sd_template.dev_max) panic ("scsi_devices corrupt (sd)");
+
+ SDp->scsi_request_fn = do_sd_request;
+ rscsi_disks[i].device = SDp;
+ rscsi_disks[i].has_part_table = 0;
+ sd_template.nr_dev++;
+ sd_gendisk.nr_real++;
+ return 0;
+}
+
+#define DEVICE_BUSY rscsi_disks[target].device->busy
+#define USAGE rscsi_disks[target].device->access_count
+#define CAPACITY rscsi_disks[target].capacity
+#define MAYBE_REINIT sd_init_onedisk(target)
+#define GENDISK_STRUCT sd_gendisk
+
+/* This routine is called to flush all partitions and partition tables
+ * for a changed scsi disk, and then re-read the new partition table.
+ * If we are revalidating a disk because of a media change, then we
+ * enter with usage == 0. If we are using an ioctl, we automatically have
+ * usage == 1 (we need an open channel to use an ioctl :-), so this
+ * is our limit.
+ */
+int revalidate_scsidisk(kdev_t dev, int maxusage){
+ int target;
+ struct gendisk * gdev;
+ unsigned long flags;
+ int max_p;
+ int start;
+ int i;
+
+ target = DEVICE_NR(dev);
+ gdev = &GENDISK_STRUCT;
+
+ save_flags(flags);
+ cli();
+ if (DEVICE_BUSY || USAGE > maxusage) {
+ restore_flags(flags);
+ printk("Device busy for revalidation (usage=%d)\n", USAGE);
+ return -EBUSY;
+ };
+ DEVICE_BUSY = 1;
+ restore_flags(flags);
+
+ max_p = gdev->max_p;
+ start = target << gdev->minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start+i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ gdev->part[minor].start_sect = 0;
+ gdev->part[minor].nr_sects = 0;
+ /*
+ * Reset the blocksize for everything so that we can read
+ * the partition table.
+ */
+ blksize_size[MAJOR_NR][minor] = 1024;
+ };
+
+#ifdef MAYBE_REINIT
+ MAYBE_REINIT;
+#endif
+
+ gdev->part[start].nr_sects = CAPACITY;
+ resetup_one_dev(gdev, target);
+
+ DEVICE_BUSY = 0;
+ return 0;
+}
+
+static int fop_revalidate_scsidisk(kdev_t dev){
+ return revalidate_scsidisk(dev, 0);
+}
+
+
+static void sd_detach(Scsi_Device * SDp)
+{
+ Scsi_Disk * dpnt;
+ int i;
+ int max_p;
+ int start;
+
+ for(dpnt = rscsi_disks, i=0; i<sd_template.dev_max; i++, dpnt++)
+ if(dpnt->device == SDp) {
+
+ /* If we are disconnecting a disk driver, sync and invalidate
+ * everything */
+ max_p = sd_gendisk.max_p;
+ start = i << sd_gendisk.minor_shift;
+
+ for (i=max_p - 1; i >=0 ; i--) {
+ int minor = start+i;
+ kdev_t devi = MKDEV(MAJOR_NR, minor);
+ sync_dev(devi);
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+ sd_gendisk.part[minor].start_sect = 0;
+ sd_gendisk.part[minor].nr_sects = 0;
+ sd_sizes[minor] = 0;
+ };
+
+ dpnt->has_part_table = 0;
+ dpnt->device = NULL;
+ dpnt->capacity = 0;
+ SDp->attached--;
+ sd_template.dev_noticed--;
+ sd_template.nr_dev--;
+ sd_gendisk.nr_real--;
+ return;
+ }
+ return;
+}
+
+#ifdef MODULE
+
+int init_module(void) {
+ sd_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sd_template);
+}
+
+void cleanup_module( void)
+{
+ struct gendisk * prev_sdgd;
+ struct gendisk * sdgd;
+
+ scsi_unregister_module(MODULE_SCSI_DEV, &sd_template);
+ unregister_blkdev(SCSI_DISK_MAJOR, "sd");
+ sd_registered--;
+ if( rscsi_disks != NULL )
+ {
+ scsi_init_free((char *) rscsi_disks,
+ (sd_template.dev_noticed + SD_EXTRA_DEVS)
+ * sizeof(Scsi_Disk));
+
+ scsi_init_free((char *) sd_sizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd_blocksizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd_hardsizes, sd_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sd,
+ (sd_template.dev_max << 4) * sizeof(struct hd_struct));
+ /*
+ * Now remove sd_gendisk from the linked list
+ */
+ sdgd = gendisk_head;
+ prev_sdgd = NULL;
+ while(sdgd != &sd_gendisk)
+ {
+ prev_sdgd = sdgd;
+ sdgd = sdgd->next;
+ }
+
+ if(sdgd != &sd_gendisk)
+ printk("sd_gendisk not in disk chain.\n");
+ else {
+ if(prev_sdgd != NULL)
+ prev_sdgd->next = sdgd->next;
+ else
+ gendisk_head = sdgd->next;
+ }
+ }
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ hardsect_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+ sd_template.dev_max = 0;
+}
+#endif /* MODULE */
+
+/*
+ * Overrides for Emacs so that we almost 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/i386/i386at/gpl/linux/scsi/sd.h b/i386/i386at/gpl/linux/scsi/sd.h
new file mode 100644
index 00000000..7a8219b7
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/sd.h
@@ -0,0 +1,65 @@
+/*
+ * sd.h Copyright (C) 1992 Drew Eckhardt
+ * SCSI disk driver header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+#ifndef _SD_H
+#define _SD_H
+/*
+ $Header: cvs/gnumach/i386/i386at/gpl/linux/scsi/Attic/sd.h,v 1.1.1.1 1997/02/25 21:27:52 thomas Exp $
+*/
+
+#ifndef _SCSI_H
+#include "scsi.h"
+#endif
+
+#ifndef _GENDISK_H
+#include <linux/genhd.h>
+#endif
+
+extern struct hd_struct * sd;
+
+typedef struct scsi_disk {
+ unsigned capacity; /* size in blocks */
+ unsigned sector_size; /* size in bytes */
+ Scsi_Device *device;
+ unsigned char ready; /* flag ready for FLOPTICAL */
+ unsigned char write_prot; /* flag write_protect for rmvable dev */
+ unsigned char sector_bit_size; /* sector_size = 2 to the bit size power */
+ unsigned char sector_bit_shift; /* power of 2 sectors per FS block */
+ unsigned ten:1; /* support ten byte read / write */
+ unsigned remap:1; /* support remapping */
+ unsigned has_part_table:1; /* has partition table */
+} Scsi_Disk;
+
+extern Scsi_Disk * rscsi_disks;
+
+extern int revalidate_scsidisk(kdev_t dev, int maxusage);
+
+#endif
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/sd_ioctl.c b/i386/i386at/gpl/linux/scsi/sd_ioctl.c
new file mode 100644
index 00000000..1898af61
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/sd_ioctl.c
@@ -0,0 +1,94 @@
+/*
+ * drivers/scsi/sd_ioctl.c
+ *
+ * ioctl handling for SCSI disks
+ */
+
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <linux/hdreg.h>
+#include <linux/errno.h>
+
+#include <asm/segment.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "scsi_ioctl.h"
+#include "hosts.h"
+#include "sd.h"
+
+int sd_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ kdev_t dev = inode->i_rdev;
+ int error;
+ struct Scsi_Host * host;
+ int diskinfo[4];
+ struct hd_geometry *loc = (struct hd_geometry *) arg;
+
+ switch (cmd) {
+ case HDIO_GETGEO: /* Return BIOS disk parameters */
+ if (!loc) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, loc, sizeof(*loc));
+ if (error)
+ return error;
+ host = rscsi_disks[MINOR(dev) >> 4].device->host;
+ diskinfo[0] = 0;
+ diskinfo[1] = 0;
+ diskinfo[2] = 0;
+ if(host->hostt->bios_param != NULL)
+ host->hostt->bios_param(&rscsi_disks[MINOR(dev) >> 4],
+ dev,
+ &diskinfo[0]);
+ put_user(diskinfo[0], &loc->heads);
+ put_user(diskinfo[1], &loc->sectors);
+ put_user(diskinfo[2], &loc->cylinders);
+ put_user(sd[MINOR(inode->i_rdev)].start_sect, &loc->start);
+ return 0;
+ case BLKGETSIZE: /* Return device size */
+ if (!arg) return -EINVAL;
+ error = verify_area(VERIFY_WRITE, (long *) arg, sizeof(long));
+ if (error)
+ return error;
+ put_user(sd[MINOR(inode->i_rdev)].nr_sects,
+ (long *) arg);
+ return 0;
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+ case BLKFLSBUF:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ fsync_dev(inode->i_rdev);
+ invalidate_buffers(inode->i_rdev);
+ return 0;
+
+ case BLKRRPART: /* Re-read partition tables */
+ return revalidate_scsidisk(dev, 1);
+ default:
+ return scsi_ioctl(rscsi_disks[MINOR(dev) >> 4].device , cmd, (void *) arg);
+ }
+}
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/seagate.c b/i386/i386at/gpl/linux/scsi/seagate.c
new file mode 100644
index 00000000..5b25a833
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/seagate.c
@@ -0,0 +1,1744 @@
+/*
+ * seagate.c Copyright (C) 1992, 1993 Drew Eckhardt
+ * low level scsi driver for ST01/ST02, Future Domain TMC-885,
+ * TMC-950 by
+ *
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Note : TMC-880 boards don't work because they have two bits in
+ * the status register flipped, I'll fix this "RSN"
+ *
+ * This card does all the I/O via memory mapped I/O, so there is no need
+ * to check or allocate a region of the I/O address space.
+ */
+
+/*
+ * Configuration :
+ * To use without BIOS -DOVERRIDE=base_address -DCONTROLLER=FD or SEAGATE
+ * -DIRQ will override the default of 5.
+ * Note: You can now set these options from the kernel's "command line".
+ * The syntax is:
+ *
+ * st0x=ADDRESS,IRQ (for a Seagate controller)
+ * or:
+ * tmc8xx=ADDRESS,IRQ (for a TMC-8xx or TMC-950 controller)
+ * eg:
+ * tmc8xx=0xC8000,15
+ *
+ * will configure the driver for a TMC-8xx style controller using IRQ 15
+ * with a base address of 0xC8000.
+ *
+ * -DFAST or -DFAST32 will use blind transfers where possible
+ *
+ * -DARBITRATE will cause the host adapter to arbitrate for the
+ * bus for better SCSI-II compatibility, rather than just
+ * waiting for BUS FREE and then doing its thing. Should
+ * let us do one command per Lun when I integrate my
+ * reorganization changes into the distribution sources.
+ *
+ * -DSLOW_HANDSHAKE will allow compatibility with broken devices that don't
+ * handshake fast enough (ie, some CD ROM's) for the Seagate
+ * code.
+ *
+ * -DSLOW_RATE=x, x some number will let you specify a default
+ * transfer rate if handshaking isn't working correctly.
+ */
+
+#include <linux/module.h>
+
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <linux/string.h>
+#include <linux/config.h>
+#include <linux/proc_fs.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "seagate.h"
+#include "constants.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_seagate = {
+ PROC_SCSI_SEAGATE, 7, "seagate",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+#ifndef IRQ
+#define IRQ 5
+#endif
+
+#if (defined(FAST32) && !defined(FAST))
+#define FAST
+#endif
+
+#if defined(SLOW_RATE) && !defined(SLOW_HANDSHAKE)
+#define SLOW_HANDSHAKE
+#endif
+
+#if defined(SLOW_HANDSHAKE) && !defined(SLOW_RATE)
+#define SLOW_RATE 50
+#endif
+
+
+#if defined(LINKED)
+#undef LINKED /* Linked commands are currently broken ! */
+#endif
+
+static int internal_command(unsigned char target, unsigned char lun,
+ const void *cmnd,
+ void *buff, int bufflen, int reselect);
+
+static int incommand; /*
+ set if arbitration has finished and we are
+ in some command phase.
+ */
+
+static const void *base_address = NULL; /*
+ Where the card ROM starts,
+ used to calculate memory mapped
+ register location.
+ */
+#ifdef notyet
+static volatile int abort_confirm = 0;
+#endif
+
+static volatile void *st0x_cr_sr; /*
+ control register write,
+ status register read.
+ 256 bytes in length.
+
+ Read is status of SCSI BUS,
+ as per STAT masks.
+
+ */
+
+
+static volatile void *st0x_dr; /*
+ data register, read write
+ 256 bytes in length.
+ */
+
+
+static volatile int st0x_aborted=0; /*
+ set when we are aborted, ie by a time out, etc.
+ */
+
+static unsigned char controller_type = 0; /* set to SEAGATE for ST0x boards or FD for TMC-8xx boards */
+static unsigned char irq = IRQ;
+
+#define retcode(result) (((result) << 16) | (message << 8) | status)
+#define STATUS (*(volatile unsigned char *) st0x_cr_sr)
+#define CONTROL STATUS
+#define DATA (*(volatile unsigned char *) st0x_dr)
+
+void st0x_setup (char *str, int *ints) {
+ controller_type = SEAGATE;
+ base_address = (void *) ints[1];
+ irq = ints[2];
+}
+
+void tmc8xx_setup (char *str, int *ints) {
+ controller_type = FD;
+ base_address = (void *) ints[1];
+ irq = ints[2];
+}
+
+
+#ifndef OVERRIDE
+static const char * seagate_bases[] = {
+ (char *) 0xc8000, (char *) 0xca000, (char *) 0xcc000,
+ (char *) 0xce000, (char *) 0xdc000, (char *) 0xde000
+};
+
+typedef struct {
+ const char *signature ;
+ unsigned offset;
+ unsigned length;
+ unsigned char type;
+} Signature;
+
+static const Signature signatures[] = {
+#ifdef CONFIG_SCSI_SEAGATE
+{"ST01 v1.7 (C) Copyright 1987 Seagate", 15, 37, SEAGATE},
+{"SCSI BIOS 2.00 (C) Copyright 1987 Seagate", 15, 40, SEAGATE},
+
+/*
+ * The following two lines are NOT mistakes. One detects ROM revision
+ * 3.0.0, the other 3.2. Since seagate has only one type of SCSI adapter,
+ * and this is not going to change, the "SEAGATE" and "SCSI" together
+ * are probably "good enough"
+ */
+
+{"SEAGATE SCSI BIOS ",16, 17, SEAGATE},
+{"SEAGATE SCSI BIOS ",17, 17, SEAGATE},
+
+/*
+ * However, future domain makes several incompatible SCSI boards, so specific
+ * signatures must be used.
+ */
+
+{"FUTURE DOMAIN CORP. (C) 1986-1989 V5.0C2/14/89", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1989 V6.0A7/28/89", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0105/31/90",5, 47, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V6.0209/18/90",5, 47, FD},
+{"FUTURE DOMAIN CORP. (C) 1986-1990 V7.009/18/90", 5, 46, FD},
+{"FUTURE DOMAIN CORP. (C) 1992 V8.00.004/02/92", 5, 44, FD},
+{"IBM F1 BIOS V1.1004/30/92", 5, 25, FD},
+{"FUTURE DOMAIN TMC-950", 5, 21, FD},
+#endif /* CONFIG_SCSI_SEAGATE */
+}
+;
+
+#define NUM_SIGNATURES (sizeof(signatures) / sizeof(Signature))
+#endif /* n OVERRIDE */
+
+/*
+ * hostno stores the hostnumber, as told to us by the init routine.
+ */
+
+static int hostno = -1;
+static void seagate_reconnect_intr(int, struct pt_regs *);
+
+#ifdef FAST
+static int fast = 1;
+#endif
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * Support for broken devices :
+ * The Seagate board has a handshaking problem. Namely, a lack
+ * thereof for slow devices. You can blast 600K/second through
+ * it if you are polling for each byte, more if you do a blind
+ * transfer. In the first case, with a fast device, REQ will
+ * transition high-low or high-low-high before your loop restarts
+ * and you'll have no problems. In the second case, the board
+ * will insert wait states for up to 13.2 usecs for REQ to
+ * transition low->high, and everything will work.
+ *
+ * However, there's nothing in the state machine that says
+ * you *HAVE* to see a high-low-high set of transitions before
+ * sending the next byte, and slow things like the Trantor CD ROMS
+ * will break because of this.
+ *
+ * So, we need to slow things down, which isn't as simple as it
+ * seems. We can't slow things down period, because then people
+ * who don't recompile their kernels will shoot me for ruining
+ * their performance. We need to do it on a case per case basis.
+ *
+ * The best for performance will be to, only for borken devices
+ * (this is stored on a per-target basis in the scsi_devices array)
+ *
+ * Wait for a low->high transition before continuing with that
+ * transfer. If we timeout, continue anyways. We don't need
+ * a long timeout, because REQ should only be asserted until the
+ * corresponding ACK is received and processed.
+ *
+ * Note that we can't use the system timer for this, because of
+ * resolution, and we *really* can't use the timer chip since
+ * gettimeofday() and the beeper routines use that. So,
+ * the best thing for us to do will be to calibrate a timing
+ * loop in the initialization code using the timer chip before
+ * gettimeofday() can screw with it.
+ */
+
+static int borken_calibration = 0;
+static void borken_init (void) {
+ register int count = 0, start = jiffies + 1, stop = start + 25;
+
+ while (jiffies < start);
+ for (;jiffies < stop; ++count);
+
+/*
+ * Ok, we now have a count for .25 seconds. Convert to a
+ * count per second and divide by transfer rate in K.
+ */
+
+ borken_calibration = (count * 4) / (SLOW_RATE*1024);
+
+ if (borken_calibration < 1)
+ borken_calibration = 1;
+#if (DEBUG & DEBUG_BORKEN)
+ printk("scsi%d : borken calibrated to %dK/sec, %d cycles per transfer\n",
+ hostno, BORKEN_RATE, borken_calibration);
+#endif
+}
+
+static inline void borken_wait(void) {
+ register int count;
+ for (count = borken_calibration; count && (STATUS & STAT_REQ);
+ --count);
+#if (DEBUG & DEBUG_BORKEN)
+ if (count)
+ printk("scsi%d : borken timeout\n", hostno);
+#endif
+}
+
+#endif /* def SLOW_HANDSHAKE */
+
+int seagate_st0x_detect (Scsi_Host_Template * tpnt)
+ {
+ struct Scsi_Host *instance;
+#ifndef OVERRIDE
+ int i,j;
+#endif
+
+ tpnt->proc_dir = &proc_scsi_seagate;
+/*
+ * First, we try for the manual override.
+ */
+#ifdef DEBUG
+ printk("Autodetecting ST0x / TMC-8xx\n");
+#endif
+
+ if (hostno != -1)
+ {
+ printk ("ERROR : seagate_st0x_detect() called twice.\n");
+ return 0;
+ }
+
+ /* If the user specified the controller type from the command line,
+ controller_type will be non-zero, so don't try and detect one */
+
+ if (!controller_type) {
+#ifdef OVERRIDE
+ base_address = (void *) OVERRIDE;
+
+/* CONTROLLER is used to override controller (SEAGATE or FD). PM: 07/01/93 */
+#ifdef CONTROLLER
+ controller_type = CONTROLLER;
+#else
+#error Please use -DCONTROLLER=SEAGATE or -DCONTROLLER=FD to override controller type
+#endif /* CONTROLLER */
+#ifdef DEBUG
+ printk("Base address overridden to %x, controller type is %s\n",
+ base_address,controller_type == SEAGATE ? "SEAGATE" : "FD");
+#endif
+#else /* OVERRIDE */
+/*
+ * To detect this card, we simply look for the signature
+ * from the BIOS version notice in all the possible locations
+ * of the ROM's. This has a nice side effect of not trashing
+ * any register locations that might be used by something else.
+ *
+ * XXX - note that we probably should be probing the address
+ * space for the on-board RAM instead.
+ */
+
+ for (i = 0; i < (sizeof (seagate_bases) / sizeof (char * )); ++i)
+ for (j = 0; !base_address && j < NUM_SIGNATURES; ++j)
+ if (!memcmp ((const void *) (seagate_bases[i] +
+ signatures[j].offset), (const void *) signatures[j].signature,
+ signatures[j].length)) {
+ base_address = (const void *) seagate_bases[i];
+ controller_type = signatures[j].type;
+ }
+#endif /* OVERRIDE */
+ } /* (! controller_type) */
+
+ tpnt->this_id = (controller_type == SEAGATE) ? 7 : 6;
+ tpnt->name = (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR;
+
+ if (base_address)
+ {
+ st0x_cr_sr =(void *) (((const unsigned char *) base_address) + (controller_type == SEAGATE ? 0x1a00 : 0x1c00));
+ st0x_dr = (void *) (((const unsigned char *) base_address ) + (controller_type == SEAGATE ? 0x1c00 : 0x1e00));
+#ifdef DEBUG
+ printk("%s detected. Base address = %x, cr = %x, dr = %x\n", tpnt->name, base_address, st0x_cr_sr, st0x_dr);
+#endif
+/*
+ * At all times, we will use IRQ 5. Should also check for IRQ3 if we
+ * loose our first interrupt.
+ */
+ instance = scsi_register(tpnt, 0);
+ hostno = instance->host_no;
+ if (request_irq((int) irq, seagate_reconnect_intr, SA_INTERRUPT,
+ (controller_type == SEAGATE) ? "seagate" : "tmc-8xx")) {
+ printk("scsi%d : unable to allocate IRQ%d\n",
+ hostno, (int) irq);
+ return 0;
+ }
+ instance->irq = irq;
+ instance->io_port = (unsigned int) base_address;
+#ifdef SLOW_HANDSHAKE
+ borken_init();
+#endif
+
+ printk("%s options:"
+#ifdef ARBITRATE
+ " ARBITRATE"
+#endif
+#ifdef SLOW_HANDSHAKE
+ " SLOW_HANDSHAKE"
+#endif
+#ifdef FAST
+#ifdef FAST32
+ " FAST32"
+#else
+ " FAST"
+#endif
+#endif
+#ifdef LINKED
+ " LINKED"
+#endif
+ "\n", tpnt->name);
+ return 1;
+ }
+ else
+ {
+#ifdef DEBUG
+ printk("ST0x / TMC-8xx not detected.\n");
+#endif
+ return 0;
+ }
+ }
+
+const char *seagate_st0x_info(struct Scsi_Host * shpnt) {
+ static char buffer[64];
+ sprintf(buffer, "%s at irq %d, address 0x%05X",
+ (controller_type == SEAGATE) ? ST0X_ID_STR : FD_ID_STR,
+ irq, (unsigned int)base_address);
+ return buffer;
+}
+
+int seagate_st0x_proc_info(char *buffer, char **start, off_t offset,
+ int length, int hostno, int inout)
+{
+ const char *info = seagate_st0x_info(NULL);
+ int len;
+ int pos;
+ int begin;
+
+ if (inout) return(-ENOSYS);
+
+ begin = 0;
+ strcpy(buffer,info);
+ strcat(buffer,"\n");
+
+ pos = len = strlen(buffer);
+
+ if (pos<offset) {
+ len = 0;
+ begin = pos;
+ }
+
+ *start = buffer + (offset - begin);
+ len -= (offset - begin);
+ if ( len > length ) len = length;
+ return(len);
+}
+
+/*
+ * These are our saved pointers for the outstanding command that is
+ * waiting for a reconnect
+ */
+
+static unsigned char current_target, current_lun;
+static unsigned char *current_cmnd, *current_data;
+static int current_nobuffs;
+static struct scatterlist *current_buffer;
+static int current_bufflen;
+
+#ifdef LINKED
+
+/*
+ * linked_connected indicates whether or not we are currently connected to
+ * linked_target, linked_lun and in an INFORMATION TRANSFER phase,
+ * using linked commands.
+ */
+
+static int linked_connected = 0;
+static unsigned char linked_target, linked_lun;
+#endif
+
+
+static void (*done_fn)(Scsi_Cmnd *) = NULL;
+static Scsi_Cmnd * SCint = NULL;
+
+/*
+ * These control whether or not disconnect / reconnect will be attempted,
+ * or are being attempted.
+ */
+
+#define NO_RECONNECT 0
+#define RECONNECT_NOW 1
+#define CAN_RECONNECT 2
+
+#ifdef LINKED
+
+/*
+ * LINKED_RIGHT indicates that we are currently connected to the correct target
+ * for this command, LINKED_WRONG indicates that we are connected to the wrong
+ * target. Note that these imply CAN_RECONNECT.
+ */
+
+#define LINKED_RIGHT 3
+#define LINKED_WRONG 4
+#endif
+
+/*
+ * This determines if we are expecting to reconnect or not.
+ */
+
+static int should_reconnect = 0;
+
+/*
+ * The seagate_reconnect_intr routine is called when a target reselects the
+ * host adapter. This occurs on the interrupt triggered by the target
+ * asserting SEL.
+ */
+
+static void seagate_reconnect_intr(int irq, struct pt_regs * regs)
+ {
+ int temp;
+ Scsi_Cmnd * SCtmp;
+
+/* enable all other interrupts. */
+ sti();
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : seagate_reconnect_intr() called\n", hostno);
+#endif
+
+ if (!should_reconnect)
+ printk("scsi%d: unexpected interrupt.\n", hostno);
+ else {
+ should_reconnect = 0;
+
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : internal_command("
+ "%d, %08x, %08x, %d, RECONNECT_NOW\n", hostno,
+ current_target, current_data, current_bufflen);
+#endif
+
+ temp = internal_command (current_target, current_lun,
+ current_cmnd, current_data, current_bufflen,
+ RECONNECT_NOW);
+
+ if (msg_byte(temp) != DISCONNECT) {
+ if (done_fn) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : done_fn(%d,%08x)", hostno,
+ hostno, temp);
+#endif
+ if(!SCint) panic("SCint == NULL in seagate");
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = temp;
+ done_fn (SCtmp);
+ } else
+ printk("done_fn() not defined.\n");
+ }
+ }
+ }
+
+/*
+ * The seagate_st0x_queue_command() function provides a queued interface
+ * to the seagate SCSI driver. Basically, it just passes control onto the
+ * seagate_command() function, after fixing it so that the done_fn()
+ * is set to the one passed to the function. We have to be very careful,
+ * because there are some commands on some devices that do not disconnect,
+ * and if we simply call the done_fn when the command is done then another
+ * command is started and queue_command is called again... We end up
+ * overflowing the kernel stack, and this tends not to be such a good idea.
+ */
+
+static int recursion_depth = 0;
+
+int seagate_st0x_queue_command (Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+ {
+ int result, reconnect;
+ Scsi_Cmnd * SCtmp;
+
+ done_fn = done;
+ current_target = SCpnt->target;
+ current_lun = SCpnt->lun;
+ (const void *) current_cmnd = SCpnt->cmnd;
+ current_data = (unsigned char *) SCpnt->request_buffer;
+ current_bufflen = SCpnt->request_bufflen;
+ SCint = SCpnt;
+ if(recursion_depth) {
+ return 0;
+ };
+ recursion_depth++;
+ do{
+#ifdef LINKED
+/*
+ * Set linked command bit in control field of SCSI command.
+ */
+
+ current_cmnd[SCpnt->cmd_len] |= 0x01;
+ if (linked_connected) {
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : using linked commands, current I_T_L nexus is ",
+ hostno);
+#endif
+ if ((linked_target == current_target) &&
+ (linked_lun == current_lun)) {
+#if (DEBUG & DEBUG_LINKED)
+ printk("correct\n");
+#endif
+ reconnect = LINKED_RIGHT;
+ } else {
+#if (DEBUG & DEBUG_LINKED)
+ printk("incorrect\n");
+#endif
+ reconnect = LINKED_WRONG;
+ }
+ } else
+#endif /* LINKED */
+ reconnect = CAN_RECONNECT;
+
+
+
+
+
+ result = internal_command (SCint->target, SCint->lun, SCint->cmnd, SCint->request_buffer,
+ SCint->request_bufflen,
+ reconnect);
+ if (msg_byte(result) == DISCONNECT) break;
+ SCtmp = SCint;
+ SCint = NULL;
+ SCtmp->result = result;
+ done_fn (SCtmp);
+ } while(SCint);
+ recursion_depth--;
+ return 0;
+ }
+
+int seagate_st0x_command (Scsi_Cmnd * SCpnt) {
+ return internal_command (SCpnt->target, SCpnt->lun, SCpnt->cmnd, SCpnt->request_buffer,
+ SCpnt->request_bufflen,
+ (int) NO_RECONNECT);
+}
+
+static int internal_command(unsigned char target, unsigned char lun, const void *cmnd,
+ void *buff, int bufflen, int reselect) {
+ int len = 0;
+ unsigned char *data = NULL;
+ struct scatterlist *buffer = NULL;
+ int nobuffs = 0;
+ int clock;
+ int temp;
+#ifdef SLOW_HANDSHAKE
+ int borken; /* Does the current target require Very Slow I/O ? */
+#endif
+
+
+#if (DEBUG & PHASE_DATAIN) || (DEBUG & PHASE_DATOUT)
+ int transfered = 0;
+#endif
+
+#if (((DEBUG & PHASE_ETC) == PHASE_ETC) || (DEBUG & PRINT_COMMAND) || \
+ (DEBUG & PHASE_EXIT))
+ int i;
+#endif
+
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ int phase=0, newphase;
+#endif
+
+ int done = 0;
+ unsigned char status = 0;
+ unsigned char message = 0;
+ register unsigned char status_read;
+
+ unsigned transfersize = 0, underflow = 0;
+
+ incommand = 0;
+ st0x_aborted = 0;
+
+#ifdef SLOW_HANDSHAKE
+ borken = (int) SCint->device->borken;
+#endif
+
+#if (DEBUG & PRINT_COMMAND)
+ printk ("scsi%d : target = %d, command = ", hostno, target);
+ print_command((unsigned char *) cmnd);
+ printk("\n");
+#endif
+
+#if (DEBUG & PHASE_RESELECT)
+ switch (reselect) {
+ case RECONNECT_NOW :
+ printk("scsi%d : reconnecting\n", hostno);
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT :
+ printk("scsi%d : connected, can reconnect\n", hostno);
+ break;
+ case LINKED_WRONG :
+ printk("scsi%d : connected to wrong target, can reconnect\n",
+ hostno);
+ break;
+#endif
+ case CAN_RECONNECT :
+ printk("scsi%d : allowed to reconnect\n", hostno);
+ break;
+ default :
+ printk("scsi%d : not allowed to reconnect\n", hostno);
+ }
+#endif
+
+
+ if (target == (controller_type == SEAGATE ? 7 : 6))
+ return DID_BAD_TARGET;
+
+/*
+ * We work it differently depending on if this is is "the first time,"
+ * or a reconnect. If this is a reselect phase, then SEL will
+ * be asserted, and we must skip selection / arbitration phases.
+ */
+
+ switch (reselect) {
+ case RECONNECT_NOW:
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : phase RESELECT \n", hostno);
+#endif
+
+/*
+ * At this point, we should find the logical or of our ID and the original
+ * target's ID on the BUS, with BSY, SEL, and I/O signals asserted.
+ *
+ * After ARBITRATION phase is completed, only SEL, BSY, and the
+ * target ID are asserted. A valid initiator ID is not on the bus
+ * until IO is asserted, so we must wait for that.
+ */
+ clock = jiffies + 10;
+ for (;;) {
+ temp = STATUS;
+ if ((temp & STAT_IO) && !(temp & STAT_BSY))
+ break;
+
+ if (jiffies > clock) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : RESELECT timed out while waiting for IO .\n",
+ hostno);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+ }
+
+/*
+ * After I/O is asserted by the target, we can read our ID and its
+ * ID off of the BUS.
+ */
+
+ if (!((temp = DATA) & (controller_type == SEAGATE ? 0x80 : 0x40)))
+ {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : detected reconnect request to different target.\n"
+ "\tData bus = %d\n", hostno, temp);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+
+ if (!(temp & (1 << current_target)))
+ {
+ printk("scsi%d : Unexpected reselect interrupt. Data bus = %d\n",
+ hostno, temp);
+ return (DID_BAD_INTR << 16);
+ }
+
+ buffer=current_buffer;
+ cmnd=current_cmnd; /* WDE add */
+ data=current_data; /* WDE add */
+ len=current_bufflen; /* WDE add */
+ nobuffs=current_nobuffs;
+
+/*
+ * We have determined that we have been selected. At this point,
+ * we must respond to the reselection by asserting BSY ourselves
+ */
+
+#if 1
+ CONTROL = (BASE_CMD | CMD_DRVR_ENABLE | CMD_BSY);
+#else
+ CONTROL = (BASE_CMD | CMD_BSY);
+#endif
+
+/*
+ * The target will drop SEL, and raise BSY, at which time we must drop
+ * BSY.
+ */
+
+ for (clock = jiffies + 10; (jiffies < clock) && (STATUS & STAT_SEL););
+
+ if (jiffies >= clock)
+ {
+ CONTROL = (BASE_CMD | CMD_INTR);
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : RESELECT timed out while waiting for SEL.\n",
+ hostno);
+#endif
+ return (DID_BAD_INTR << 16);
+ }
+
+ CONTROL = BASE_CMD;
+
+/*
+ * At this point, we have connected with the target and can get
+ * on with our lives.
+ */
+ break;
+ case CAN_RECONNECT:
+
+#ifdef LINKED
+/*
+ * This is a bletcherous hack, just as bad as the Unix #! interpreter stuff.
+ * If it turns out we are using the wrong I_T_L nexus, the easiest way to deal
+ * with it is to go into our INFORMATION TRANSFER PHASE code, send a ABORT
+ * message on MESSAGE OUT phase, and then loop back to here.
+ */
+
+connect_loop :
+
+#endif
+
+#if (DEBUG & PHASE_BUS_FREE)
+ printk ("scsi%d : phase = BUS FREE \n", hostno);
+#endif
+
+/*
+ * BUS FREE PHASE
+ *
+ * On entry, we make sure that the BUS is in a BUS FREE
+ * phase, by insuring that both BSY and SEL are low for
+ * at least one bus settle delay. Several reads help
+ * eliminate wire glitch.
+ */
+
+ clock = jiffies + ST0X_BUS_FREE_DELAY;
+
+#if !defined (ARBITRATE)
+ while (((STATUS | STATUS | STATUS) &
+ (STAT_BSY | STAT_SEL)) &&
+ (!st0x_aborted) && (jiffies < clock));
+
+ if (jiffies > clock)
+ return retcode(DID_BUS_BUSY);
+ else if (st0x_aborted)
+ return retcode(st0x_aborted);
+#endif
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : phase = SELECTION\n", hostno);
+#endif
+
+ clock = jiffies + ST0X_SELECTION_DELAY;
+
+/*
+ * Arbitration/selection procedure :
+ * 1. Disable drivers
+ * 2. Write HOST adapter address bit
+ * 3. Set start arbitration.
+ * 4. We get either ARBITRATION COMPLETE or SELECT at this
+ * point.
+ * 5. OR our ID and targets on bus.
+ * 6. Enable SCSI drivers and asserted SEL and ATTN
+ */
+
+#if defined(ARBITRATE)
+ cli();
+ CONTROL = 0;
+ DATA = (controller_type == SEAGATE) ? 0x80 : 0x40;
+ CONTROL = CMD_START_ARB;
+ sti();
+ while (!((status_read = STATUS) & (STAT_ARB_CMPL | STAT_SEL)) &&
+ (jiffies < clock) && !st0x_aborted);
+
+ if (!(status_read & STAT_ARB_CMPL)) {
+#if (DEBUG & PHASE_SELECTION)
+ if (status_read & STAT_SEL)
+ printk("scsi%d : arbitration lost\n", hostno);
+ else
+ printk("scsi%d : arbitration timeout.\n", hostno);
+#endif
+ CONTROL = BASE_CMD;
+ return retcode(DID_NO_CONNECT);
+ };
+
+#if (DEBUG & PHASE_SELECTION)
+ printk("scsi%d : arbitration complete\n", hostno);
+#endif
+#endif
+
+
+/*
+ * When the SCSI device decides that we're gawking at it, it will
+ * respond by asserting BUSY on the bus.
+ *
+ * Note : the Seagate ST-01/02 product manual says that we should
+ * twiddle the DATA register before the control register. However,
+ * this does not work reliably so we do it the other way around.
+ *
+ * Probably could be a problem with arbitration too, we really should
+ * try this with a SCSI protocol or logic analyzer to see what is
+ * going on.
+ */
+ cli();
+ DATA = (unsigned char) ((1 << target) | (controller_type == SEAGATE ? 0x80 : 0x40));
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE | CMD_SEL |
+ (reselect ? CMD_ATTN : 0);
+ sti();
+ while (!((status_read = STATUS) & STAT_BSY) &&
+ (jiffies < clock) && !st0x_aborted)
+
+#if 0 && (DEBUG & PHASE_SELECTION)
+ {
+ temp = clock - jiffies;
+
+ if (!(jiffies % 5))
+ printk("seagate_st0x_timeout : %d \r",temp);
+
+ }
+ printk("Done. \n");
+ printk("scsi%d : status = %02x, seagate_st0x_timeout = %d, aborted = %02x \n",
+ hostno, status_read, temp, st0x_aborted);
+#else
+ ;
+#endif
+
+
+ if ((jiffies >= clock) && !(status_read & STAT_BSY))
+ {
+#if (DEBUG & PHASE_SELECTION)
+ printk ("scsi%d : NO CONNECT with target %d, status = %x \n",
+ hostno, target, STATUS);
+#endif
+ return retcode(DID_NO_CONNECT);
+ }
+
+/*
+ * If we have been aborted, and we have a command in progress, IE the
+ * target still has BSY asserted, then we will reset the bus, and
+ * notify the midlevel driver to expect sense.
+ */
+
+ if (st0x_aborted) {
+ CONTROL = BASE_CMD;
+ if (STATUS & STAT_BSY) {
+ printk("scsi%d : BST asserted after we've been aborted.\n",
+ hostno);
+ seagate_st0x_reset(NULL);
+ return retcode(DID_RESET);
+ }
+ return retcode(st0x_aborted);
+ }
+
+/* Establish current pointers. Take into account scatter / gather */
+
+ if ((nobuffs = SCint->use_sg)) {
+#if (DEBUG & DEBUG_SG)
+ {
+ int i;
+ printk("scsi%d : scatter gather requested, using %d buffers.\n",
+ hostno, nobuffs);
+ for (i = 0; i < nobuffs; ++i)
+ printk("scsi%d : buffer %d address = %08x length = %d\n",
+ hostno, i, buffer[i].address, buffer[i].length);
+ }
+#endif
+
+ buffer = (struct scatterlist *) SCint->buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+ } else {
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : scatter gather not requested.\n", hostno);
+#endif
+ buffer = NULL;
+ len = SCint->request_bufflen;
+ data = (unsigned char *) SCint->request_buffer;
+ }
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT))
+ printk("scsi%d : len = %d\n", hostno, len);
+#endif
+
+ break;
+#ifdef LINKED
+ case LINKED_RIGHT:
+ break;
+ case LINKED_WRONG:
+ break;
+#endif
+ }
+
+/*
+ * There are several conditions under which we wish to send a message :
+ * 1. When we are allowing disconnect / reconnect, and need to establish
+ * the I_T_L nexus via an IDENTIFY with the DiscPriv bit set.
+ *
+ * 2. When we are doing linked commands, are have the wrong I_T_L nexus
+ * established and want to send an ABORT message.
+ */
+
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE |
+ (((reselect == CAN_RECONNECT)
+#ifdef LINKED
+ || (reselect == LINKED_WRONG)
+#endif
+ ) ? CMD_ATTN : 0) ;
+
+/*
+ * INFORMATION TRANSFER PHASE
+ *
+ * The nasty looking read / write inline assembler loops we use for
+ * DATAIN and DATAOUT phases are approximately 4-5 times as fast as
+ * the 'C' versions - since we're moving 1024 bytes of data, this
+ * really adds up.
+ */
+
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ printk("scsi%d : phase = INFORMATION TRANSFER\n", hostno);
+#endif
+
+ incommand = 1;
+ transfersize = SCint->transfersize;
+ underflow = SCint->underflow;
+
+
+/*
+ * Now, we poll the device for status information,
+ * and handle any requests it makes. Note that since we are unsure of
+ * how much data will be flowing across the system, etc and cannot
+ * make reasonable timeouts, that we will instead have the midlevel
+ * driver handle any timeouts that occur in this phase.
+ */
+
+ while (((status_read = STATUS) & STAT_BSY) && !st0x_aborted && !done)
+ {
+#ifdef PARITY
+ if (status_read & STAT_PARITY)
+ {
+ printk("scsi%d : got parity error\n", hostno);
+ st0x_aborted = DID_PARITY;
+ }
+#endif
+
+ if (status_read & STAT_REQ)
+ {
+#if ((DEBUG & PHASE_ETC) == PHASE_ETC)
+ if ((newphase = (status_read & REQ_MASK)) != phase)
+ {
+ phase = newphase;
+ switch (phase)
+ {
+ case REQ_DATAOUT:
+ printk("scsi%d : phase = DATA OUT\n",
+ hostno);
+ break;
+ case REQ_DATAIN :
+ printk("scsi%d : phase = DATA IN\n",
+ hostno);
+ break;
+ case REQ_CMDOUT :
+ printk("scsi%d : phase = COMMAND OUT\n",
+ hostno);
+ break;
+ case REQ_STATIN :
+ printk("scsi%d : phase = STATUS IN\n",
+ hostno);
+ break;
+ case REQ_MSGOUT :
+ printk("scsi%d : phase = MESSAGE OUT\n",
+ hostno);
+ break;
+ case REQ_MSGIN :
+ printk("scsi%d : phase = MESSAGE IN\n",
+ hostno);
+ break;
+ default :
+ printk("scsi%d : phase = UNKNOWN\n",
+ hostno);
+ st0x_aborted = DID_ERROR;
+ }
+ }
+#endif
+ switch (status_read & REQ_MASK)
+ {
+ case REQ_DATAOUT :
+/*
+ * If we are in fast mode, then we simply splat the data out
+ * in word-sized chunks as fast as we can.
+ */
+
+#ifdef FAST
+if (!len) {
+#if 0
+ printk("scsi%d: underflow to target %d lun %d \n",
+ hostno, target, lun);
+ st0x_aborted = DID_ERROR;
+ fast = 0;
+#endif
+ break;
+}
+
+if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n", hostno, SCint->underflow,
+ SCint->transfersize, len, data);
+#endif
+
+ __asm__("
+ cld;
+"
+#ifdef FAST32
+" shr $2, %%ecx;
+1: lodsl;
+ movl %%eax, (%%edi);
+"
+#else
+"1: lodsb;
+ movb %%al, (%%edi);
+"
+#endif
+" loop 1b;" : :
+ /* input */
+ "D" (st0x_dr), "S" (data), "c" (SCint->transfersize) :
+ /* clobbered */
+ "eax", "ecx", "esi" );
+
+ len -= transfersize;
+ data += transfersize;
+
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
+ hostno, len, data);
+#endif
+
+
+} else
+#endif
+
+{
+/*
+ * We loop as long as we are in a data out phase, there is data to send,
+ * and BSY is still active.
+ */
+ __asm__ (
+
+/*
+ Local variables :
+ len = ecx
+ data = esi
+ st0x_cr_sr = ebx
+ st0x_dr = edi
+
+ Test for any data here at all.
+*/
+ "\torl %%ecx, %%ecx
+ jz 2f
+
+ cld
+
+ movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%ebx
+ movl " SYMBOL_NAME_STR(st0x_dr) ", %%edi
+
+1: movb (%%ebx), %%al\n"
+/*
+ Test for BSY
+*/
+
+ "\ttest $1, %%al
+ jz 2f\n"
+
+/*
+ Test for data out phase - STATUS & REQ_MASK should be REQ_DATAOUT, which is 0.
+*/
+ "\ttest $0xe, %%al
+ jnz 2f \n"
+/*
+ Test for REQ
+*/
+ "\ttest $0x10, %%al
+ jz 1b
+ lodsb
+ movb %%al, (%%edi)
+ loop 1b
+
+2:
+ ":
+/* output */
+"=S" (data), "=c" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"eax", "ebx", "edi");
+}
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+#endif
+ }
+ break;
+
+ case REQ_DATAIN :
+#ifdef SLOW_HANDSHAKE
+ if (borken) {
+#if (DEBUG & (PHASE_DATAIN))
+ transfered += len;
+#endif
+ for (; len && (STATUS & (REQ_MASK | STAT_REQ)) == (REQ_DATAIN |
+ STAT_REQ); --len) {
+ *data++ = DATA;
+ borken_wait();
+}
+#if (DEBUG & (PHASE_DATAIN))
+ transfered -= len;
+#endif
+ } else
+#endif
+#ifdef FAST
+if (fast && transfersize && !(len % transfersize) && (len >= transfersize)
+#ifdef FAST32
+ && !(transfersize % 4)
+#endif
+ ) {
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer, underflow = %d, transfersize = %d\n"
+ " len = %d, data = %08x\n", hostno, SCint->underflow,
+ SCint->transfersize, len, data);
+#endif
+ __asm__("
+ cld;
+"
+#ifdef FAST32
+" shr $2, %%ecx;
+1: movl (%%esi), %%eax;
+ stosl;
+"
+#else
+"1: movb (%%esi), %%al;
+ stosb;
+"
+#endif
+
+" loop 1b;" : :
+ /* input */
+ "S" (st0x_dr), "D" (data), "c" (SCint->transfersize) :
+ /* clobbered */
+ "eax", "ecx", "edi");
+
+ len -= transfersize;
+ data += transfersize;
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, transfersize);
+ transfered += transfersize;
+#endif
+
+#if (DEBUG & DEBUG_FAST)
+ printk("scsi%d : FAST transfer complete len = %d data = %08x\n",
+ hostno, len, data);
+#endif
+
+} else
+#endif
+{
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered += %d\n", hostno, len);
+ transfered += len; /* Assume we'll transfer it all, then
+ subtract what we *didn't* transfer */
+#endif
+
+/*
+ * We loop as long as we are in a data in phase, there is room to read,
+ * and BSY is still active
+ */
+
+ __asm__ (
+/*
+ Local variables :
+ ecx = len
+ edi = data
+ esi = st0x_cr_sr
+ ebx = st0x_dr
+
+ Test for room to read
+*/
+ "\torl %%ecx, %%ecx
+ jz 2f
+
+ cld
+ movl " SYMBOL_NAME_STR(st0x_cr_sr) ", %%esi
+ movl " SYMBOL_NAME_STR(st0x_dr) ", %%ebx
+
+1: movb (%%esi), %%al\n"
+/*
+ Test for BSY
+*/
+
+ "\ttest $1, %%al
+ jz 2f\n"
+
+/*
+ Test for data in phase - STATUS & REQ_MASK should be REQ_DATAIN, = STAT_IO, which is 4.
+*/
+ "\tmovb $0xe, %%ah
+ andb %%al, %%ah
+ cmpb $0x04, %%ah
+ jne 2f\n"
+
+/*
+ Test for REQ
+*/
+ "\ttest $0x10, %%al
+ jz 1b
+
+ movb (%%ebx), %%al
+ stosb
+ loop 1b\n"
+
+"2:\n"
+ :
+/* output */
+"=D" (data), "=c" (len) :
+/* input */
+"0" (data), "1" (len) :
+/* clobbered */
+"eax","ebx", "esi");
+
+#if (DEBUG & PHASE_DATAIN)
+ printk("scsi%d: transfered -= %d\n", hostno, len);
+ transfered -= len; /* Since we assumed all of Len got
+ * transfered, correct our mistake */
+#endif
+}
+
+ if (!len && nobuffs) {
+ --nobuffs;
+ ++buffer;
+ len = buffer->length;
+ data = (unsigned char *) buffer->address;
+#if (DEBUG & DEBUG_SG)
+ printk("scsi%d : next scatter-gather buffer len = %d address = %08x\n",
+ hostno, len, data);
+#endif
+ }
+
+ break;
+
+ case REQ_CMDOUT :
+ while (((status_read = STATUS) & STAT_BSY) &&
+ ((status_read & REQ_MASK) == REQ_CMDOUT))
+ if (status_read & STAT_REQ) {
+ DATA = *(const unsigned char *) cmnd;
+ cmnd = 1+(const unsigned char *) cmnd;
+#ifdef SLOW_HANDSHAKE
+ if (borken)
+ borken_wait();
+#endif
+ }
+ break;
+
+ case REQ_STATIN :
+ status = DATA;
+ break;
+
+ case REQ_MSGOUT :
+/*
+ * We can only have sent a MSG OUT if we requested to do this
+ * by raising ATTN. So, we must drop ATTN.
+ */
+
+ CONTROL = BASE_CMD | CMD_DRVR_ENABLE;
+/*
+ * If we are reconnecting, then we must send an IDENTIFY message in
+ * response to MSGOUT.
+ */
+ switch (reselect) {
+ case CAN_RECONNECT:
+ DATA = IDENTIFY(1, lun);
+
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGOUT))
+ printk("scsi%d : sent IDENTIFY message.\n", hostno);
+#endif
+ break;
+#ifdef LINKED
+ case LINKED_WRONG:
+ DATA = ABORT;
+ linked_connected = 0;
+ reselect = CAN_RECONNECT;
+ goto connect_loop;
+#if (DEBUG & (PHASE_MSGOUT | DEBUG_LINKED))
+ printk("scsi%d : sent ABORT message to cancel incorrect I_T_L nexus.\n", hostno);
+#endif
+#endif /* LINKED */
+#if (DEBUG & DEBUG_LINKED)
+ printk("correct\n");
+#endif
+ default:
+ DATA = NOP;
+ printk("scsi%d : target %d requested MSGOUT, sent NOP message.\n", hostno, target);
+ }
+ break;
+
+ case REQ_MSGIN :
+ switch (message = DATA) {
+ case DISCONNECT :
+ should_reconnect = 1;
+ current_data = data; /* WDE add */
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_nobuffs = nobuffs;
+#ifdef LINKED
+ linked_connected = 0;
+#endif
+ done=1;
+#if (DEBUG & (PHASE_RESELECT | PHASE_MSGIN))
+ printk("scsi%d : disconnected.\n", hostno);
+#endif
+ break;
+
+#ifdef LINKED
+ case LINKED_CMD_COMPLETE:
+ case LINKED_FLG_CMD_COMPLETE:
+#endif
+ case COMMAND_COMPLETE :
+/*
+ * Note : we should check for underflow here.
+ */
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : command complete.\n", hostno);
+#endif
+ done = 1;
+ break;
+ case ABORT :
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : abort message.\n", hostno);
+#endif
+ done=1;
+ break;
+ case SAVE_POINTERS :
+ current_buffer = buffer;
+ current_bufflen = len; /* WDE add */
+ current_data = data; /* WDE mod */
+ current_nobuffs = nobuffs;
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : pointers saved.\n", hostno);
+#endif
+ break;
+ case RESTORE_POINTERS:
+ buffer=current_buffer;
+ cmnd=current_cmnd;
+ data=current_data; /* WDE mod */
+ len=current_bufflen;
+ nobuffs=current_nobuffs;
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : pointers restored.\n", hostno);
+#endif
+ break;
+ default:
+
+/*
+ * IDENTIFY distinguishes itself from the other messages by setting the
+ * high byte.
+ *
+ * Note : we need to handle at least one outstanding command per LUN,
+ * and need to hash the SCSI command for that I_T_L nexus based on the
+ * known ID (at this point) and LUN.
+ */
+
+ if (message & 0x80) {
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : IDENTIFY message received from id %d, lun %d.\n",
+ hostno, target, message & 7);
+#endif
+ } else {
+
+/*
+ * We should go into a MESSAGE OUT phase, and send a MESSAGE_REJECT
+ * if we run into a message that we don't like. The seagate driver
+ * needs some serious restructuring first though.
+ */
+
+#if (DEBUG & PHASE_MSGIN)
+ printk("scsi%d : unknown message %d from target %d.\n",
+ hostno, message, target);
+#endif
+ }
+ }
+ break;
+
+ default :
+ printk("scsi%d : unknown phase.\n", hostno);
+ st0x_aborted = DID_ERROR;
+ }
+
+#ifdef SLOW_HANDSHAKE
+/*
+ * I really don't care to deal with borken devices in each single
+ * byte transfer case (ie, message in, message out, status), so
+ * I'll do the wait here if necessary.
+ */
+ if (borken)
+ borken_wait();
+#endif
+
+ } /* if ends */
+ } /* while ends */
+
+#if (DEBUG & (PHASE_DATAIN | PHASE_DATAOUT | PHASE_EXIT))
+ printk("scsi%d : Transfered %d bytes\n", hostno, transfered);
+#endif
+
+#if (DEBUG & PHASE_EXIT)
+#if 0 /* Doesn't work for scatter / gather */
+ printk("Buffer : \n");
+ for (i = 0; i < 20; ++i)
+ printk ("%02x ", ((unsigned char *) data)[i]); /* WDE mod */
+ printk("\n");
+#endif
+ printk("scsi%d : status = ", hostno);
+ print_status(status);
+ printk("message = %02x\n", message);
+#endif
+
+
+/* We shouldn't reach this until *after* BSY has been deasserted */
+#ifdef notyet
+ if (st0x_aborted) {
+ if (STATUS & STAT_BSY) {
+ seagate_st0x_reset(NULL);
+ st0x_aborted = DID_RESET;
+ }
+ abort_confirm = 1;
+ }
+#endif
+
+#ifdef LINKED
+else {
+/*
+ * Fix the message byte so that unsuspecting high level drivers don't
+ * puke when they see a LINKED COMMAND message in place of the COMMAND
+ * COMPLETE they may be expecting. Shouldn't be necessary, but it's
+ * better to be on the safe side.
+ *
+ * A non LINKED* message byte will indicate that the command completed,
+ * and we are now disconnected.
+ */
+
+ switch (message) {
+ case LINKED_CMD_COMPLETE :
+ case LINKED_FLG_CMD_COMPLETE :
+ message = COMMAND_COMPLETE;
+ linked_target = current_target;
+ linked_lun = current_lun;
+ linked_connected = 1;
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : keeping I_T_L nexus established for linked command.\n",
+ hostno);
+#endif
+/*
+ * We also will need to adjust status to accommodate intermediate conditions.
+ */
+ if ((status == INTERMEDIATE_GOOD) ||
+ (status == INTERMEDIATE_C_GOOD))
+ status = GOOD;
+
+ break;
+/*
+ * We should also handle what are "normal" termination messages
+ * here (ABORT, BUS_DEVICE_RESET?, and COMMAND_COMPLETE individually,
+ * and flake if things aren't right.
+ */
+
+ default :
+#if (DEBUG & DEBUG_LINKED)
+ printk("scsi%d : closing I_T_L nexus.\n", hostno);
+#endif
+ linked_connected = 0;
+ }
+ }
+#endif /* LINKED */
+
+
+
+
+ if (should_reconnect) {
+#if (DEBUG & PHASE_RESELECT)
+ printk("scsi%d : exiting seagate_st0x_queue_command() with reconnect enabled.\n",
+ hostno);
+#endif
+ CONTROL = BASE_CMD | CMD_INTR ;
+ } else
+ CONTROL = BASE_CMD;
+
+ return retcode (st0x_aborted);
+ }
+
+int seagate_st0x_abort (Scsi_Cmnd * SCpnt)
+ {
+ st0x_aborted = DID_ABORT;
+
+ return SCSI_ABORT_PENDING;
+ }
+
+/*
+ the seagate_st0x_reset function resets the SCSI bus
+*/
+
+int seagate_st0x_reset (Scsi_Cmnd * SCpnt)
+ {
+ unsigned clock;
+ /*
+ No timeouts - this command is going to fail because
+ it was reset.
+ */
+
+#ifdef DEBUG
+ printk("In seagate_st0x_reset()\n");
+#endif
+
+
+ /* assert RESET signal on SCSI bus. */
+
+ CONTROL = BASE_CMD | CMD_RST;
+ clock=jiffies+2;
+
+
+ /* Wait. */
+
+ while (jiffies < clock);
+
+ CONTROL = BASE_CMD;
+
+ st0x_aborted = DID_RESET;
+
+#ifdef DEBUG
+ printk("SCSI bus reset.\n");
+#endif
+ return SCSI_RESET_WAKEUP;
+ }
+
+#include <asm/segment.h>
+#include "sd.h"
+#include "scsi_ioctl.h"
+
+int seagate_st0x_biosparam(Disk * disk, kdev_t dev, int* ip) {
+ unsigned char buf[256 + sizeof(int) * 2], cmd[6], *data, *page;
+ int *sizes, result, formatted_sectors, total_sectors;
+ int cylinders, heads, sectors;
+
+/*
+ * Only SCSI-I CCS drives and later implement the necessary mode sense
+ * pages.
+ */
+
+ if (disk->device->scsi_level < 2)
+ return -1;
+
+ sizes = (int *) buf;
+ data = (unsigned char *) (sizes + 2);
+
+ cmd[0] = MODE_SENSE;
+ cmd[1] = (disk->device->lun << 5) & 0xe5;
+ cmd[2] = 0x04; /* Read page 4, rigid disk geometry page current values */
+ cmd[3] = 0;
+ cmd[4] = 255;
+ cmd[5] = 0;
+
+/*
+ * We are transferring 0 bytes in the out direction, and expect to get back
+ * 24 bytes for each mode page.
+ */
+
+ sizes[0] = 0;
+ sizes[1] = 256;
+
+ memcpy (data, cmd, 6);
+
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+/*
+ * The mode page lies beyond the MODE SENSE header, with length 4, and
+ * the BLOCK DESCRIPTOR, with length header[3].
+ */
+
+ page = data + 4 + data[3];
+ heads = (int) page[5];
+ cylinders = (page[2] << 16) | (page[3] << 8) | page[4];
+
+ cmd[2] = 0x03; /* Read page 3, format page current values */
+ memcpy (data, cmd, 6);
+
+ if (!(result = kernel_scsi_ioctl (disk->device, SCSI_IOCTL_SEND_COMMAND, (void *) buf))) {
+ page = data + 4 + data[3];
+ sectors = (page[10] << 8) | page[11];
+
+
+/*
+ * Get the total number of formatted sectors from the block descriptor,
+ * so we can tell how many are being used for alternates.
+ */
+
+ formatted_sectors = (data[4 + 1] << 16) | (data[4 + 2] << 8) |
+ data[4 + 3] ;
+
+ total_sectors = (heads * cylinders * sectors);
+
+/*
+ * Adjust the real geometry by subtracting
+ * (spare sectors / (heads * tracks)) cylinders from the number of cylinders.
+ *
+ * It appears that the CE cylinder CAN be a partial cylinder.
+ */
+
+
+printk("scsi%d : heads = %d cylinders = %d sectors = %d total = %d formatted = %d\n",
+ hostno, heads, cylinders, sectors, total_sectors, formatted_sectors);
+
+ if (!heads || !sectors || !cylinders)
+ result = -1;
+ else
+ cylinders -= ((total_sectors - formatted_sectors) / (heads * sectors));
+
+/*
+ * Now, we need to do a sanity check on the geometry to see if it is
+ * BIOS compatible. The maximum BIOS geometry is 1024 cylinders *
+ * 256 heads * 64 sectors.
+ */
+
+ if ((cylinders > 1024) || (sectors > 64))
+ result = -1;
+ else {
+ ip[0] = heads;
+ ip[1] = sectors;
+ ip[2] = cylinders;
+ }
+
+/*
+ * There should be an alternate mapping for things the seagate doesn't
+ * understand, but I couldn't say what it is with reasonable certainty.
+ */
+
+ }
+ }
+
+ return result;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = SEAGATE_ST0X;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/seagate.h b/i386/i386at/gpl/linux/scsi/seagate.h
new file mode 100644
index 00000000..8d9e1a42
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/seagate.h
@@ -0,0 +1,139 @@
+/*
+ * seagate.h Copyright (C) 1992 Drew Eckhardt
+ * low level scsi driver header for ST01/ST02 by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ */
+
+#ifndef _SEAGATE_H
+ #define SEAGATE_H
+/*
+ $Header
+*/
+#ifndef ASM
+int seagate_st0x_detect(Scsi_Host_Template *);
+int seagate_st0x_command(Scsi_Cmnd *);
+int seagate_st0x_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+
+int seagate_st0x_abort(Scsi_Cmnd *);
+const char *seagate_st0x_info(struct Scsi_Host *);
+int seagate_st0x_reset(Scsi_Cmnd *);
+int seagate_st0x_proc_info(char *,char **,off_t,int,int,int);
+
+#ifndef NULL
+ #define NULL 0
+#endif
+
+#include <linux/kdev_t.h>
+int seagate_st0x_biosparam(Disk *, kdev_t, int*);
+
+#define SEAGATE_ST0X { NULL, NULL, NULL, seagate_st0x_proc_info, \
+ NULL, seagate_st0x_detect, \
+ NULL, \
+ seagate_st0x_info, seagate_st0x_command, \
+ seagate_st0x_queue_command, seagate_st0x_abort, \
+ seagate_st0x_reset, NULL, seagate_st0x_biosparam, \
+ 1, 7, SG_ALL, 1, 0, 0, DISABLE_CLUSTERING}
+#endif
+
+
+/*
+ defining PARITY causes parity data to be checked
+*/
+
+#define PARITY
+
+
+/*
+ Thanks to Brian Antoine for the example code in his Messy-Loss ST-01
+ driver, and Mitsugu Suzuki for information on the ST-01
+ SCSI host.
+*/
+
+/*
+ CONTROL defines
+*/
+
+#define CMD_RST 0x01
+#define CMD_SEL 0x02
+#define CMD_BSY 0x04
+#define CMD_ATTN 0x08
+#define CMD_START_ARB 0x10
+#define CMD_EN_PARITY 0x20
+#define CMD_INTR 0x40
+#define CMD_DRVR_ENABLE 0x80
+
+/*
+ STATUS
+*/
+
+#define STAT_BSY 0x01
+#define STAT_MSG 0x02
+#define STAT_IO 0x04
+#define STAT_CD 0x08
+#define STAT_REQ 0x10
+#define STAT_SEL 0x20
+#define STAT_PARITY 0x40
+#define STAT_ARB_CMPL 0x80
+
+/*
+ REQUESTS
+*/
+
+#define REQ_MASK (STAT_CD | STAT_IO | STAT_MSG)
+#define REQ_DATAOUT 0
+#define REQ_DATAIN STAT_IO
+#define REQ_CMDOUT STAT_CD
+#define REQ_STATIN (STAT_CD | STAT_IO)
+#define REQ_MSGOUT (STAT_MSG | STAT_CD)
+#define REQ_MSGIN (STAT_MSG | STAT_CD | STAT_IO)
+
+extern volatile int seagate_st0x_timeout;
+
+#ifdef PARITY
+ #define BASE_CMD CMD_EN_PARITY
+#else
+ #define BASE_CMD 0
+#endif
+
+/*
+ Debugging code
+*/
+
+#define PHASE_BUS_FREE 1
+#define PHASE_ARBITRATION 2
+#define PHASE_SELECTION 4
+#define PHASE_DATAIN 8
+#define PHASE_DATAOUT 0x10
+#define PHASE_CMDOUT 0x20
+#define PHASE_MSGIN 0x40
+#define PHASE_MSGOUT 0x80
+#define PHASE_STATUSIN 0x100
+#define PHASE_ETC (PHASE_DATAIN | PHASE_DATA_OUT | PHASE_CMDOUT | PHASE_MSGIN | PHASE_MSGOUT | PHASE_STATUSIN)
+#define PRINT_COMMAND 0x200
+#define PHASE_EXIT 0x400
+#define PHASE_RESELECT 0x800
+#define DEBUG_FAST 0x1000
+#define DEBUG_SG 0x2000
+#define DEBUG_LINKED 0x4000
+#define DEBUG_BORKEN 0x8000
+
+/*
+ * Control options - these are timeouts specified in .01 seconds.
+ */
+
+/* 30, 20 work */
+#define ST0X_BUS_FREE_DELAY 25
+#define ST0X_SELECTION_DELAY 25
+
+#define eoi() __asm__("push %%eax\nmovb $0x20, %%al\noutb %%al, $0x20\npop %%eax"::)
+
+#define SEAGATE 1 /* these determine the type of the controller */
+#define FD 2
+
+#define ST0X_ID_STR "Seagate ST-01/ST-02"
+#define FD_ID_STR "TMC-8XX/TMC-950"
+
+#endif
+
diff --git a/i386/i386at/gpl/linux/scsi/sr.c b/i386/i386at/gpl/linux/scsi/sr.c
new file mode 100644
index 00000000..dc15cf58
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/sr.c
@@ -0,0 +1,1191 @@
+/*
+ * sr.c Copyright (C) 1992 David Giller
+ * Copyright (C) 1993, 1994, 1995 Eric Youngdale
+ *
+ * adapted from:
+ * sd.c Copyright (C) 1992 Drew Eckhardt
+ * Linux scsi disk driver by
+ * Drew Eckhardt <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale ericy@cais.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ *
+ * Modified by Eric Youngdale eric@aib.com to support loadable
+ * low-level scsi drivers.
+ *
+ * Modified by Thomas Quinot thomas@melchior.cuivre.fdn.fr to
+ * provide auto-eject.
+ *
+ */
+
+#include <linux/module.h>
+
+#include <linux/fs.h>
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/string.h>
+#include <linux/errno.h>
+#include <linux/cdrom.h>
+#include <asm/system.h>
+
+#define MAJOR_NR SCSI_CDROM_MAJOR
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include "scsi_ioctl.h" /* For the door lock/unlock commands */
+#include "constants.h"
+
+#define MAX_RETRIES 3
+#define SR_TIMEOUT (150 * HZ)
+
+static int sr_init(void);
+static void sr_finish(void);
+static int sr_attach(Scsi_Device *);
+static int sr_detect(Scsi_Device *);
+static void sr_detach(Scsi_Device *);
+
+struct Scsi_Device_Template sr_template = {NULL, "cdrom", "sr", NULL, TYPE_ROM,
+ SCSI_CDROM_MAJOR, 0, 0, 0, 1,
+ sr_detect, sr_init,
+ sr_finish, sr_attach, sr_detach};
+
+Scsi_CD * scsi_CDs = NULL;
+static int * sr_sizes;
+
+static int * sr_blocksizes;
+
+static int sr_open(struct inode *, struct file *);
+static void get_sectorsize(int);
+
+extern int sr_ioctl(struct inode *, struct file *, unsigned int, unsigned long);
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt);
+static int check_cdrom_media_change(kdev_t);
+
+static void sr_release(struct inode * inode, struct file * file)
+{
+ sync_dev(inode->i_rdev);
+ if(! --scsi_CDs[MINOR(inode->i_rdev)].device->access_count)
+ {
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].auto_eject)
+ sr_ioctl(inode, NULL, CDROMEJECT, 0);
+ }
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)--;
+ if(sr_template.usage_count) (*sr_template.usage_count)--;
+}
+
+static struct file_operations sr_fops =
+{
+ NULL, /* lseek - default */
+ block_read, /* read - general block-dev read */
+ block_write, /* write - general block-dev write */
+ NULL, /* readdir - bad */
+ NULL, /* select */
+ sr_ioctl, /* ioctl */
+ NULL, /* mmap */
+ sr_open, /* special open code */
+ sr_release, /* release */
+ NULL, /* fsync */
+ NULL, /* fasync */
+ check_cdrom_media_change, /* Disk change */
+ NULL /* revalidate */
+};
+
+/*
+ * This function checks to see if the media has been changed in the
+ * CDROM drive. It is possible that we have already sensed a change,
+ * or the drive may have sensed one and not yet reported it. We must
+ * be ready for either case. This function always reports the current
+ * value of the changed bit. If flag is 0, then the changed bit is reset.
+ * This function could be done as an ioctl, but we would need to have
+ * an inode for that to work, and we do not always have one.
+ */
+
+int check_cdrom_media_change(kdev_t full_dev){
+ int retval, target;
+ struct inode inode;
+ int flag = 0;
+
+ target = MINOR(full_dev);
+
+ if (target >= sr_template.nr_dev) {
+ printk("CD-ROM request error: invalid device.\n");
+ return 0;
+ };
+
+ inode.i_rdev = full_dev; /* This is all we really need here */
+ retval = sr_ioctl(&inode, NULL, SCSI_IOCTL_TEST_UNIT_READY, 0);
+
+ if(retval){ /* Unable to test, unit probably not ready. This usually
+ * means there is no disc in the drive. Mark as changed,
+ * and we will figure it out later once the drive is
+ * available again. */
+
+ scsi_CDs[target].device->changed = 1;
+ return 1; /* This will force a flush, if called from
+ * check_disk_change */
+ };
+
+ retval = scsi_CDs[target].device->changed;
+ if(!flag) {
+ scsi_CDs[target].device->changed = 0;
+ /* If the disk changed, the capacity will now be different,
+ * so we force a re-read of this information */
+ if (retval) scsi_CDs[target].needs_sector_size = 1;
+ };
+ return retval;
+}
+
+/*
+ * rw_intr is the interrupt routine for the device driver. It will be notified on the
+ * end of a SCSI read / write, and will take on of several actions based on success or failure.
+ */
+
+static void rw_intr (Scsi_Cmnd * SCpnt)
+{
+ int result = SCpnt->result;
+ int this_count = SCpnt->this_count;
+
+#ifdef DEBUG
+ printk("sr.c done: %x %x\n",result, SCpnt->request.bh->b_data);
+#endif
+ if (!result)
+ { /* No error */
+ if (SCpnt->use_sg == 0) {
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ {
+ int offset;
+ offset = (SCpnt->request.sector % 4) << 9;
+ memcpy((char *)SCpnt->request.buffer,
+ (char *)SCpnt->buffer + offset,
+ this_count << 9);
+ /* Even though we are not using scatter-gather, we look
+ * ahead and see if there is a linked request for the
+ * other half of this buffer. If there is, then satisfy
+ * it. */
+ if((offset == 0) && this_count == 2 &&
+ SCpnt->request.nr_sectors > this_count &&
+ SCpnt->request.bh &&
+ SCpnt->request.bh->b_reqnext &&
+ SCpnt->request.bh->b_reqnext->b_size == 1024) {
+ memcpy((char *)SCpnt->request.bh->b_reqnext->b_data,
+ (char *)SCpnt->buffer + 1024,
+ 1024);
+ this_count += 2;
+ };
+
+ scsi_free(SCpnt->buffer, 2048);
+ }
+ } else {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if (sgpnt[i].alt_address) {
+ if (sgpnt[i].alt_address != sgpnt[i].address) {
+ memcpy(sgpnt[i].alt_address, sgpnt[i].address, sgpnt[i].length);
+ };
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ };
+ };
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ if(SCpnt->request.sector % 4) this_count -= 2;
+ /* See if there is a padding record at the end that needs to be removed */
+ if(this_count > SCpnt->request.nr_sectors)
+ this_count -= 2;
+ };
+
+#ifdef DEBUG
+ printk("(%x %x %x) ",SCpnt->request.bh, SCpnt->request.nr_sectors,
+ this_count);
+#endif
+ if (SCpnt->request.nr_sectors > this_count)
+ {
+ SCpnt->request.errors = 0;
+ if (!SCpnt->request.bh)
+ panic("sr.c: linked page request (%lx %x)",
+ SCpnt->request.sector, this_count);
+ }
+
+ SCpnt = end_scsi_request(SCpnt, 1, this_count); /* All done */
+ requeue_sr_request(SCpnt);
+ return;
+ } /* Normal completion */
+
+ /* We only come through here if we have an error of some kind */
+
+ /* Free up any indirection buffers we allocated for DMA purposes. */
+ if (SCpnt->use_sg) {
+ struct scatterlist * sgpnt;
+ int i;
+ sgpnt = (struct scatterlist *) SCpnt->buffer;
+ for(i=0; i<SCpnt->use_sg; i++) {
+ if (sgpnt[i].alt_address) {
+ scsi_free(sgpnt[i].address, sgpnt[i].length);
+ };
+ };
+ scsi_free(SCpnt->buffer, SCpnt->sglist_len); /* Free list of scatter-gather pointers */
+ } else {
+ if (SCpnt->buffer != SCpnt->request.buffer)
+ scsi_free(SCpnt->buffer, SCpnt->bufflen);
+ };
+
+ if (driver_byte(result) != 0) {
+ if ((SCpnt->sense_buffer[0] & 0x7f) == 0x70) {
+ if ((SCpnt->sense_buffer[2] & 0xf) == UNIT_ATTENTION) {
+ /* detected disc change. set a bit and quietly refuse
+ * further access. */
+
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->changed = 1;
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt);
+ return;
+ }
+ }
+
+ if (SCpnt->sense_buffer[2] == ILLEGAL_REQUEST) {
+ printk("CD-ROM error: ");
+ print_sense("sr", SCpnt);
+ printk("command was: ");
+ print_command(SCpnt->cmnd);
+ if (scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten) {
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].ten = 0;
+ requeue_sr_request(SCpnt);
+ result = 0;
+ return;
+ } else {
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt); /* Do next request */
+ return;
+ }
+
+ }
+
+ if (SCpnt->sense_buffer[2] == NOT_READY) {
+ printk("CDROM not ready. Make sure you have a disc in the drive.\n");
+ SCpnt = end_scsi_request(SCpnt, 0, this_count);
+ requeue_sr_request(SCpnt); /* Do next request */
+ return;
+ };
+ }
+
+ /* We only get this far if we have an error we have not recognized */
+ if(result) {
+ printk("SCSI CD error : host %d id %d lun %d return code = %03x\n",
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->host->host_no,
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->id,
+ scsi_CDs[DEVICE_NR(SCpnt->request.rq_dev)].device->lun,
+ result);
+
+ if (status_byte(result) == CHECK_CONDITION)
+ print_sense("sr", SCpnt);
+
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.current_nr_sectors);
+ requeue_sr_request(SCpnt);
+ }
+}
+
+/*
+ * Here I tried to implement better support for PhotoCD's.
+ *
+ * Much of this has do be done with vendor-specific SCSI-commands.
+ * So I have to complete it step by step. Useful information is welcome.
+ *
+ * Actually works:
+ * - NEC: Detection and support of multisession CD's. Special handling
+ * for XA-disks is not necessary.
+ *
+ * - TOSHIBA: setting density is done here now, mounting PhotoCD's should
+ * work now without running the program "set_density"
+ * Multisession CD's are supported too.
+ *
+ * kraxel@cs.tu-berlin.de (Gerd Knorr)
+ */
+/*
+ * 19950704 operator@melchior.cuivre.fdn.fr (Thomas Quinot)
+ *
+ * - SONY: Same as Nec.
+ *
+ * - PIONEER: works with SONY code
+ */
+
+static void sr_photocd(struct inode *inode)
+{
+ unsigned long sector,min,sec,frame;
+ unsigned char buf[40]; /* the buffer for the ioctl */
+ unsigned char *cmd; /* the scsi-command */
+ unsigned char *send; /* the data we send to the drive ... */
+ unsigned char *rec; /* ... and get back */
+ int rc,is_xa,no_multi;
+
+ if (scsi_CDs[MINOR(inode->i_rdev)].xa_flags & 0x02) {
+#ifdef DEBUG
+ printk("sr_photocd: CDROM and/or the driver does not support multisession CD's");
+#endif
+ return;
+ }
+
+ if (!suser()) {
+ /* I'm not the superuser, so SCSI_IOCTL_SEND_COMMAND isn't allowed for me.
+ * That's why mpcd_sector will be initialized with zero, because I'm not
+ * able to get the right value. Necessary only if access_count is 1, else
+ * no disk change happened since the last call of this function and we can
+ * keep the old value.
+ */
+ if (1 == scsi_CDs[MINOR(inode->i_rdev)].device->access_count) {
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = 0;
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ }
+ return;
+ }
+
+ sector = 0;
+ is_xa = 0;
+ no_multi = 0;
+ cmd = rec = &buf[8];
+
+ switch(scsi_CDs[MINOR(inode->i_rdev)].device->manufacturer) {
+
+ case SCSI_MAN_NEC:
+#ifdef DEBUG
+ printk("sr_photocd: use NEC code\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0x0; /* we send nothing... */
+ *((unsigned long*)buf+1) = 0x16; /* and receive 0x16 bytes */
+ cmd[0] = 0xde;
+ cmd[1] = 0x03;
+ cmd[2] = 0xb0;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (NEC): 0x%x\n",rc);
+ break;
+ }
+ if (rec[14] != 0 && rec[14] != 0xb0) {
+ printk("sr_photocd: (NEC) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ min = (unsigned long) rec[15]/16*10 + (unsigned long) rec[15]%16;
+ sec = (unsigned long) rec[16]/16*10 + (unsigned long) rec[16]%16;
+ frame = (unsigned long) rec[17]/16*10 + (unsigned long) rec[17]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ is_xa = (rec[14] == 0xb0);
+#ifdef DEBUG
+ if (sector) {
+ printk("sr_photocd: multisession CD detected. start: %lu\n",sector);
+ }
+#endif
+ break;
+
+ case SCSI_MAN_TOSHIBA:
+#ifdef DEBUG
+ printk("sr_photocd: use TOSHIBA code\n");
+#endif
+
+ /* we request some disc information (is it a XA-CD ?,
+ * where starts the last session ?) */
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0;
+ *((unsigned long*)buf+1) = 4; /* we receive 4 bytes from the drive */
+ cmd[0] = 0xc7;
+ cmd[1] = 3;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ if (rc == 0x28000002) {
+ /* Got a "not ready" - error. No chance to find out if this is
+ * because there is no CD in the drive or because the drive
+ * don't knows multisession CD's. So I need to do an extra check... */
+ if (kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_TEST_UNIT_READY, NULL)) {
+ printk("sr_photocd: drive not ready\n");
+ } else {
+ printk("sr_photocd: (TOSHIBA) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ }
+ } else
+ printk("sr_photocd: ioctl error (TOSHIBA #1): 0x%x\n",rc);
+ break; /* if the first ioctl fails, we don't call the second one */
+ }
+ is_xa = (rec[0] == 0x20);
+ min = (unsigned long) rec[1]/16*10 + (unsigned long) rec[1]%16;
+ sec = (unsigned long) rec[2]/16*10 + (unsigned long) rec[2]%16;
+ frame = (unsigned long) rec[3]/16*10 + (unsigned long) rec[3]%16;
+ sector = min*CD_SECS*CD_FRAMES + sec*CD_FRAMES + frame;
+ if (sector) {
+ sector -= CD_BLOCK_OFFSET;
+#ifdef DEBUG
+ printk("sr_photocd: multisession CD detected: start: %lu\n",sector);
+#endif
+ }
+
+ /* now we do a get_density... */
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0;
+ *((unsigned long*)buf+1) = 12;
+ cmd[0] = 0x1a;
+ cmd[2] = 1;
+ cmd[4] = 12;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (TOSHIBA #2): 0x%x\n",rc);
+ break;
+ }
+#ifdef DEBUG
+ printk("sr_photocd: get_density: 0x%x\n",rec[4]);
+#endif
+
+ /* ...and only if necessary a set_density */
+ if ((rec[4] != 0x81 && is_xa) || (rec[4] != 0 && !is_xa)) {
+#ifdef DEBUG
+ printk("sr_photocd: doing set_density\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 12; /* sending 12 bytes... */
+ *((unsigned long*)buf+1) = 0;
+ cmd[0] = 0x15;
+ cmd[1] = (1 << 4);
+ cmd[4] = 12;
+ send = &cmd[6]; /* this is a 6-Byte command */
+ send[ 3] = 0x08; /* the data for the command */
+ send[ 4] = (is_xa) ? 0x81 : 0; /* density 0x81 for XA-CD's, 0 else */
+ send[10] = 0x08;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (TOSHIBA #3): 0x%x\n",rc);
+ }
+ /* The set_density command may have changed the sector size or capacity. */
+ scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size = 1;
+ }
+ break;
+
+ case SCSI_MAN_SONY: /* Thomas QUINOT <thomas@melchior.cuivre.fdn.fr> */
+ case SCSI_MAN_PIONEER:
+#ifdef DEBUG
+ printk("sr_photocd: use SONY/PIONEER code\n");
+#endif
+ memset(buf,0,40);
+ *((unsigned long*)buf) = 0x0; /* we send nothing... */
+ *((unsigned long*)buf+1) = 0x0c; /* and receive 0x0c bytes */
+ cmd[0] = 0x43; /* Read TOC */
+ cmd[8] = 0x0c;
+ cmd[9] = 0x40;
+ rc = kernel_scsi_ioctl(scsi_CDs[MINOR(inode->i_rdev)].device,
+ SCSI_IOCTL_SEND_COMMAND, buf);
+
+ if (rc != 0) {
+ printk("sr_photocd: ioctl error (SONY): 0x%x\n",rc);
+ break;
+ }
+ if ((rec[0] << 8) + rec[1] != 0x0a) {
+ printk("sr_photocd: (SONY) Hmm, seems the CDROM doesn't support multisession CD's\n");
+ no_multi = 1;
+ break;
+ }
+ sector = rec[11] + (rec[10] << 8) + (rec[9] << 16) + (rec[8] << 24);
+ is_xa = !!sector;
+#ifdef DEBUG
+ if (sector)
+ printk ("sr_photocd: multisession CD detected. start: %lu\n",sector);
+#endif
+ break;
+
+ case SCSI_MAN_NEC_OLDCDR:
+ case SCSI_MAN_UNKNOWN:
+ default:
+ sector = 0;
+ no_multi = 1;
+ break; }
+
+ scsi_CDs[MINOR(inode->i_rdev)].mpcd_sector = sector;
+ if (is_xa)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x01;
+ else
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags &= ~0x01;
+ if (no_multi)
+ scsi_CDs[MINOR(inode->i_rdev)].xa_flags |= 0x02;
+ return;
+}
+
+static int sr_open(struct inode * inode, struct file * filp)
+{
+ if(MINOR(inode->i_rdev) >= sr_template.nr_dev ||
+ !scsi_CDs[MINOR(inode->i_rdev)].device) return -ENXIO; /* No such device */
+
+ if (filp->f_mode & 2)
+ return -EROFS;
+
+ check_disk_change(inode->i_rdev);
+
+ if(!scsi_CDs[MINOR(inode->i_rdev)].device->access_count++)
+ sr_ioctl(inode, NULL, SCSI_IOCTL_DOORLOCK, 0);
+ if (scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)
+ (*scsi_CDs[MINOR(inode->i_rdev)].device->host->hostt->usage_count)++;
+ if(sr_template.usage_count) (*sr_template.usage_count)++;
+
+ sr_photocd(inode);
+
+ /* If this device did not have media in the drive at boot time, then
+ * we would have been unable to get the sector size. Check to see if
+ * this is the case, and try again.
+ */
+
+ if(scsi_CDs[MINOR(inode->i_rdev)].needs_sector_size)
+ get_sectorsize(MINOR(inode->i_rdev));
+
+ return 0;
+}
+
+
+/*
+ * do_sr_request() is the request handler function for the sr driver.
+ * Its function in life is to take block device requests, and
+ * translate them to SCSI commands.
+ */
+
+static void do_sr_request (void)
+{
+ Scsi_Cmnd * SCpnt = NULL;
+ struct request * req = NULL;
+ Scsi_Device * SDev;
+ unsigned long flags;
+ int flag = 0;
+
+ while (1==1){
+ save_flags(flags);
+ cli();
+ if (CURRENT != NULL && CURRENT->rq_status == RQ_INACTIVE) {
+ restore_flags(flags);
+ return;
+ };
+
+ INIT_SCSI_REQUEST;
+
+ SDev = scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device;
+
+ /*
+ * I am not sure where the best place to do this is. We need
+ * to hook in a place where we are likely to come if in user
+ * space.
+ */
+ if( SDev->was_reset )
+ {
+ /*
+ * We need to relock the door, but we might
+ * be in an interrupt handler. Only do this
+ * from user space, since we do not want to
+ * sleep from an interrupt.
+ */
+ if( SDev->removable && !intr_count )
+ {
+ scsi_ioctl(SDev, SCSI_IOCTL_DOORLOCK, 0);
+ }
+ SDev->was_reset = 0;
+ }
+
+ if (flag++ == 0)
+ SCpnt = allocate_device(&CURRENT,
+ scsi_CDs[DEVICE_NR(CURRENT->rq_dev)].device, 0);
+ else SCpnt = NULL;
+ restore_flags(flags);
+
+ /* This is a performance enhancement. We dig down into the request list and
+ * try and find a queueable request (i.e. device not busy, and host able to
+ * accept another command. If we find one, then we queue it. This can
+ * make a big difference on systems with more than one disk drive. We want
+ * to have the interrupts off when monkeying with the request list, because
+ * otherwise the kernel might try and slip in a request in between somewhere. */
+
+ if (!SCpnt && sr_template.nr_dev > 1){
+ struct request *req1;
+ req1 = NULL;
+ save_flags(flags);
+ cli();
+ req = CURRENT;
+ while(req){
+ SCpnt = request_queueable(req,
+ scsi_CDs[DEVICE_NR(req->rq_dev)].device);
+ if(SCpnt) break;
+ req1 = req;
+ req = req->next;
+ };
+ if (SCpnt && req->rq_status == RQ_INACTIVE) {
+ if (req == CURRENT)
+ CURRENT = CURRENT->next;
+ else
+ req1->next = req->next;
+ };
+ restore_flags(flags);
+ };
+
+ if (!SCpnt)
+ return; /* Could not find anything to do */
+
+ wake_up(&wait_for_request);
+
+ /* Queue command */
+ requeue_sr_request(SCpnt);
+ }; /* While */
+}
+
+void requeue_sr_request (Scsi_Cmnd * SCpnt)
+{
+ unsigned int dev, block, realcount;
+ unsigned char cmd[10], *buffer, tries;
+ int this_count, start, end_rec;
+
+ tries = 2;
+
+ repeat:
+ if(!SCpnt || SCpnt->request.rq_status == RQ_INACTIVE) {
+ do_sr_request();
+ return;
+ }
+
+ dev = MINOR(SCpnt->request.rq_dev);
+ block = SCpnt->request.sector;
+ buffer = NULL;
+ this_count = 0;
+
+ if (dev >= sr_template.nr_dev) {
+ /* printk("CD-ROM request error: invalid device.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ if (!scsi_CDs[dev].use) {
+ /* printk("CD-ROM request error: device marked not in use.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ if (scsi_CDs[dev].device->changed) {
+ /*
+ * quietly refuse to do anything to a changed disc
+ * until the changed bit has been reset
+ */
+ /* printk("CD-ROM has been changed. Prohibiting further I/O.\n"); */
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ tries = 2;
+ goto repeat;
+ }
+
+ switch (SCpnt->request.cmd)
+ {
+ case WRITE:
+ SCpnt = end_scsi_request(SCpnt, 0, SCpnt->request.nr_sectors);
+ goto repeat;
+ break;
+ case READ :
+ cmd[0] = READ_6;
+ break;
+ default :
+ panic ("Unknown sr command %d\n", SCpnt->request.cmd);
+ }
+
+ cmd[1] = (SCpnt->lun << 5) & 0xe0;
+
+ /*
+ * Now do the grungy work of figuring out which sectors we need, and
+ * where in memory we are going to put them.
+ *
+ * The variables we need are:
+ *
+ * this_count= number of 512 byte sectors being read
+ * block = starting cdrom sector to read.
+ * realcount = # of cdrom sectors to read
+ *
+ * The major difference between a scsi disk and a scsi cdrom
+ * is that we will always use scatter-gather if we can, because we can
+ * work around the fact that the buffer cache has a block size of 1024,
+ * and we have 2048 byte sectors. This code should work for buffers that
+ * are any multiple of 512 bytes long.
+ */
+
+ SCpnt->use_sg = 0;
+
+ if (SCpnt->host->sg_tablesize > 0 &&
+ (!need_isa_buffer ||
+ dma_free_sectors >= 10)) {
+ struct buffer_head * bh;
+ struct scatterlist * sgpnt;
+ int count, this_count_max;
+ bh = SCpnt->request.bh;
+ this_count = 0;
+ count = 0;
+ this_count_max = (scsi_CDs[dev].ten ? 0xffff : 0xff) << 4;
+ /* Calculate how many links we can use. First see if we need
+ * a padding record at the start */
+ this_count = SCpnt->request.sector % 4;
+ if(this_count) count++;
+ while(bh && count < SCpnt->host->sg_tablesize) {
+ if ((this_count + (bh->b_size >> 9)) > this_count_max) break;
+ this_count += (bh->b_size >> 9);
+ count++;
+ bh = bh->b_reqnext;
+ };
+ /* Fix up in case of an odd record at the end */
+ end_rec = 0;
+ if(this_count % 4) {
+ if (count < SCpnt->host->sg_tablesize) {
+ count++;
+ end_rec = (4 - (this_count % 4)) << 9;
+ this_count += 4 - (this_count % 4);
+ } else {
+ count--;
+ this_count -= (this_count % 4);
+ };
+ };
+ SCpnt->use_sg = count; /* Number of chains */
+ count = 512;/* scsi_malloc can only allocate in chunks of 512 bytes*/
+ while( count < (SCpnt->use_sg * sizeof(struct scatterlist)))
+ count = count << 1;
+ SCpnt->sglist_len = count;
+ sgpnt = (struct scatterlist * ) scsi_malloc(count);
+ if (!sgpnt) {
+ printk("Warning - running *really* short on DMA buffers\n");
+ SCpnt->use_sg = 0; /* No memory left - bail out */
+ } else {
+ buffer = (unsigned char *) sgpnt;
+ count = 0;
+ bh = SCpnt->request.bh;
+ if(SCpnt->request.sector % 4) {
+ sgpnt[count].length = (SCpnt->request.sector % 4) << 9;
+ sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+ if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+ sgpnt[count].alt_address = sgpnt[count].address; /* Flag to delete
+ if needed */
+ count++;
+ };
+ for(bh = SCpnt->request.bh; count < SCpnt->use_sg;
+ count++, bh = bh->b_reqnext) {
+ if (bh) { /* Need a placeholder at the end of the record? */
+ sgpnt[count].address = bh->b_data;
+ sgpnt[count].length = bh->b_size;
+ sgpnt[count].alt_address = NULL;
+ } else {
+ sgpnt[count].address = (char *) scsi_malloc(end_rec);
+ if(!sgpnt[count].address) panic("SCSI DMA pool exhausted.");
+ sgpnt[count].length = end_rec;
+ sgpnt[count].alt_address = sgpnt[count].address;
+ if (count+1 != SCpnt->use_sg) panic("Bad sr request list");
+ break;
+ };
+ if (((long) sgpnt[count].address) + sgpnt[count].length > ISA_DMA_THRESHOLD &&
+ SCpnt->host->unchecked_isa_dma) {
+ sgpnt[count].alt_address = sgpnt[count].address;
+ /* We try and avoid exhausting the DMA pool, since it is easier
+ * to control usage here. In other places we might have a more
+ * pressing need, and we would be screwed if we ran out */
+ if(dma_free_sectors < (sgpnt[count].length >> 9) + 5) {
+ sgpnt[count].address = NULL;
+ } else {
+ sgpnt[count].address = (char *) scsi_malloc(sgpnt[count].length);
+ };
+ /* If we start running low on DMA buffers, we abort the scatter-gather
+ * operation, and free all of the memory we have allocated. We want to
+ * ensure that all scsi operations are able to do at least a non-scatter/gather
+ * operation */
+ if(sgpnt[count].address == NULL){ /* Out of dma memory */
+ printk("Warning: Running low on SCSI DMA buffers");
+ /* Try switching back to a non scatter-gather operation. */
+ while(--count >= 0){
+ if(sgpnt[count].alt_address)
+ scsi_free(sgpnt[count].address, sgpnt[count].length);
+ };
+ SCpnt->use_sg = 0;
+ scsi_free(buffer, SCpnt->sglist_len);
+ break;
+ }; /* if address == NULL */
+ }; /* if need DMA fixup */
+ }; /* for loop to fill list */
+#ifdef DEBUG
+ printk("SR: %d %d %d %d %d *** ",SCpnt->use_sg, SCpnt->request.sector,
+ this_count,
+ SCpnt->request.current_nr_sectors,
+ SCpnt->request.nr_sectors);
+ for(count=0; count<SCpnt->use_sg; count++)
+ printk("SGlist: %d %x %x %x\n", count,
+ sgpnt[count].address,
+ sgpnt[count].alt_address,
+ sgpnt[count].length);
+#endif
+ }; /* Able to allocate scatter-gather list */
+ };
+
+ if (SCpnt->use_sg == 0){
+ /* We cannot use scatter-gather. Do this the old fashion way */
+ if (!SCpnt->request.bh)
+ this_count = SCpnt->request.nr_sectors;
+ else
+ this_count = (SCpnt->request.bh->b_size >> 9);
+
+ start = block % 4;
+ if (start)
+ {
+ this_count = ((this_count > 4 - start) ?
+ (4 - start) : (this_count));
+ buffer = (unsigned char *) scsi_malloc(2048);
+ }
+ else if (this_count < 4)
+ {
+ buffer = (unsigned char *) scsi_malloc(2048);
+ }
+ else
+ {
+ this_count -= this_count % 4;
+ buffer = (unsigned char *) SCpnt->request.buffer;
+ if (((long) buffer) + (this_count << 9) > ISA_DMA_THRESHOLD &&
+ SCpnt->host->unchecked_isa_dma)
+ buffer = (unsigned char *) scsi_malloc(this_count << 9);
+ }
+ };
+
+ if (scsi_CDs[dev].sector_size == 2048)
+ block = block >> 2; /* These are the sectors that the cdrom uses */
+ else
+ block = block & 0xfffffffc;
+
+ realcount = (this_count + 3) / 4;
+
+ if (scsi_CDs[dev].sector_size == 512) realcount = realcount << 2;
+
+ if (((realcount > 0xff) || (block > 0x1fffff)) && scsi_CDs[dev].ten)
+ {
+ if (realcount > 0xffff)
+ {
+ realcount = 0xffff;
+ this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+ }
+
+ cmd[0] += READ_10 - READ_6 ;
+ cmd[2] = (unsigned char) (block >> 24) & 0xff;
+ cmd[3] = (unsigned char) (block >> 16) & 0xff;
+ cmd[4] = (unsigned char) (block >> 8) & 0xff;
+ cmd[5] = (unsigned char) block & 0xff;
+ cmd[6] = cmd[9] = 0;
+ cmd[7] = (unsigned char) (realcount >> 8) & 0xff;
+ cmd[8] = (unsigned char) realcount & 0xff;
+ }
+ else
+ {
+ if (realcount > 0xff)
+ {
+ realcount = 0xff;
+ this_count = realcount * (scsi_CDs[dev].sector_size >> 9);
+ }
+
+ cmd[1] |= (unsigned char) ((block >> 16) & 0x1f);
+ cmd[2] = (unsigned char) ((block >> 8) & 0xff);
+ cmd[3] = (unsigned char) block & 0xff;
+ cmd[4] = (unsigned char) realcount;
+ cmd[5] = 0;
+ }
+
+#ifdef DEBUG
+ {
+ int i;
+ printk("ReadCD: %d %d %d %d\n",block, realcount, buffer, this_count);
+ printk("Use sg: %d\n", SCpnt->use_sg);
+ printk("Dumping command: ");
+ for(i=0; i<12; i++) printk("%2.2x ", cmd[i]);
+ printk("\n");
+ };
+#endif
+
+ /* Some dumb host adapters can speed transfers by knowing the
+ * minimum transfersize in advance.
+ *
+ * We shouldn't disconnect in the middle of a sector, but the cdrom
+ * sector size can be larger than the size of a buffer and the
+ * transfer may be split to the size of a buffer. So it's safe to
+ * assume that we can at least transfer the minimum of the buffer
+ * size (1024) and the sector size between each connect / disconnect.
+ */
+
+ SCpnt->transfersize = (scsi_CDs[dev].sector_size > 1024) ?
+ 1024 : scsi_CDs[dev].sector_size;
+
+ SCpnt->this_count = this_count;
+ scsi_do_cmd (SCpnt, (void *) cmd, buffer,
+ realcount * scsi_CDs[dev].sector_size,
+ rw_intr, SR_TIMEOUT, MAX_RETRIES);
+}
+
+static int sr_detect(Scsi_Device * SDp){
+
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 0;
+
+ printk("Detected scsi CD-ROM sr%d at scsi%d, channel %d, id %d, lun %d\n",
+ sr_template.dev_noticed++,
+ SDp->host->host_no, SDp->channel, SDp->id, SDp->lun);
+
+ return 1;
+}
+
+static int sr_attach(Scsi_Device * SDp){
+ Scsi_CD * cpnt;
+ int i;
+
+ if(SDp->type != TYPE_ROM && SDp->type != TYPE_WORM) return 1;
+
+ if (sr_template.nr_dev >= sr_template.dev_max)
+ {
+ SDp->attached--;
+ return 1;
+ }
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(!cpnt->device) break;
+
+ if(i >= sr_template.dev_max) panic ("scsi_devices corrupt (sr)");
+
+ SDp->scsi_request_fn = do_sr_request;
+ scsi_CDs[i].device = SDp;
+ sr_template.nr_dev++;
+ if(sr_template.nr_dev > sr_template.dev_max)
+ panic ("scsi_devices corrupt (sr)");
+ return 0;
+}
+
+
+static void sr_init_done (Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+static void get_sectorsize(int i){
+ unsigned char cmd[10];
+ unsigned char *buffer;
+ int the_result, retries;
+ Scsi_Cmnd * SCpnt;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ SCpnt = allocate_device(NULL, scsi_CDs[i].device, 1);
+
+ retries = 3;
+ do {
+ cmd[0] = READ_CAPACITY;
+ cmd[1] = (scsi_CDs[i].device->lun << 5) & 0xe0;
+ memset ((void *) &cmd[2], 0, 8);
+ SCpnt->request.rq_status = RQ_SCSI_BUSY; /* Mark as really busy */
+ SCpnt->cmd_len = 0;
+
+ memset(buffer, 0, 8);
+
+ /* Do the command and wait.. */
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd (SCpnt,
+ (void *) cmd, (void *) buffer,
+ 512, sr_init_done, SR_TIMEOUT,
+ MAX_RETRIES);
+ down(&sem);
+ }
+
+ the_result = SCpnt->result;
+ retries--;
+
+ } while(the_result && retries);
+
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Mark as not busy */
+
+ wake_up(&SCpnt->device->device_wait);
+
+ if (the_result) {
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+ } else {
+ scsi_CDs[i].capacity = (buffer[0] << 24) |
+ (buffer[1] << 16) | (buffer[2] << 8) | buffer[3];
+ scsi_CDs[i].sector_size = (buffer[4] << 24) |
+ (buffer[5] << 16) | (buffer[6] << 8) | buffer[7];
+ if(scsi_CDs[i].sector_size == 0) scsi_CDs[i].sector_size = 2048;
+ /* Work around bug/feature in HP 4020i CD-Recorder... */
+ if(scsi_CDs[i].sector_size == 2340) scsi_CDs[i].sector_size = 2048;
+ if(scsi_CDs[i].sector_size != 2048 &&
+ scsi_CDs[i].sector_size != 512) {
+ printk ("scd%d : unsupported sector size %d.\n",
+ i, scsi_CDs[i].sector_size);
+ scsi_CDs[i].capacity = 0;
+ scsi_CDs[i].needs_sector_size = 1;
+ };
+ if(scsi_CDs[i].sector_size == 2048)
+ scsi_CDs[i].capacity *= 4;
+ scsi_CDs[i].needs_sector_size = 0;
+ sr_sizes[i] = scsi_CDs[i].capacity;
+ };
+ scsi_free(buffer, 512);
+}
+
+static int sr_registered = 0;
+
+static int sr_init()
+{
+ int i;
+
+ if(sr_template.dev_noticed == 0) return 0;
+
+ if(!sr_registered) {
+ if (register_blkdev(MAJOR_NR,"sr",&sr_fops)) {
+ printk("Unable to get major %d for SCSI-CD\n",MAJOR_NR);
+ return 1;
+ }
+ sr_registered++;
+ }
+
+
+ if (scsi_CDs) return 0;
+ sr_template.dev_max = sr_template.dev_noticed + SR_EXTRA_DEVS;
+ scsi_CDs = (Scsi_CD *) scsi_init_malloc(sr_template.dev_max * sizeof(Scsi_CD), GFP_ATOMIC);
+ memset(scsi_CDs, 0, sr_template.dev_max * sizeof(Scsi_CD));
+
+ sr_sizes = (int *) scsi_init_malloc(sr_template.dev_max * sizeof(int), GFP_ATOMIC);
+ memset(sr_sizes, 0, sr_template.dev_max * sizeof(int));
+
+ sr_blocksizes = (int *) scsi_init_malloc(sr_template.dev_max *
+ sizeof(int), GFP_ATOMIC);
+ for(i=0;i<sr_template.dev_max;i++) sr_blocksizes[i] = 2048;
+ blksize_size[MAJOR_NR] = sr_blocksizes;
+ return 0;
+}
+
+void sr_finish()
+{
+ int i;
+
+ blk_dev[MAJOR_NR].request_fn = DEVICE_REQUEST;
+ blk_size[MAJOR_NR] = sr_sizes;
+
+ for (i = 0; i < sr_template.nr_dev; ++i)
+ {
+ /* If we have already seen this, then skip it. Comes up
+ * with loadable modules. */
+ if (scsi_CDs[i].capacity) continue;
+ scsi_CDs[i].capacity = 0x1fffff;
+ scsi_CDs[i].sector_size = 2048; /* A guess, just in case */
+ scsi_CDs[i].needs_sector_size = 1;
+#if 0
+ /* seems better to leave this for later */
+ get_sectorsize(i);
+ printk("Scd sectorsize = %d bytes.\n", scsi_CDs[i].sector_size);
+#endif
+ scsi_CDs[i].use = 1;
+ scsi_CDs[i].ten = 1;
+ scsi_CDs[i].remap = 1;
+ scsi_CDs[i].auto_eject = 0; /* Default is not to eject upon unmount. */
+ sr_sizes[i] = scsi_CDs[i].capacity;
+ }
+
+
+ /* If our host adapter is capable of scatter-gather, then we increase
+ * the read-ahead to 16 blocks (32 sectors). If not, we use
+ * a two block (4 sector) read ahead. */
+ if(scsi_CDs[0].device && scsi_CDs[0].device->host->sg_tablesize)
+ read_ahead[MAJOR_NR] = 32; /* 32 sector read-ahead. Always removable. */
+ else
+ read_ahead[MAJOR_NR] = 4; /* 4 sector read-ahead */
+
+ return;
+}
+
+static void sr_detach(Scsi_Device * SDp)
+{
+ Scsi_CD * cpnt;
+ int i;
+
+ for(cpnt = scsi_CDs, i=0; i<sr_template.dev_max; i++, cpnt++)
+ if(cpnt->device == SDp) {
+ kdev_t devi = MKDEV(MAJOR_NR, i);
+
+ /*
+ * Since the cdrom is read-only, no need to sync the device.
+ * We should be kind to our buffer cache, however.
+ */
+ invalidate_inodes(devi);
+ invalidate_buffers(devi);
+
+ /*
+ * Reset things back to a sane state so that one can re-load a new
+ * driver (perhaps the same one).
+ */
+ cpnt->device = NULL;
+ cpnt->capacity = 0;
+ SDp->attached--;
+ sr_template.nr_dev--;
+ sr_template.dev_noticed--;
+ sr_sizes[i] = 0;
+ return;
+ }
+ return;
+}
+
+
+#ifdef MODULE
+
+int init_module(void) {
+ sr_template.usage_count = &mod_use_count_;
+ return scsi_register_module(MODULE_SCSI_DEV, &sr_template);
+}
+
+void cleanup_module( void)
+{
+ scsi_unregister_module(MODULE_SCSI_DEV, &sr_template);
+ unregister_blkdev(SCSI_CDROM_MAJOR, "sr");
+ sr_registered--;
+ if(scsi_CDs != NULL) {
+ scsi_init_free((char *) scsi_CDs,
+ (sr_template.dev_noticed + SR_EXTRA_DEVS)
+ * sizeof(Scsi_CD));
+
+ scsi_init_free((char *) sr_sizes, sr_template.dev_max * sizeof(int));
+ scsi_init_free((char *) sr_blocksizes, sr_template.dev_max * sizeof(int));
+ }
+
+ blksize_size[MAJOR_NR] = NULL;
+ blk_dev[MAJOR_NR].request_fn = NULL;
+ blk_size[MAJOR_NR] = NULL;
+ read_ahead[MAJOR_NR] = 0;
+
+ sr_template.dev_max = 0;
+}
+#endif /* MODULE */
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/sr.h b/i386/i386at/gpl/linux/scsi/sr.h
new file mode 100644
index 00000000..381678a6
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/sr.h
@@ -0,0 +1,40 @@
+/*
+ * sr.h by David Giller
+ * CD-ROM disk driver header file
+ *
+ * adapted from:
+ * sd.h Copyright (C) 1992 Drew Eckhardt
+ * SCSI disk driver header file by
+ * Drew Eckhardt
+ *
+ * <drew@colorado.edu>
+ *
+ * Modified by Eric Youngdale eric@aib.com to
+ * add scatter-gather, multiple outstanding request, and other
+ * enhancements.
+ */
+
+#ifndef _SR_H
+#define _SR_H
+
+#include "scsi.h"
+
+typedef struct
+ {
+ unsigned capacity; /* size in blocks */
+ unsigned sector_size; /* size in bytes */
+ Scsi_Device *device;
+ unsigned long mpcd_sector; /* for reading multisession-CD's */
+ char xa_flags; /* some flags for handling XA-CD's */
+ unsigned char sector_bit_size; /* sector size = 2^sector_bit_size */
+ unsigned char sector_bit_shift; /* sectors/FS block = 2^sector_bit_shift*/
+ unsigned needs_sector_size:1; /* needs to get sector size */
+ unsigned ten:1; /* support ten byte commands */
+ unsigned remap:1; /* support remapping */
+ unsigned use:1; /* is this device still supportable */
+ unsigned auto_eject:1; /* auto-eject medium on last release. */
+ } Scsi_CD;
+
+extern Scsi_CD * scsi_CDs;
+
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/sr_ioctl.c b/i386/i386at/gpl/linux/scsi/sr_ioctl.c
new file mode 100644
index 00000000..2313cf8c
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/sr_ioctl.c
@@ -0,0 +1,489 @@
+#include <linux/kernel.h>
+#include <linux/sched.h>
+#include <linux/mm.h>
+#include <linux/fs.h>
+#include <asm/segment.h>
+#include <linux/errno.h>
+
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sr.h"
+#include "scsi_ioctl.h"
+
+#include <linux/cdrom.h>
+
+#define IOCTL_RETRIES 3
+/* The CDROM is fairly slow, so we need a little extra time */
+/* In fact, it is very slow if it has to spin up first */
+#define IOCTL_TIMEOUT 3000
+
+static void sr_ioctl_done(Scsi_Cmnd * SCpnt)
+{
+ struct request * req;
+
+ req = &SCpnt->request;
+ req->rq_status = RQ_SCSI_DONE; /* Busy, but indicate request done */
+
+ if (req->sem != NULL) {
+ up(req->sem);
+ }
+}
+
+/* We do our own retries because we want to know what the specific
+ error code is. Normally the UNIT_ATTENTION code will automatically
+ clear after one error */
+
+static int do_ioctl(int target, unsigned char * sr_cmd, void * buffer, unsigned buflength)
+{
+ Scsi_Cmnd * SCpnt;
+ int result;
+
+ SCpnt = allocate_device(NULL, scsi_CDs[target].device, 1);
+ {
+ struct semaphore sem = MUTEX_LOCKED;
+ SCpnt->request.sem = &sem;
+ scsi_do_cmd(SCpnt,
+ (void *) sr_cmd, buffer, buflength, sr_ioctl_done,
+ IOCTL_TIMEOUT, IOCTL_RETRIES);
+ down(&sem);
+ }
+
+ result = SCpnt->result;
+
+ /* Minimal error checking. Ignore cases we know about, and report the rest. */
+ if(driver_byte(result) != 0)
+ switch(SCpnt->sense_buffer[2] & 0xf) {
+ case UNIT_ATTENTION:
+ scsi_CDs[target].device->changed = 1;
+ printk("Disc change detected.\n");
+ break;
+ case NOT_READY: /* This happens if there is no disc in drive */
+ printk("CDROM not ready. Make sure there is a disc in the drive.\n");
+ break;
+ case ILLEGAL_REQUEST:
+ printk("CDROM (ioctl) reports ILLEGAL REQUEST.\n");
+ break;
+ default:
+ printk("SCSI CD error: host %d id %d lun %d return code = %03x\n",
+ scsi_CDs[target].device->host->host_no,
+ scsi_CDs[target].device->id,
+ scsi_CDs[target].device->lun,
+ result);
+ printk("\tSense class %x, sense error %x, extended sense %x\n",
+ sense_class(SCpnt->sense_buffer[0]),
+ sense_error(SCpnt->sense_buffer[0]),
+ SCpnt->sense_buffer[2] & 0xf);
+
+ };
+
+ result = SCpnt->result;
+ SCpnt->request.rq_status = RQ_INACTIVE; /* Deallocate */
+ wake_up(&SCpnt->device->device_wait);
+ /* Wake up a process waiting for device*/
+ return result;
+}
+
+int sr_ioctl(struct inode * inode, struct file * file, unsigned int cmd, unsigned long arg)
+{
+ u_char sr_cmd[10];
+
+ kdev_t dev = inode->i_rdev;
+ int result, target, err;
+
+ target = MINOR(dev);
+
+ if (target >= sr_template.nr_dev ||
+ !scsi_CDs[target].device) return -ENXIO;
+
+ switch (cmd)
+ {
+ /* Sun-compatible */
+ case CDROMPAUSE:
+
+ sr_cmd[0] = SCMD_PAUSE_RESUME;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
+ sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
+ sr_cmd[8] = 0;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+
+ case CDROMRESUME:
+
+ sr_cmd[0] = SCMD_PAUSE_RESUME;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = 0;
+ sr_cmd[5] = sr_cmd[6] = sr_cmd[7] = 0;
+ sr_cmd[8] = 1;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+
+ return result;
+
+ case CDROMPLAYMSF:
+ {
+ struct cdrom_msf msf;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (msf));
+ if (err) return err;
+
+ memcpy_fromfs(&msf, (void *) arg, sizeof(msf));
+
+ sr_cmd[0] = SCMD_PLAYAUDIO_MSF;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = 0;
+ sr_cmd[3] = msf.cdmsf_min0;
+ sr_cmd[4] = msf.cdmsf_sec0;
+ sr_cmd[5] = msf.cdmsf_frame0;
+ sr_cmd[6] = msf.cdmsf_min1;
+ sr_cmd[7] = msf.cdmsf_sec1;
+ sr_cmd[8] = msf.cdmsf_frame1;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+ }
+
+ case CDROMPLAYBLK:
+ {
+ struct cdrom_blk blk;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (blk));
+ if (err) return err;
+
+ memcpy_fromfs(&blk, (void *) arg, sizeof(blk));
+
+ sr_cmd[0] = SCMD_PLAYAUDIO10;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = blk.from >> 24;
+ sr_cmd[3] = blk.from >> 16;
+ sr_cmd[4] = blk.from >> 8;
+ sr_cmd[5] = blk.from;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = blk.len >> 8;
+ sr_cmd[8] = blk.len;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+ }
+
+ case CDROMPLAYTRKIND:
+ {
+ struct cdrom_ti ti;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (ti));
+ if (err) return err;
+
+ memcpy_fromfs(&ti, (void *) arg, sizeof(ti));
+
+ sr_cmd[0] = SCMD_PLAYAUDIO_TI;
+ sr_cmd[1] = scsi_CDs[target].device->lun << 5;
+ sr_cmd[2] = 0;
+ sr_cmd[3] = 0;
+ sr_cmd[4] = ti.cdti_trk0;
+ sr_cmd[5] = ti.cdti_ind0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = ti.cdti_trk1;
+ sr_cmd[8] = ti.cdti_ind1;
+ sr_cmd[9] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+
+ return result;
+ }
+
+ case CDROMREADTOCHDR:
+ {
+ struct cdrom_tochdr tochdr;
+ char * buffer;
+
+ sr_cmd[0] = SCMD_READ_TOC;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = 0; /* MSB of length (12) */
+ sr_cmd[8] = 12; /* LSB of length */
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl(target, sr_cmd, buffer, 12);
+
+ tochdr.cdth_trk0 = buffer[2];
+ tochdr.cdth_trk1 = buffer[3];
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tochdr));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &tochdr, sizeof (struct cdrom_tochdr));
+
+ return result;
+ }
+
+ case CDROMREADTOCENTRY:
+ {
+ struct cdrom_tocentry tocentry;
+ char * buffer;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_tocentry));
+ if (err) return err;
+
+ memcpy_fromfs (&tocentry, (void *) arg, sizeof (struct cdrom_tocentry));
+
+ sr_cmd[0] = SCMD_READ_TOC;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = tocentry.cdte_track;
+ sr_cmd[7] = 0; /* MSB of length (12) */
+ sr_cmd[8] = 12; /* LSB of length */
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl (target, sr_cmd, buffer, 12);
+
+ if (tocentry.cdte_format == CDROM_MSF) {
+ tocentry.cdte_addr.msf.minute = buffer[9];
+ tocentry.cdte_addr.msf.second = buffer[10];
+ tocentry.cdte_addr.msf.frame = buffer[11];
+ tocentry.cdte_ctrl = buffer[5] & 0xf;
+ }
+ else
+ tocentry.cdte_addr.lba = (int) buffer[0];
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_tocentry));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &tocentry, sizeof (struct cdrom_tocentry));
+
+ return result;
+ }
+
+ case CDROMSTOP:
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 0;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+
+ case CDROMSTART:
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 1;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 1;
+
+ result = do_ioctl(target, sr_cmd, NULL, 255);
+ return result;
+
+ case CDROMEJECT:
+ /*
+ * Allow 0 for access count for auto-eject feature.
+ */
+ if (scsi_CDs[target].device -> access_count > 1)
+ return -EBUSY;
+
+ sr_ioctl (inode, NULL, SCSI_IOCTL_DOORUNLOCK, 0);
+ sr_cmd[0] = START_STOP;
+ sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 1;
+ sr_cmd[2] = sr_cmd[3] = sr_cmd[5] = 0;
+ sr_cmd[4] = 0x02;
+
+ if (!(result = do_ioctl(target, sr_cmd, NULL, 255)))
+ scsi_CDs[target].device -> changed = 1;
+
+ return result;
+
+ case CDROMEJECT_SW:
+ scsi_CDs[target].auto_eject = !!arg;
+ return 0;
+
+ case CDROMVOLCTRL:
+ {
+ char * buffer, * mask;
+ struct cdrom_volctrl volctrl;
+
+ err = verify_area (VERIFY_READ, (void *) arg, sizeof (struct cdrom_volctrl));
+ if (err) return err;
+
+ memcpy_fromfs (&volctrl, (void *) arg, sizeof (struct cdrom_volctrl));
+
+ /* First we get the current params so we can just twiddle the volume */
+
+ sr_cmd[0] = MODE_SENSE;
+ sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+ sr_cmd[2] = 0xe; /* Want mode page 0xe, CDROM audio params */
+ sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ buffer = (unsigned char *) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ if ((result = do_ioctl (target, sr_cmd, buffer, 28))) {
+ printk ("Hosed while obtaining audio mode page\n");
+ scsi_free(buffer, 512);
+ return result;
+ }
+
+ sr_cmd[0] = MODE_SENSE;
+ sr_cmd[1] = (scsi_CDs[target].device -> lun) << 5;
+ sr_cmd[2] = 0x4e; /* Want the mask for mode page 0xe */
+ sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ mask = (unsigned char *) scsi_malloc(512);
+ if(!mask) {
+ scsi_free(buffer, 512);
+ return -ENOMEM;
+ };
+
+ if ((result = do_ioctl (target, sr_cmd, mask, 28))) {
+ printk ("Hosed while obtaining mask for audio mode page\n");
+ scsi_free(buffer, 512);
+ scsi_free(mask, 512);
+ return result;
+ }
+
+ /* Now mask and substitute our own volume and reuse the rest */
+ buffer[0] = 0; /* Clear reserved field */
+
+ buffer[21] = volctrl.channel0 & mask[21];
+ buffer[23] = volctrl.channel1 & mask[23];
+ buffer[25] = volctrl.channel2 & mask[25];
+ buffer[27] = volctrl.channel3 & mask[27];
+
+ sr_cmd[0] = MODE_SELECT;
+ sr_cmd[1] = ((scsi_CDs[target].device -> lun) << 5) | 0x10; /* Params are SCSI-2 */
+ sr_cmd[2] = sr_cmd[3] = 0;
+ sr_cmd[4] = 28;
+ sr_cmd[5] = 0;
+
+ result = do_ioctl (target, sr_cmd, buffer, 28);
+ scsi_free(buffer, 512);
+ scsi_free(mask, 512);
+ return result;
+ }
+
+ case CDROMSUBCHNL:
+ {
+ struct cdrom_subchnl subchnl;
+ char * buffer;
+
+ sr_cmd[0] = SCMD_READ_SUBCHANNEL;
+ sr_cmd[1] = ((scsi_CDs[target].device->lun) << 5) | 0x02; /* MSF format */
+ sr_cmd[2] = 0x40; /* I do want the subchannel info */
+ sr_cmd[3] = 0x01; /* Give me current position info */
+ sr_cmd[4] = sr_cmd[5] = 0;
+ sr_cmd[6] = 0;
+ sr_cmd[7] = 0;
+ sr_cmd[8] = 16;
+ sr_cmd[9] = 0;
+
+ buffer = (unsigned char*) scsi_malloc(512);
+ if(!buffer) return -ENOMEM;
+
+ result = do_ioctl(target, sr_cmd, buffer, 16);
+
+ subchnl.cdsc_audiostatus = buffer[1];
+ subchnl.cdsc_format = CDROM_MSF;
+ subchnl.cdsc_ctrl = buffer[5] & 0xf;
+ subchnl.cdsc_trk = buffer[6];
+ subchnl.cdsc_ind = buffer[7];
+
+ subchnl.cdsc_reladdr.msf.minute = buffer[13];
+ subchnl.cdsc_reladdr.msf.second = buffer[14];
+ subchnl.cdsc_reladdr.msf.frame = buffer[15];
+ subchnl.cdsc_absaddr.msf.minute = buffer[9];
+ subchnl.cdsc_absaddr.msf.second = buffer[10];
+ subchnl.cdsc_absaddr.msf.frame = buffer[11];
+
+ scsi_free(buffer, 512);
+
+ err = verify_area (VERIFY_WRITE, (void *) arg, sizeof (struct cdrom_subchnl));
+ if (err)
+ return err;
+ memcpy_tofs ((void *) arg, &subchnl, sizeof (struct cdrom_subchnl));
+ return result;
+ }
+
+ case CDROMREADMODE2:
+ return -EINVAL;
+ case CDROMREADMODE1:
+ return -EINVAL;
+
+ /* block-copy from ../block/sbpcd.c with some adjustments... */
+ case CDROMMULTISESSION: /* tell start-of-last-session to user */
+ {
+ struct cdrom_multisession ms_info;
+ long lba;
+
+ err = verify_area(VERIFY_READ, (void *) arg,
+ sizeof(struct cdrom_multisession));
+ if (err) return (err);
+
+ memcpy_fromfs(&ms_info, (void *) arg, sizeof(struct cdrom_multisession));
+
+ if (ms_info.addr_format==CDROM_MSF) { /* MSF-bin requested */
+ lba = scsi_CDs[target].mpcd_sector+CD_BLOCK_OFFSET;
+ ms_info.addr.msf.minute = lba / (CD_SECS*CD_FRAMES);
+ lba %= CD_SECS*CD_FRAMES;
+ ms_info.addr.msf.second = lba / CD_FRAMES;
+ ms_info.addr.msf.frame = lba % CD_FRAMES;
+ } else if (ms_info.addr_format==CDROM_LBA) /* lba requested */
+ ms_info.addr.lba=scsi_CDs[target].mpcd_sector;
+ else return (-EINVAL);
+
+ ms_info.xa_flag=scsi_CDs[target].xa_flags & 0x01;
+
+ err=verify_area(VERIFY_WRITE,(void *) arg,
+ sizeof(struct cdrom_multisession));
+ if (err) return (err);
+
+ memcpy_tofs((void *) arg, &ms_info, sizeof(struct cdrom_multisession));
+ return (0);
+ }
+
+ case BLKRASET:
+ if(!suser()) return -EACCES;
+ if(!(inode->i_rdev)) return -EINVAL;
+ if(arg > 0xff) return -EINVAL;
+ read_ahead[MAJOR(inode->i_rdev)] = arg;
+ return 0;
+ RO_IOCTLS(dev,arg);
+ default:
+ return scsi_ioctl(scsi_CDs[target].device,cmd,(void *) arg);
+ }
+}
+
+/*
+ * 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/i386/i386at/gpl/linux/scsi/t128.c b/i386/i386at/gpl/linux/scsi/t128.c
new file mode 100644
index 00000000..9212b61e
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/t128.c
@@ -0,0 +1,413 @@
+#define AUTOSENSE
+#define PSEUDO_DMA
+
+/*
+ * Trantor T128/T128F/T228 driver
+ * Note : architecturally, the T100 and T130 are different and won't
+ * work
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 3.
+ *
+ * For more information, please consult
+ *
+ * Trantor Systems, Ltd.
+ * T128/T128F/T228 SCSI Host Adapter
+ * Hardware Specifications
+ *
+ * Trantor Systems, Ltd.
+ * 5415 Randall Place
+ * Fremont, CA 94538
+ * 1+ (415) 770-1400, FAX 1+ (415) 770-9910
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * Options :
+ * AUTOSENSE - if defined, REQUEST SENSE will be performed automatically
+ * for commands that return with a CHECK CONDITION status.
+ *
+ * PSEUDO_DMA - enables PSEUDO-DMA hardware, should give a 3-4X performance
+ * increase compared to polled I/O.
+ *
+ * PARITY - enable parity checking. Not supported.
+ *
+ * SCSI2 - enable support for SCSI-II tagged queueing. Untested.
+ *
+ *
+ * UNSAFE - leave interrupts enabled during pseudo-DMA transfers. You
+ * only really want to use this if you're having a problem with
+ * dropped characters during high speed communications, and even
+ * then, you're going to be better off twiddling with transfersize.
+ *
+ * USLEEP - enable support for devices that don't disconnect. Untested.
+ *
+ * The card is detected and initialized in one of several ways :
+ * 1. Autoprobe (default) - since the board is memory mapped,
+ * a BIOS signature is scanned for to locate the registers.
+ * An interrupt is triggered to autoprobe for the interrupt
+ * line.
+ *
+ * 2. With command line overrides - t128=address,irq may be
+ * used on the LILO command line to override the defaults.
+ *
+ * 3. With the T128_OVERRIDE compile time define. This is
+ * specified as an array of address, irq tuples. Ie, for
+ * one board at the default 0xcc000 address, IRQ5, I could say
+ * -DT128_OVERRIDE={{0xcc000, 5}}
+ *
+ * Note that if the override methods are used, place holders must
+ * be specified for other boards in the system.
+ *
+ * T128/T128F jumper/dipswitch settings (note : on my sample, the switches
+ * were epoxy'd shut, meaning I couldn't change the 0xcc000 base address) :
+ *
+ * T128 Sw7 Sw8 Sw6 = 0ws Sw5 = boot
+ * T128F Sw6 Sw7 Sw5 = 0ws Sw4 = boot Sw8 = floppy disable
+ * cc000 off off
+ * c8000 off on
+ * dc000 on off
+ * d8000 on on
+ *
+ *
+ * Interrupts
+ * There is a 12 pin jumper block, jp1, numbered as follows :
+ * T128 (JP1) T128F (J5)
+ * 2 4 6 8 10 12 11 9 7 5 3 1
+ * 1 3 5 7 9 11 12 10 8 6 4 2
+ *
+ * 3 2-4
+ * 5 1-3
+ * 7 3-5
+ * T128F only
+ * 10 8-10
+ * 12 7-9
+ * 14 10-12
+ * 15 9-11
+ */
+
+/*
+ * $Log: t128.c,v $
+ * Revision 1.1.1.1 1996/10/30 01:40:07 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:52 goel
+ * Linux driver merge.
+ *
+ */
+
+#include <asm/system.h>
+#include <linux/signal.h>
+#include <linux/sched.h>
+#include <asm/io.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "t128.h"
+#define AUTOPROBE_IRQ
+#include "NCR5380.h"
+#include "constants.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_t128 = {
+ PROC_SCSI_T128, 4, "t128",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+static struct override {
+ unsigned char *address;
+ int irq;
+} overrides
+#ifdef T128_OVERRIDE
+ [] = T128_OVERRIDE;
+#else
+ [4] = {{NULL,IRQ_AUTO}, {NULL,IRQ_AUTO}, {NULL,IRQ_AUTO},
+ {NULL,IRQ_AUTO}};
+#endif
+
+#define NO_OVERRIDES (sizeof(overrides) / sizeof(struct override))
+
+static struct base {
+ unsigned char *address;
+ int noauto;
+} bases[] = {{(unsigned char *) 0xcc000, 0}, {(unsigned char *) 0xc8000, 0},
+ {(unsigned char *) 0xdc000, 0}, {(unsigned char *) 0xd8000, 0}};
+
+#define NO_BASES (sizeof (bases) / sizeof (struct base))
+
+static const struct signature {
+ const char *string;
+ int offset;
+} signatures[] = {
+{"TSROM: SCSI BIOS, Version 1.12", 0x36},
+};
+
+#define NO_SIGNATURES (sizeof (signatures) / sizeof (struct signature))
+
+/*
+ * Function : t128_setup(char *str, int *ints)
+ *
+ * Purpose : LILO command line initialization of the overrides array,
+ *
+ * Inputs : str - unused, ints - array of integer parameters with ints[0]
+ * equal to the number of ints.
+ *
+ */
+
+void t128_setup(char *str, int *ints) {
+ static int commandline_current = 0;
+ int i;
+ if (ints[0] != 2)
+ printk("t128_setup : usage t128=address,irq\n");
+ else
+ if (commandline_current < NO_OVERRIDES) {
+ overrides[commandline_current].address = (unsigned char *) ints[1];
+ overrides[commandline_current].irq = ints[2];
+ for (i = 0; i < NO_BASES; ++i)
+ if (bases[i].address == (unsigned char *) ints[1]) {
+ bases[i].noauto = 1;
+ break;
+ }
+ ++commandline_current;
+ }
+}
+
+/*
+ * Function : int t128_detect(Scsi_Host_Template * tpnt)
+ *
+ * Purpose : detects and initializes T128,T128F, or T228 controllers
+ * that were autoprobed, overridden on the LILO command line,
+ * or specified at compile time.
+ *
+ * Inputs : tpnt - template for this SCSI adapter.
+ *
+ * Returns : 1 if a host adapter was found, 0 if not.
+ *
+ */
+
+int t128_detect(Scsi_Host_Template * tpnt) {
+ static int current_override = 0, current_base = 0;
+ struct Scsi_Host *instance;
+ unsigned char *base;
+ int sig, count;
+
+ tpnt->proc_dir = &proc_scsi_t128;
+
+ for (count = 0; current_override < NO_OVERRIDES; ++current_override) {
+ base = NULL;
+
+ if (overrides[current_override].address)
+ base = overrides[current_override].address;
+ else
+ for (; !base && (current_base < NO_BASES); ++current_base) {
+#if (TDEBUG & TDEBUG_INIT)
+ printk("scsi : probing address %08x\n", (unsigned int) bases[current_base].address);
+#endif
+ for (sig = 0; sig < NO_SIGNATURES; ++sig)
+ if (!bases[current_base].noauto && !memcmp
+ (bases[current_base].address + signatures[sig].offset,
+ signatures[sig].string, strlen(signatures[sig].string))) {
+ base = bases[current_base].address;
+#if (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : detected board.\n");
+#endif
+ break;
+ }
+ }
+
+#if defined(TDEBUG) && (TDEBUG & TDEBUG_INIT)
+ printk("scsi-t128 : base = %08x\n", (unsigned int) base);
+#endif
+
+ if (!base)
+ break;
+
+ instance = scsi_register (tpnt, sizeof(struct NCR5380_hostdata));
+ instance->base = base;
+
+ NCR5380_init(instance, 0);
+
+ if (overrides[current_override].irq != IRQ_AUTO)
+ instance->irq = overrides[current_override].irq;
+ else
+ instance->irq = NCR5380_probe_irq(instance, T128_IRQS);
+
+ if (instance->irq != IRQ_NONE)
+ if (request_irq(instance->irq, t128_intr, SA_INTERRUPT, "t128")) {
+ printk("scsi%d : IRQ%d not free, interrupts disabled\n",
+ instance->host_no, instance->irq);
+ instance->irq = IRQ_NONE;
+ }
+
+ if (instance->irq == IRQ_NONE) {
+ printk("scsi%d : interrupts not enabled. for better interactive performance,\n", instance->host_no);
+ printk("scsi%d : please jumper the board for a free IRQ.\n", instance->host_no);
+ }
+
+#if defined(TDEBUG) && (TDEBUG & TDEBUG_INIT)
+ printk("scsi%d : irq = %d\n", instance->host_no, instance->irq);
+#endif
+
+ printk("scsi%d : at 0x%08x", instance->host_no, (int)
+ instance->base);
+ if (instance->irq == IRQ_NONE)
+ printk (" interrupts disabled");
+ else
+ printk (" irq %d", instance->irq);
+ printk(" options CAN_QUEUE=%d CMD_PER_LUN=%d release=%d",
+ CAN_QUEUE, CMD_PER_LUN, T128_PUBLIC_RELEASE);
+ NCR5380_print_options(instance);
+ printk("\n");
+
+ ++current_override;
+ ++count;
+ }
+ return count;
+}
+
+/*
+ * Function : int t128_biosparam(Disk * disk, kdev_t dev, int *ip)
+ *
+ * Purpose : Generates a BIOS / DOS compatible H-C-S mapping for
+ * the specified device / size.
+ *
+ * Inputs : size = size of device in sectors (512 bytes), dev = block device
+ * major / minor, ip[] = {heads, sectors, cylinders}
+ *
+ * Returns : always 0 (success), initializes ip
+ *
+ */
+
+/*
+ * XXX Most SCSI boards use this mapping, I could be incorrect. Some one
+ * using hard disks on a trantor should verify that this mapping corresponds
+ * to that used by the BIOS / ASPI driver by running the linux fdisk program
+ * and matching the H_C_S coordinates to what DOS uses.
+ */
+
+int t128_biosparam(Disk * disk, kdev_t dev, int * ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pread (struct Scsi_Host *instance,
+ * unsigned char *dst, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma read function, transfers len bytes to
+ * dst
+ *
+ * Inputs : dst = destination, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pread (struct Scsi_Host *instance, unsigned char *dst,
+ int len) {
+ register unsigned char *reg = (unsigned char *) (instance->base +
+ T_DATA_REG_OFFSET), *d = dst;
+ register i = len;
+
+
+#if 0
+ for (; i; --i) {
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+#else
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+ for (; i; --i) {
+#endif
+ *d++ = *reg;
+ }
+
+ if (*(instance->base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
+ unsigned char tmp;
+ volatile unsigned char *foo;
+ foo = instance->base + T_CONTROL_REG_OFFSET;
+ tmp = *foo;
+ *foo = tmp | T_CR_CT;
+ *foo = tmp;
+ printk("scsi%d : watchdog timer fired in NCR5380_pread()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+/*
+ * Function : int NCR5380_pwrite (struct Scsi_Host *instance,
+ * unsigned char *src, int len)
+ *
+ * Purpose : Fast 5380 pseudo-dma write function, transfers len bytes from
+ * src
+ *
+ * Inputs : src = source, len = length in bytes
+ *
+ * Returns : 0 on success, non zero on a failure such as a watchdog
+ * timeout.
+ */
+
+static inline int NCR5380_pwrite (struct Scsi_Host *instance, unsigned char *src,
+ int len) {
+ register unsigned char *reg = (unsigned char *) (instance->base +
+ T_DATA_REG_OFFSET), *s = src;
+ register i = len;
+
+#if 0
+ for (; i; --i) {
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+#else
+ while (!(instance->base[T_STATUS_REG_OFFSET]) & T_ST_RDY) barrier();
+ for (; i; --i) {
+#endif
+ *reg = *s++;
+ }
+
+ if (*(instance->base + T_STATUS_REG_OFFSET) & T_ST_TIM) {
+ unsigned char tmp;
+ volatile unsigned char *foo;
+ foo = instance->base + T_CONTROL_REG_OFFSET;
+ tmp = *foo;
+ *foo = tmp | T_CR_CT;
+ *foo = tmp;
+ printk("scsi%d : watchdog timer fired in NCR5380_pwrite()\n",
+ instance->host_no);
+ return -1;
+ } else
+ return 0;
+}
+
+#ifdef MACH
+#include "NCR5380.src"
+#else
+#include "NCR5380.c"
+#endif
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = TRANTOR_T128;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/t128.h b/i386/i386at/gpl/linux/scsi/t128.h
new file mode 100644
index 00000000..8c7cb579
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/t128.h
@@ -0,0 +1,176 @@
+/*
+ * Trantor T128/T128F/T228 defines
+ * Note : architecturally, the T100 and T128 are different and won't work
+ *
+ * Copyright 1993, Drew Eckhardt
+ * Visionary Computing
+ * (Unix and Linux consulting and custom programming)
+ * drew@colorado.edu
+ * +1 (303) 440-4894
+ *
+ * DISTRIBUTION RELEASE 3.
+ *
+ * For more information, please consult
+ *
+ * Trantor Systems, Ltd.
+ * T128/T128F/T228 SCSI Host Adapter
+ * Hardware Specifications
+ *
+ * Trantor Systems, Ltd.
+ * 5415 Randall Place
+ * Fremont, CA 94538
+ * 1+ (415) 770-1400, FAX 1+ (415) 770-9910
+ *
+ * and
+ *
+ * NCR 5380 Family
+ * SCSI Protocol Controller
+ * Databook
+ *
+ * NCR Microelectronics
+ * 1635 Aeroplaza Drive
+ * Colorado Springs, CO 80916
+ * 1+ (719) 578-3400
+ * 1+ (800) 334-5454
+ */
+
+/*
+ * $Log: t128.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:40:07 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1996/03/25 20:25:52 goel
+ * Linux driver merge.
+ *
+ */
+
+#ifndef T128_H
+#define T128_H
+
+#define T128_PUBLIC_RELEASE 3
+
+#define TDEBUG_INIT 0x1
+#define TDEBUG_TRANSFER 0x2
+
+/*
+ * The trantor boards are memory mapped. They use an NCR5380 or
+ * equivalent (my sample board had part second sourced from ZILOG).
+ * NCR's recommended "Pseudo-DMA" architecture is used, where
+ * a PAL drives the DMA signals on the 5380 allowing fast, blind
+ * transfers with proper handshaking.
+ */
+
+/*
+ * Note : a boot switch is provided for the purpose of informing the
+ * firmware to boot or not boot from attached SCSI devices. So, I imagine
+ * there are fewer people who've yanked the ROM like they do on the Seagate
+ * to make bootup faster, and I'll probably use this for autodetection.
+ */
+#define T_ROM_OFFSET 0
+
+/*
+ * Note : my sample board *WAS NOT* populated with the SRAM, so this
+ * can't be used for autodetection without a ROM present.
+ */
+#define T_RAM_OFFSET 0x1800
+
+/*
+ * All of the registers are allocated 32 bytes of address space, except
+ * for the data register (read/write to/from the 5380 in pseudo-DMA mode)
+ */
+#define T_CONTROL_REG_OFFSET 0x1c00 /* rw */
+#define T_CR_INT 0x10 /* Enable interrupts */
+#define T_CR_CT 0x02 /* Reset watchdog timer */
+
+#define T_STATUS_REG_OFFSET 0x1c20 /* ro */
+#define T_ST_BOOT 0x80 /* Boot switch */
+#define T_ST_S3 0x40 /* User settable switches, */
+#define T_ST_S2 0x20 /* read 0 when switch is on, 1 off */
+#define T_ST_S1 0x10
+#define T_ST_PS2 0x08 /* Set for Microchannel 228 */
+#define T_ST_RDY 0x04 /* 5380 DRQ */
+#define T_ST_TIM 0x02 /* indicates 40us watchdog timer fired */
+#define T_ST_ZERO 0x01 /* Always zero */
+
+#define T_5380_OFFSET 0x1d00 /* 8 registers here, see NCR5380.h */
+
+#define T_DATA_REG_OFFSET 0x1e00 /* rw 512 bytes long */
+
+#ifndef ASM
+int t128_abort(Scsi_Cmnd *);
+int t128_biosparam(Disk *, kdev_t, int*);
+int t128_detect(Scsi_Host_Template *);
+int t128_queue_command(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int t128_reset(Scsi_Cmnd *);
+
+#ifndef NULL
+#define NULL 0
+#endif
+
+#ifndef CMD_PER_LUN
+#define CMD_PER_LUN 2
+#endif
+
+#ifndef CAN_QUEUE
+#define CAN_QUEUE 32
+#endif
+
+/*
+ * I hadn't thought of this with the earlier drivers - but to prevent
+ * macro definition conflicts, we shouldn't define all of the internal
+ * macros when this is being used solely for the host stub.
+ */
+
+#if defined(HOSTS_C) || defined(MODULE)
+
+#define TRANTOR_T128 {NULL, NULL, NULL, NULL, \
+ "Trantor T128/T128F/T228", t128_detect, NULL, \
+ NULL, \
+ NULL, t128_queue_command, t128_abort, t128_reset, NULL, \
+ t128_biosparam, \
+ /* can queue */ CAN_QUEUE, /* id */ 7, SG_ALL, \
+ /* cmd per lun */ CMD_PER_LUN , 0, 0, DISABLE_CLUSTERING}
+
+#endif
+
+#ifndef(HOSTS_C)
+
+#define NCR5380_implementation_fields \
+ volatile unsigned char *base
+
+#define NCR5380_local_declare() \
+ volatile unsigned char *base
+
+#define NCR5380_setup(instance) \
+ base = (volatile unsigned char *) (instance)->base
+
+#define T128_address(reg) (base + T_5380_OFFSET + ((reg) * 0x20))
+
+#if !(TDEBUG & TDEBUG_TRANSFER)
+#define NCR5380_read(reg) (*(T128_address(reg)))
+#define NCR5380_write(reg, value) (*(T128_address(reg)) = (value))
+#else
+#define NCR5380_read(reg) \
+ (((unsigned char) printk("scsi%d : read register %d at address %08x\n"\
+ , instance->hostno, (reg), T128_address(reg))), *(T128_address(reg)))
+
+#define NCR5380_write(reg, value) { \
+ printk("scsi%d : write %02x to register %d at address %08x\n", \
+ instance->hostno, (value), (reg), T128_address(reg)); \
+ *(T128_address(reg)) = (value); \
+}
+#endif
+
+#define NCR5380_intr t128_intr
+#define NCR5380_queue_command t128_queue_command
+#define NCR5380_abort t128_abort
+#define NCR5380_reset t128_reset
+
+/* 15 14 12 10 7 5 3
+ 1101 0100 1010 1000 */
+
+#define T128_IRQS 0xc4a8
+
+#endif /* else def HOSTS_C */
+#endif /* ndef ASM */
+#endif /* T128_H */
diff --git a/i386/i386at/gpl/linux/scsi/u14-34f.c b/i386/i386at/gpl/linux/scsi/u14-34f.c
new file mode 100644
index 00000000..ce0e378e
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/u14-34f.c
@@ -0,0 +1,1044 @@
+/*
+ * u14-34f.c - Low-level driver for UltraStor 14F/34F SCSI host adapters.
+ *
+ * 13 Jun 1995 rev. 2.01 for linux 1.2.10
+ * HAVE_OLD_UX4F_FIRMWARE should be defined for U34F boards when
+ * the firmware prom is not the lastest one (28008-006).
+ *
+ * 11 Mar 1995 rev. 2.00 for linux 1.2.0
+ * Fixed a bug which prevented media change detection for removable
+ * disk drives.
+ *
+ * 23 Feb 1995 rev. 1.18 for linux 1.1.94
+ * Added a check for scsi_register returning NULL.
+ *
+ * 11 Feb 1995 rev. 1.17 for linux 1.1.91
+ * U14F qualified to run with 32 sglists.
+ * Now DEBUG_RESET is disabled by default.
+ *
+ * 9 Feb 1995 rev. 1.16 for linux 1.1.90
+ * Use host->wish_block instead of host->block.
+ *
+ * 8 Feb 1995 rev. 1.15 for linux 1.1.89
+ * Cleared target_time_out counter while performing a reset.
+ *
+ * 28 Jan 1995 rev. 1.14 for linux 1.1.86
+ * Added module support.
+ * Log and do a retry when a disk drive returns a target status
+ * different from zero on a recovered error.
+ * Auto detects if U14F boards have an old firmware revision.
+ * Max number of scatter/gather lists set to 16 for all boards
+ * (most installation run fine using 33 sglists, while other
+ * has problems when using more then 16).
+ *
+ * 16 Jan 1995 rev. 1.13 for linux 1.1.81
+ * Display a message if check_region detects a port address
+ * already in use.
+ *
+ * 15 Dec 1994 rev. 1.12 for linux 1.1.74
+ * The host->block flag is set for all the detected ISA boards.
+ *
+ * 30 Nov 1994 rev. 1.11 for linux 1.1.68
+ * Redo i/o on target status CHECK_CONDITION for TYPE_DISK only.
+ * Added optional support for using a single board at a time.
+ *
+ * 14 Nov 1994 rev. 1.10 for linux 1.1.63
+ *
+ * 28 Oct 1994 rev. 1.09 for linux 1.1.58 Final BETA release.
+ * 16 Jul 1994 rev. 1.00 for linux 1.1.29 Initial ALPHA release.
+ *
+ * This driver is a total replacement of the original UltraStor
+ * scsi driver, but it supports ONLY the 14F and 34F boards.
+ * It can be configured in the same kernel in which the original
+ * ultrastor driver is configured to allow the original U24F
+ * support.
+ *
+ * Multiple U14F and/or U34F host adapters are supported.
+ *
+ * Copyright (C) 1994, 1995 Dario Ballabio (dario@milano.europe.dg.com)
+ *
+ * WARNING: if your 14/34F board has an old firmware revision (see below)
+ * you must change "#undef" into "#define" in the following
+ * statement.
+ */
+#undef HAVE_OLD_UX4F_FIRMWARE
+/*
+ * The UltraStor 14F, 24F, and 34F are a family of intelligent, high
+ * performance SCSI-2 host adapters.
+ * Here is the scoop on the various models:
+ *
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VESA Local-Bus Bus Master HA (no WD1003 emulation).
+ *
+ * This code has been tested with up to two U14F boards, using both
+ * firmware 28004-005/38004-004 (BIOS rev. 2.00) and the latest firmware
+ * 28004-006/38004-005 (BIOS rev. 2.01).
+ *
+ * The latest firmware is required in order to get reliable operations when
+ * clustering is enabled. ENABLE_CLUSTERING provides a performance increase
+ * up to 50% on sequential access.
+ *
+ * Since the Scsi_Host_Template structure is shared among all 14F and 34F,
+ * the last setting of use_clustering is in effect for all of these boards.
+ *
+ * Here a sample configuration using two U14F boards:
+ *
+ U14F0: PORT 0x330, BIOS 0xc8000, IRQ 11, DMA 5, SG 32, Mbox 16, CmdLun 2, C1.
+ U14F1: PORT 0x340, BIOS 0x00000, IRQ 10, DMA 6, SG 32, Mbox 16, CmdLun 2, C1.
+ *
+ * The boot controller must have its BIOS enabled, while other boards can
+ * have their BIOS disabled, or enabled to an higher address.
+ * Boards are named Ux4F0, Ux4F1..., according to the port address order in
+ * the io_port[] array.
+ *
+ * The following facts are based on real testing results (not on
+ * documentation) on the above U14F board.
+ *
+ * - The U14F board should be jumpered for bus on time less or equal to 7
+ * microseconds, while the default is 11 microseconds. This is order to
+ * get acceptable performance while using floppy drive and hard disk
+ * together. The jumpering for 7 microseconds is: JP13 pin 15-16,
+ * JP14 pin 7-8 and pin 9-10.
+ * The reduction has a little impact on scsi performance.
+ *
+ * - If scsi bus length exceeds 3m., the scsi bus speed needs to be reduced
+ * from 10Mhz to 5Mhz (do this by inserting a jumper on JP13 pin 7-8).
+ *
+ * - If U14F on board firmware is older than 28004-006/38004-005,
+ * the U14F board is unable to provide reliable operations if the scsi
+ * request length exceeds 16Kbyte. When this length is exceeded the
+ * behavior is:
+ * - adapter_status equal 0x96 or 0xa3 or 0x93 or 0x94;
+ * - adapter_status equal 0 and target_status equal 2 on for all targets
+ * in the next operation following the reset.
+ * This sequence takes a long time (>3 seconds), so in the meantime
+ * the SD_TIMEOUT in sd.c could expire giving rise to scsi aborts
+ * (SD_TIMEOUT has been increased from 3 to 6 seconds in 1.1.31).
+ * Because of this I had to DISABLE_CLUSTERING and to work around the
+ * bus reset in the interrupt service routine, returning DID_BUS_BUSY
+ * so that the operations are retried without complains from the scsi.c
+ * code.
+ * Any reset of the scsi bus is going to kill tape operations, since
+ * no retry is allowed for tapes. Bus resets are more likely when the
+ * scsi bus is under heavy load.
+ * Requests using scatter/gather have a maximum length of 16 x 1024 bytes
+ * when DISABLE_CLUSTERING is in effect, but unscattered requests could be
+ * larger than 16Kbyte.
+ *
+ * The new firmware has fixed all the above problems.
+ *
+ * For U34F boards the latest bios prom is 38008-002 (BIOS rev. 2.01),
+ * the latest firmware prom is 28008-006. Older firmware 28008-005 has
+ * problems when using more then 16 scatter/gather lists.
+ *
+ * In order to support multiple ISA boards in a reliable way,
+ * the driver sets host->wish_block = TRUE for all ISA boards.
+ */
+
+#if defined(MODULE)
+#include <linux/module.h>
+#include <linux/version.h>
+#endif
+
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <asm/io.h>
+#include <asm/system.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+#include <asm/dma.h>
+#include <asm/irq.h>
+#include "u14-34f.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_u14_34f = {
+ PROC_SCSI_U14_34F, 6, "u14_34f",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+/* Values for the PRODUCT_ID ports for the 14/34F */
+#define PRODUCT_ID1 0x56
+#define PRODUCT_ID2 0x40 /* NOTE: Only upper nibble is used */
+
+/* Subversion values */
+#define ISA 0
+#define ESA 1
+
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#undef DEBUG_DETECT
+#undef DEBUG_INTERRUPT
+#undef DEBUG_STATISTICS
+#undef DEBUG_RESET
+
+#define MAX_TARGET 8
+#define MAX_IRQ 16
+#define MAX_BOARDS 4
+#define MAX_MAILBOXES 16
+#define MAX_SGLIST 32
+#define MAX_SAFE_SGLIST 16
+#define MAX_CMD_PER_LUN 2
+
+#define FALSE 0
+#define TRUE 1
+#define FREE 0
+#define IN_USE 1
+#define LOCKED 2
+#define IN_RESET 3
+#define IGNORE 4
+#define NO_IRQ 0xff
+#define NO_DMA 0xff
+#define MAXLOOP 200000
+
+#define REG_LCL_MASK 0
+#define REG_LCL_INTR 1
+#define REG_SYS_MASK 2
+#define REG_SYS_INTR 3
+#define REG_PRODUCT_ID1 4
+#define REG_PRODUCT_ID2 5
+#define REG_CONFIG1 6
+#define REG_CONFIG2 7
+#define REG_OGM 8
+#define REG_ICM 12
+#define REGION_SIZE 13
+#define BSY_ASSERTED 0x01
+#define IRQ_ASSERTED 0x01
+#define CMD_RESET 0xc0
+#define CMD_OGM_INTR 0x01
+#define CMD_CLR_INTR 0x01
+#define CMD_ENA_INTR 0x81
+#define ASOK 0x00
+#define ASST 0x91
+
+#define PACKED __attribute__((packed))
+
+/* MailBox SCSI Command Packet */
+struct mscp {
+ unsigned char opcode: 3; /* type of command */
+ unsigned char xdir: 2; /* data transfer direction */
+ unsigned char dcn: 1; /* disable disconnect */
+ unsigned char ca: 1; /* use cache (if available) */
+ unsigned char sg: 1; /* scatter/gather operation */
+ unsigned char target: 3; /* target SCSI id */
+ unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */
+ unsigned char lun: 3; /* logical unit number */
+ unsigned int data_address PACKED; /* transfer data pointer */
+ unsigned int data_len PACKED; /* length in bytes */
+ unsigned int command_link PACKED; /* for linking command chains */
+ unsigned char scsi_command_link_id; /* identifies command in chain */
+ unsigned char use_sg; /* (if sg is set) 8 bytes per list */
+ unsigned char sense_len;
+ unsigned char scsi_cdbs_len; /* 6, 10, or 12 */
+ unsigned char scsi_cdbs[12]; /* SCSI commands */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ unsigned int sense_addr PACKED;
+
+ Scsi_Cmnd *SCpnt;
+
+ struct sg_list {
+ unsigned int address; /* Segment Address */
+ unsigned int num_bytes; /* Segment Length */
+ } sglist[MAX_SGLIST];
+
+ unsigned int index; /* cp index */
+ };
+
+struct hostdata {
+ struct mscp cp[MAX_MAILBOXES]; /* Mailboxes for this board */
+ unsigned int cp_stat[MAX_MAILBOXES]; /* FREE, IN_USE, LOCKED, IN_RESET */
+ unsigned int last_cp_used; /* Index of last mailbox used */
+ unsigned int iocount; /* Total i/o done for this board */
+ unsigned int multicount; /* Total ... in second ihdlr loop */
+ int board_number; /* Number of this board */
+ char board_name[16]; /* Name of this board */
+ char board_id[256]; /* data from INQUIRY on this board */
+ int in_reset; /* True if board is doing a reset */
+ int target_time_out[MAX_TARGET]; /* N. of timeout errors on target */
+ int target_reset[MAX_TARGET]; /* If TRUE redo operation on target */
+ unsigned char subversion; /* Bus type, either ISA or ESA */
+ unsigned char heads;
+ unsigned char sectors;
+
+ /* slot != 0 for the U24F, slot == 0 for both the U14F and U34F */
+ unsigned char slot;
+ };
+
+static struct Scsi_Host * sh[MAX_BOARDS + 1];
+static const char* driver_name = "Ux4F";
+static unsigned int irqlist[MAX_IRQ], calls[MAX_IRQ];
+
+#define HD(board) ((struct hostdata *) &sh[board]->hostdata)
+#define BN(board) (HD(board)->board_name)
+
+static void u14_34f_interrupt_handler(int, struct pt_regs *);
+static int do_trace = FALSE;
+
+static inline unchar wait_on_busy(ushort iobase) {
+ unsigned int loop = MAXLOOP;
+
+ while (inb(iobase + REG_LCL_INTR) & BSY_ASSERTED)
+ if (--loop == 0) return TRUE;
+
+ return FALSE;
+}
+
+static int board_inquiry(unsigned int j) {
+ struct mscp *cpp;
+ unsigned int time, limit = 0;
+
+ cpp = &HD(j)->cp[0];
+ memset(cpp, 0, sizeof(struct mscp));
+ cpp->opcode = OP_HOST_ADAPTER;
+ cpp->xdir = DTD_IN;
+ cpp->data_address = (unsigned int) HD(j)->board_id;
+ cpp->data_len = sizeof(HD(j)->board_id);
+ cpp->scsi_cdbs_len = 6;
+ cpp->scsi_cdbs[0] = HA_CMD_INQUIRY;
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: board_inquiry, adapter busy.\n", BN(j));
+ return TRUE;
+ }
+
+ HD(j)->cp_stat[0] = IGNORE;
+
+ /* Clear the interrupt indication */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ /* Store pointer in OGM address bytes */
+ outl((unsigned int)cpp, sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ sti();
+ time = jiffies;
+ while (jiffies < (time + 100) && limit++ < 100000000);
+ cli();
+
+ if (cpp->adapter_status || HD(j)->cp_stat[0] != FREE) {
+ HD(j)->cp_stat[0] = FREE;
+ printk("%s: board_inquiry, err 0x%x.\n", BN(j), cpp->adapter_status);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+static inline int port_detect(ushort *port_base, unsigned int j,
+ Scsi_Host_Template * tpnt) {
+ unsigned char irq, dma_channel, subversion;
+ unsigned char in_byte;
+
+ /* Allowed BIOS base addresses (NULL indicates reserved) */
+ void *bios_segment_table[8] = {
+ NULL,
+ (void *) 0xc4000, (void *) 0xc8000, (void *) 0xcc000, (void *) 0xd0000,
+ (void *) 0xd4000, (void *) 0xd8000, (void *) 0xdc000
+ };
+
+ /* Allowed IRQs */
+ unsigned char interrupt_table[4] = { 15, 14, 11, 10 };
+
+ /* Allowed DMA channels for ISA (0 indicates reserved) */
+ unsigned char dma_channel_table[4] = { 5, 6, 7, 0 };
+
+ /* Head/sector mappings */
+ struct {
+ unsigned char heads;
+ unsigned char sectors;
+ } mapping_table[4] = {
+ { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 }
+ };
+
+ struct config_1 {
+ unsigned char bios_segment: 3;
+ unsigned char removable_disks_as_fixed: 1;
+ unsigned char interrupt: 2;
+ unsigned char dma_channel: 2;
+ } config_1;
+
+ struct config_2 {
+ unsigned char ha_scsi_id: 3;
+ unsigned char mapping_mode: 2;
+ unsigned char bios_drive_number: 1;
+ unsigned char tfr_port: 2;
+ } config_2;
+
+ char name[16];
+
+ sprintf(name, "%s%d", driver_name, j);
+
+ if(check_region(*port_base, REGION_SIZE)) {
+ printk("%s: address 0x%03x in use, skipping probe.\n",
+ name, *port_base);
+ return FALSE;
+ }
+
+ if (inb(*port_base + REG_PRODUCT_ID1) != PRODUCT_ID1) return FALSE;
+
+ in_byte = inb(*port_base + REG_PRODUCT_ID2);
+
+ if ((in_byte & 0xf0) != PRODUCT_ID2) return FALSE;
+
+ *(char *)&config_1 = inb(*port_base + REG_CONFIG1);
+ *(char *)&config_2 = inb(*port_base + REG_CONFIG2);
+
+ irq = interrupt_table[config_1.interrupt];
+ dma_channel = dma_channel_table[config_1.dma_channel];
+ subversion = (in_byte & 0x0f);
+
+ /* Board detected, allocate its IRQ if not already done */
+ if ((irq >= MAX_IRQ) || ((irqlist[irq] == NO_IRQ) && request_irq
+ (irq, u14_34f_interrupt_handler, SA_INTERRUPT, driver_name))) {
+ printk("%s: unable to allocate IRQ %u, detaching.\n", name, irq);
+ return FALSE;
+ }
+
+ if (subversion == ISA && request_dma(dma_channel, driver_name)) {
+ printk("%s: unable to allocate DMA channel %u, detaching.\n",
+ name, dma_channel);
+ free_irq(irq);
+ return FALSE;
+ }
+
+ sh[j] = scsi_register(tpnt, sizeof(struct hostdata));
+
+ if (sh[j] == NULL) {
+ printk("%s: unable to register host, detaching.\n", name);
+
+ if (irqlist[irq] == NO_IRQ) free_irq(irq);
+
+ if (subversion == ISA) free_dma(dma_channel);
+
+ return FALSE;
+ }
+
+ sh[j]->io_port = *port_base;
+ sh[j]->n_io_port = REGION_SIZE;
+ sh[j]->base = bios_segment_table[config_1.bios_segment];
+ sh[j]->irq = irq;
+ sh[j]->sg_tablesize = MAX_SGLIST;
+ sh[j]->this_id = config_2.ha_scsi_id;
+ sh[j]->can_queue = MAX_MAILBOXES;
+ sh[j]->cmd_per_lun = MAX_CMD_PER_LUN;
+
+#if defined(DEBUG_DETECT)
+ {
+ unsigned char sys_mask, lcl_mask;
+
+ sys_mask = inb(sh[j]->io_port + REG_SYS_MASK);
+ lcl_mask = inb(sh[j]->io_port + REG_LCL_MASK);
+ printk("SYS_MASK 0x%x, LCL_MASK 0x%x.\n", sys_mask, lcl_mask);
+ }
+#endif
+
+ /* If BIOS is disabled, force enable interrupts */
+ if (sh[j]->base == 0) outb(CMD_ENA_INTR, sh[j]->io_port + REG_SYS_MASK);
+
+ /* Register the I/O space that we use */
+ request_region(sh[j]->io_port, REGION_SIZE, driver_name);
+
+ memset(HD(j), 0, sizeof(struct hostdata));
+ HD(j)->heads = mapping_table[config_2.mapping_mode].heads;
+ HD(j)->sectors = mapping_table[config_2.mapping_mode].sectors;
+ HD(j)->subversion = subversion;
+ HD(j)->board_number = j;
+ irqlist[irq] = j;
+
+ if (HD(j)->subversion == ESA) {
+
+#if defined (HAVE_OLD_UX4F_FIRMWARE)
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+#endif
+
+ sh[j]->dma_channel = NO_DMA;
+ sh[j]->unchecked_isa_dma = FALSE;
+ sprintf(BN(j), "U34F%d", j);
+ }
+ else {
+ sh[j]->wish_block = TRUE;
+
+#if defined (HAVE_OLD_UX4F_FIRMWARE)
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+#endif
+
+ sh[j]->dma_channel = dma_channel;
+ sh[j]->unchecked_isa_dma = TRUE;
+ sprintf(BN(j), "U14F%d", j);
+ disable_dma(dma_channel);
+ clear_dma_ff(dma_channel);
+ set_dma_mode(dma_channel, DMA_MODE_CASCADE);
+ enable_dma(dma_channel);
+ }
+
+ if (HD(j)->subversion == ISA && !board_inquiry(j)) {
+ HD(j)->board_id[40] = 0;
+
+ if (strcmp(&HD(j)->board_id[32], "06000600")) {
+ printk("%s: %s.\n", BN(j), &HD(j)->board_id[8]);
+ printk("%s: firmware %s is outdated, FW PROM should be 28004-006.\n",
+ BN(j), &HD(j)->board_id[32]);
+ sh[j]->hostt->use_clustering = DISABLE_CLUSTERING;
+ sh[j]->sg_tablesize = MAX_SAFE_SGLIST;
+ }
+ }
+
+ printk("%s: PORT 0x%03x, BIOS 0x%05x, IRQ %u, DMA %u, SG %d, "\
+ "Mbox %d, CmdLun %d, C%d.\n", BN(j), sh[j]->io_port,
+ (int)sh[j]->base, sh[j]->irq,
+ sh[j]->dma_channel, sh[j]->sg_tablesize,
+ sh[j]->can_queue, sh[j]->cmd_per_lun,
+ sh[j]->hostt->use_clustering);
+ return TRUE;
+}
+
+int u14_34f_detect (Scsi_Host_Template * tpnt) {
+ unsigned int j = 0, k, flags;
+
+ ushort io_port[] = {
+ 0x330, 0x340, 0x230, 0x240, 0x210, 0x130, 0x140, 0x0
+ };
+
+ ushort *port_base = io_port;
+
+ tpnt->proc_dir = &proc_scsi_u14_34f;
+
+ save_flags(flags);
+ cli();
+
+ for (k = 0; k < MAX_IRQ; k++) {
+ irqlist[k] = NO_IRQ;
+ calls[k] = 0;
+ }
+
+ for (k = 0; k < MAX_BOARDS + 1; k++) sh[k] = NULL;
+
+ while (*port_base) {
+
+ if (j < MAX_BOARDS && port_detect(port_base, j, tpnt)) j++;
+
+ port_base++;
+ }
+
+ if (j > 0)
+ printk("UltraStor 14F/34F: Copyright (C) 1994, 1995 Dario Ballabio.\n");
+
+ restore_flags(flags);
+ return j;
+}
+
+static inline void build_sg_list(struct mscp *cpp, Scsi_Cmnd *SCpnt) {
+ unsigned int k, data_len = 0;
+ struct scatterlist * sgpnt;
+
+ sgpnt = (struct scatterlist *) SCpnt->request_buffer;
+
+ for (k = 0; k < SCpnt->use_sg; k++) {
+ cpp->sglist[k].address = (unsigned int) sgpnt[k].address;
+ cpp->sglist[k].num_bytes = sgpnt[k].length;
+ data_len += sgpnt[k].length;
+ }
+
+ cpp->use_sg = SCpnt->use_sg;
+ cpp->data_address = (unsigned int) cpp->sglist;
+ cpp->data_len = data_len;
+}
+
+int u14_34f_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *)) {
+ unsigned int i, j, k, flags;
+ struct mscp *cpp;
+
+ save_flags(flags);
+ cli();
+ /* j is the board number */
+ j = ((struct hostdata *) SCpnt->host->hostdata)->board_number;
+
+ if (!done) panic("%s: qcomm, pid %ld, null done.\n", BN(j), SCpnt->pid);
+
+ /* i is the mailbox number, look for the first free mailbox
+ starting from last_cp_used */
+ i = HD(j)->last_cp_used + 1;
+
+ for (k = 0; k < sh[j]->can_queue; k++, i++) {
+
+ if (i >= sh[j]->can_queue) i = 0;
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ HD(j)->last_cp_used = i;
+ break;
+ }
+ }
+
+ if (k == sh[j]->can_queue) {
+ printk("%s: qcomm, no free mailbox, resetting.\n", BN(j));
+
+ if (HD(j)->in_reset)
+ printk("%s: qcomm, already in reset.\n", BN(j));
+ else if (u14_34f_reset(SCpnt) == SCSI_RESET_SUCCESS)
+ panic("%s: qcomm, SCSI_RESET_SUCCESS.\n", BN(j));
+
+ SCpnt->result = DID_BUS_BUSY << 16;
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, pid %ld, DID_BUS_BUSY, done.\n", BN(j), SCpnt->pid);
+ restore_flags(flags);
+ done(SCpnt);
+ return 0;
+ }
+
+ /* Set pointer to control packet structure */
+ cpp = &HD(j)->cp[i];
+
+ memset(cpp, 0, sizeof(struct mscp));
+ SCpnt->scsi_done = done;
+ cpp->index = i;
+ SCpnt->host_scribble = (unsigned char *) &cpp->index;
+
+ if (do_trace) printk("%s: qcomm, mbox %d, target %d, pid %ld.\n",
+ BN(j), i, SCpnt->target, SCpnt->pid);
+
+ cpp->opcode = OP_SCSI;
+ cpp->xdir = DTD_SCSI;
+ cpp->target = SCpnt->target;
+ cpp->lun = SCpnt->lun;
+ cpp->SCpnt = SCpnt;
+ cpp->sense_addr = (unsigned int) SCpnt->sense_buffer;
+ cpp->sense_len = sizeof SCpnt->sense_buffer;
+
+ if (SCpnt->use_sg) {
+ cpp->sg = TRUE;
+ build_sg_list(cpp, SCpnt);
+ }
+ else {
+ cpp->data_address = (unsigned int)SCpnt->request_buffer;
+ cpp->data_len = SCpnt->request_bufflen;
+ }
+
+ cpp->scsi_cdbs_len = SCpnt->cmd_len;
+ memcpy(cpp->scsi_cdbs, SCpnt->cmnd, cpp->scsi_cdbs_len);
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ SCpnt->result = DID_ERROR << 16;
+ SCpnt->host_scribble = NULL;
+ printk("%s: qcomm, target %d, pid %ld, adapter busy, DID_ERROR, done.\n",
+ BN(j), SCpnt->target, SCpnt->pid);
+ restore_flags(flags);
+ done(SCpnt);
+ return 0;
+ }
+
+ /* Store pointer in OGM address bytes */
+ outl((unsigned int)cpp, sh[j]->io_port + REG_OGM);
+
+ /* Issue OGM interrupt */
+ outb(CMD_OGM_INTR, sh[j]->io_port + REG_LCL_INTR);
+
+ HD(j)->cp_stat[i] = IN_USE;
+ restore_flags(flags);
+ return 0;
+}
+
+int u14_34f_abort(Scsi_Cmnd *SCarg) {
+ unsigned int i, j, flags;
+
+ save_flags(flags);
+ cli();
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+
+ if (SCarg->host_scribble == NULL) {
+ printk("%s: abort, target %d, pid %ld inactive.\n",
+ BN(j), SCarg->target, SCarg->pid);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ i = *(unsigned int *)SCarg->host_scribble;
+ printk("%s: abort, mbox %d, target %d, pid %ld.\n",
+ BN(j), i, SCarg->target, SCarg->pid);
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: abort, invalid SCarg->host_scribble.\n", BN(j));
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: abort, timeout error.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: abort, mbox %d is free.\n", BN(j), i);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_USE) {
+ printk("%s: abort, mbox %d is in use.\n", BN(j), i);
+
+ if (SCarg != HD(j)->cp[i].SCpnt)
+ panic("%s: abort, mbox %d, SCarg %p, cp SCpnt %p.\n",
+ BN(j), i, SCarg, HD(j)->cp[i].SCpnt);
+
+ restore_flags(flags);
+ return SCSI_ABORT_SNOOZE;
+ }
+
+ if (HD(j)->cp_stat[i] == IN_RESET) {
+ printk("%s: abort, mbox %d is in reset.\n", BN(j), i);
+ restore_flags(flags);
+ return SCSI_ABORT_ERROR;
+ }
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ printk("%s: abort, mbox %d is locked.\n", BN(j), i);
+ restore_flags(flags);
+ return SCSI_ABORT_NOT_RUNNING;
+ }
+ restore_flags(flags);
+ panic("%s: abort, mbox %d, invalid cp_stat.\n", BN(j), i);
+}
+
+int u14_34f_reset(Scsi_Cmnd * SCarg) {
+ unsigned int i, j, flags, time, k, limit = 0;
+ int arg_done = FALSE;
+ Scsi_Cmnd *SCpnt;
+
+ save_flags(flags);
+ cli();
+ j = ((struct hostdata *) SCarg->host->hostdata)->board_number;
+ printk("%s: reset, enter, target %d, pid %ld.\n",
+ BN(j), SCarg->target, SCarg->pid);
+
+ if (SCarg->host_scribble == NULL)
+ printk("%s: reset, pid %ld inactive.\n", BN(j), SCarg->pid);
+
+ if (HD(j)->in_reset) {
+ printk("%s: reset, exit, already in reset.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_RESET_ERROR;
+ }
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: reset, exit, timeout error.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_RESET_ERROR;
+ }
+
+ for (k = 0; k < MAX_TARGET; k++) HD(j)->target_reset[k] = TRUE;
+
+ for (k = 0; k < MAX_TARGET; k++) HD(j)->target_time_out[k] = 0;
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ if (HD(j)->cp_stat[i] == FREE) continue;
+
+ if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: reset, locked mbox %d forced free.\n", BN(j), i);
+ continue;
+ }
+
+ SCpnt = HD(j)->cp[i].SCpnt;
+ HD(j)->cp_stat[i] = IN_RESET;
+ printk("%s: reset, mbox %d in reset, pid %ld.\n",
+ BN(j), i, SCpnt->pid);
+
+ if (SCpnt == NULL)
+ panic("%s: reset, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: reset, mbox %d, garbled SCpnt.\n", BN(j), i);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: reset, mbox %d, index mismatch.\n", BN(j), i);
+
+ if (SCpnt->scsi_done == NULL)
+ panic("%s: reset, mbox %d, SCpnt->scsi_done == NULL.\n", BN(j), i);
+
+ if (SCpnt == SCarg) arg_done = TRUE;
+ }
+
+ if (wait_on_busy(sh[j]->io_port)) {
+ printk("%s: reset, cannot reset, timeout error.\n", BN(j));
+ restore_flags(flags);
+ return SCSI_RESET_ERROR;
+ }
+
+ outb(CMD_RESET, sh[j]->io_port + REG_LCL_INTR);
+ printk("%s: reset, board reset done, enabling interrupts.\n", BN(j));
+
+#if defined (DEBUG_RESET)
+ do_trace = TRUE;
+#endif
+
+ HD(j)->in_reset = TRUE;
+ sti();
+ time = jiffies;
+ while (jiffies < (time + 100) && limit++ < 100000000);
+ cli();
+ printk("%s: reset, interrupts disabled, loops %d.\n", BN(j), limit);
+
+ for (i = 0; i < sh[j]->can_queue; i++) {
+
+ /* Skip mailboxes already set free by interrupt */
+ if (HD(j)->cp_stat[i] != IN_RESET) continue;
+
+ SCpnt = HD(j)->cp[i].SCpnt;
+ SCpnt->result = DID_RESET << 16;
+ SCpnt->host_scribble = NULL;
+
+ /* This mailbox is still waiting for its interrupt */
+ HD(j)->cp_stat[i] = LOCKED;
+
+ printk("%s, reset, mbox %d locked, DID_RESET, pid %ld done.\n",
+ BN(j), i, SCpnt->pid);
+ restore_flags(flags);
+ SCpnt->scsi_done(SCpnt);
+ cli();
+ }
+
+ HD(j)->in_reset = FALSE;
+ do_trace = FALSE;
+ restore_flags(flags);
+
+ if (arg_done) {
+ printk("%s: reset, exit, success.\n", BN(j));
+ return SCSI_RESET_SUCCESS;
+ }
+ else {
+ printk("%s: reset, exit, wakeup.\n", BN(j));
+ return SCSI_RESET_PUNT;
+ }
+}
+
+int u14_34f_biosparam(Disk * disk, kdev_t dev, int * dkinfo) {
+ unsigned int j = 0;
+ int size = disk->capacity;
+
+ dkinfo[0] = HD(j)->heads;
+ dkinfo[1] = HD(j)->sectors;
+ dkinfo[2] = size / (HD(j)->heads * HD(j)->sectors);
+ return 0;
+}
+
+static void u14_34f_interrupt_handler(int irq, struct pt_regs * regs) {
+ Scsi_Cmnd *SCpnt;
+ unsigned int i, j, k, flags, status, tstatus, loops, total_loops = 0;
+ struct mscp *spp;
+
+ save_flags(flags);
+ cli();
+
+ if (irqlist[irq] == NO_IRQ) {
+ printk("%s, ihdlr, irq %d, unexpected interrupt.\n", driver_name, irq);
+ restore_flags(flags);
+ return;
+ }
+
+ if (do_trace) printk("%s: ihdlr, enter, irq %d, calls %d.\n",
+ driver_name, irq, calls[irq]);
+
+ /* Service all the boards configured on this irq */
+ for (j = 0; sh[j] != NULL; j++) {
+
+ if (sh[j]->irq != irq) continue;
+
+ loops = 0;
+
+ /* Loop until all interrupts for a board are serviced */
+ while (inb(sh[j]->io_port + REG_SYS_INTR) & IRQ_ASSERTED) {
+ total_loops++;
+ loops++;
+
+ if (do_trace) printk("%s: ihdlr, start service, count %d.\n",
+ BN(j), HD(j)->iocount);
+
+ spp = (struct mscp *)inl(sh[j]->io_port + REG_ICM);
+
+ /* Clear interrupt pending flag */
+ outb(CMD_CLR_INTR, sh[j]->io_port + REG_SYS_INTR);
+
+ i = spp - HD(j)->cp;
+
+ if (i >= sh[j]->can_queue)
+ panic("%s: ihdlr, invalid mscp address.\n", BN(j));
+
+ if (HD(j)->cp_stat[i] == IGNORE) {
+ HD(j)->cp_stat[i] = FREE;
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == LOCKED) {
+ HD(j)->cp_stat[i] = FREE;
+ printk("%s: ihdlr, mbox %d unlocked, count %d.\n",
+ BN(j), i, HD(j)->iocount);
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == FREE) {
+ printk("%s: ihdlr, mbox %d is free, count %d.\n",
+ BN(j), i, HD(j)->iocount);
+ continue;
+ }
+ else if (HD(j)->cp_stat[i] == IN_RESET)
+ printk("%s: ihdlr, mbox %d is in reset.\n", BN(j), i);
+ else if (HD(j)->cp_stat[i] != IN_USE)
+ panic("%s: ihdlr, mbox %d, invalid cp_stat.\n", BN(j), i);
+
+ HD(j)->cp_stat[i] = FREE;
+ SCpnt = spp->SCpnt;
+
+ if (SCpnt == NULL)
+ panic("%s: ihdlr, mbox %d, SCpnt == NULL.\n", BN(j), i);
+
+ if (SCpnt->host_scribble == NULL)
+ panic("%s: ihdlr, mbox %d, pid %ld, SCpnt %p garbled.\n",
+ BN(j), i, SCpnt->pid, SCpnt);
+
+ if (*(unsigned int *)SCpnt->host_scribble != i)
+ panic("%s: ihdlr, mbox %d, pid %ld, index mismatch %d,"\
+ " irq %d.\n", BN(j), i, SCpnt->pid,
+ *(unsigned int *)SCpnt->host_scribble, irq);
+
+ tstatus = status_byte(spp->target_status);
+
+ switch (spp->adapter_status) {
+ case ASOK: /* status OK */
+
+ /* Forces a reset if a disk drive keeps returning BUSY */
+ if (tstatus == BUSY && SCpnt->device->type != TYPE_TAPE)
+ status = DID_ERROR << 16;
+
+ /* If there was a bus reset, redo operation on each target */
+ else if (tstatus != GOOD
+ && SCpnt->device->type == TYPE_DISK
+ && HD(j)->target_reset[SCpnt->target])
+ status = DID_BUS_BUSY << 16;
+
+ /* Works around a flaw in scsi.c */
+ else if (tstatus == CHECK_CONDITION
+ && SCpnt->device->type == TYPE_DISK
+ && (SCpnt->sense_buffer[2] & 0xf) == RECOVERED_ERROR)
+ status = DID_BUS_BUSY << 16;
+
+ else
+ status = DID_OK << 16;
+
+ if (tstatus == GOOD)
+ HD(j)->target_reset[SCpnt->target] = FALSE;
+
+ if (spp->target_status && SCpnt->device->type == TYPE_DISK)
+ printk("%s: ihdlr, target %d:%d, pid %ld, target_status "\
+ "0x%x, sense key 0x%x.\n", BN(j),
+ SCpnt->target, SCpnt->lun, SCpnt->pid,
+ spp->target_status, SCpnt->sense_buffer[2]);
+
+ HD(j)->target_time_out[SCpnt->target] = 0;
+
+ break;
+ case ASST: /* Selection Time Out */
+
+ if (HD(j)->target_time_out[SCpnt->target] > 1)
+ status = DID_ERROR << 16;
+ else {
+ status = DID_TIME_OUT << 16;
+ HD(j)->target_time_out[SCpnt->target]++;
+ }
+
+ break;
+ case 0x92: /* Data over/under-run */
+ case 0x93: /* Unexpected bus free */
+ case 0x94: /* Target bus phase sequence failure */
+ case 0x96: /* Illegal SCSI command */
+ case 0xa3: /* SCSI bus reset error */
+
+ if (SCpnt->device->type != TYPE_TAPE)
+ status = DID_BUS_BUSY << 16;
+ else
+ status = DID_ERROR << 16;
+
+ for (k = 0; k < MAX_TARGET; k++)
+ HD(j)->target_reset[k] = TRUE;
+
+ break;
+ case 0x01: /* Invalid command */
+ case 0x02: /* Invalid parameters */
+ case 0x03: /* Invalid data list */
+ case 0x84: /* SCSI bus abort error */
+ case 0x9b: /* Auto request sense error */
+ case 0x9f: /* Unexpected command complete message error */
+ case 0xff: /* Invalid parameter in the S/G list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ }
+
+ SCpnt->result = status | spp->target_status;
+ HD(j)->iocount++;
+
+ if (loops > 1) HD(j)->multicount++;
+
+#if defined (DEBUG_INTERRUPT)
+ if (SCpnt->result || do_trace)
+#else
+ if ((spp->adapter_status != ASOK && HD(j)->iocount > 1000) ||
+ (spp->adapter_status != ASOK &&
+ spp->adapter_status != ASST && HD(j)->iocount <= 1000) ||
+ do_trace)
+#endif
+ printk("%s: ihdlr, mbox %d, err 0x%x:%x,"\
+ " target %d:%d, pid %ld, count %d.\n",
+ BN(j), i, spp->adapter_status, spp->target_status,
+ SCpnt->target, SCpnt->lun, SCpnt->pid, HD(j)->iocount);
+
+ /* Set the command state to inactive */
+ SCpnt->host_scribble = NULL;
+
+ restore_flags(flags);
+ SCpnt->scsi_done(SCpnt);
+ cli();
+
+ } /* Multiple command loop */
+
+ } /* Boards loop */
+
+ calls[irq]++;
+
+ if (total_loops == 0)
+ printk("%s: ihdlr, irq %d, no command completed, calls %d.\n",
+ driver_name, irq, calls[irq]);
+
+ if (do_trace) printk("%s: ihdlr, exit, irq %d, calls %d.\n",
+ driver_name, irq, calls[irq]);
+
+#if defined (DEBUG_STATISTICS)
+ if ((calls[irq] % 100000) == 10000)
+ for (j = 0; sh[j] != NULL; j++)
+ printk("%s: ihdlr, calls %d, count %d, multi %d.\n", BN(j),
+ calls[(sh[j]->irq)], HD(j)->iocount, HD(j)->multicount);
+#endif
+
+ restore_flags(flags);
+ return;
+}
+
+#if defined(MODULE)
+Scsi_Host_Template driver_template = ULTRASTOR_14_34F;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/u14-34f.h b/i386/i386at/gpl/linux/scsi/u14-34f.h
new file mode 100644
index 00000000..2988824e
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/u14-34f.h
@@ -0,0 +1,38 @@
+/*
+ * u14-34f.h - used by the low-level driver for UltraStor 14F/34F
+ */
+#ifndef _U14_34F_H
+#define _U14_34F_H
+
+int u14_34f_detect(Scsi_Host_Template *);
+int u14_34f_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int u14_34f_abort(Scsi_Cmnd *);
+int u14_34f_reset(Scsi_Cmnd *);
+int u14_34f_biosparam(Disk *, kdev_t, int *);
+
+#define U14_34F_VERSION "2.01.00"
+
+#define ULTRASTOR_14_34F { \
+ NULL, /* Ptr for modules */ \
+ NULL, /* usage count for modules */ \
+ NULL, \
+ NULL, \
+ "UltraStor 14F/34F rev. " U14_34F_VERSION " ", \
+ u14_34f_detect, \
+ NULL, /* Release */ \
+ NULL, \
+ NULL, \
+ u14_34f_queuecommand, \
+ u14_34f_abort, \
+ u14_34f_reset, \
+ NULL, \
+ u14_34f_biosparam, \
+ 0, /* can_queue, reset by detect */ \
+ 7, /* this_id, reset by detect */ \
+ 0, /* sg_tablesize, reset by detect */ \
+ 0, /* cmd_per_lun, reset by detect */ \
+ 0, /* number of boards present */ \
+ 1, /* unchecked isa dma, reset by detect */ \
+ ENABLE_CLUSTERING \
+ }
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/ultrastor.c b/i386/i386at/gpl/linux/scsi/ultrastor.c
new file mode 100644
index 00000000..23e94a91
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/ultrastor.c
@@ -0,0 +1,1160 @@
+/*
+ * ultrastor.c Copyright (C) 1992 David B. Gentzel
+ * Low-level SCSI driver for UltraStor 14F, 24F, and 34F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
+ * 24F and multiple command support by John F. Carr (jfc@athena.mit.edu)
+ * John's work modified by Caleb Epstein (cae@jpmorgan.com) and
+ * Eric Youngdale (ericy@cais.com).
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+/*
+ * TODO:
+ * 1. Find out why scatter/gather is limited to 16 requests per command.
+ * This is fixed, at least on the 24F, as of version 1.12 - CAE.
+ * 2. Look at command linking (mscp.command_link and
+ * mscp.command_link_id). (Does not work with many disks,
+ * and no performance increase. ERY).
+ * 3. Allow multiple adapters.
+ */
+
+/*
+ * NOTES:
+ * The UltraStor 14F, 24F, and 34F are a family of intelligent, high
+ * performance SCSI-2 host adapters. They all support command queueing
+ * and scatter/gather I/O. Some of them can also emulate the standard
+ * WD1003 interface for use with OS's which don't support SCSI. Here
+ * is the scoop on the various models:
+ * 14F - ISA first-party DMA HA with floppy support and WD1003 emulation.
+ * 14N - ISA HA with floppy support. I think that this is a non-DMA
+ * HA. Nothing further known.
+ * 24F - EISA Bus Master HA with floppy support and WD1003 emulation.
+ * 34F - VL-Bus Bus Master HA with floppy support (no WD1003 emulation).
+ *
+ * The 14F, 24F, and 34F are supported by this driver.
+ *
+ * Places flagged with a triple question-mark are things which are either
+ * unfinished, questionable, or wrong.
+ */
+
+/* Changes from version 1.11 alpha to 1.12
+ *
+ * Increased the size of the scatter-gather list to 33 entries for
+ * the 24F adapter (it was 16). I don't have the specs for the 14F
+ * or the 34F, so they may support larger s-g lists as well.
+ *
+ * Caleb Epstein <cae@jpmorgan.com>
+ */
+
+/* Changes from version 1.9 to 1.11
+ *
+ * Patches to bring this driver up to speed with the default kernel
+ * driver which supports only the 14F and 34F adapters. This version
+ * should compile cleanly into 0.99.13, 0.99.12 and probably 0.99.11.
+ *
+ * Fixes from Eric Youngdale to fix a few possible race conditions and
+ * several problems with bit testing operations (insufficient
+ * parentheses).
+ *
+ * Removed the ultrastor_abort() and ultrastor_reset() functions
+ * (enclosed them in #if 0 / #endif). These functions, at least on
+ * the 24F, cause the SCSI bus to do odd things and generally lead to
+ * kernel panics and machine hangs. This is like the Adaptec code.
+ *
+ * Use check/snarf_region for 14f, 34f to avoid I/O space address conflicts.
+ */
+
+/* Changes from version 1.8 to version 1.9
+ *
+ * 0.99.11 patches (cae@jpmorgan.com) */
+
+/* Changes from version 1.7 to version 1.8
+ *
+ * Better error reporting.
+ */
+
+/* Changes from version 1.6 to version 1.7
+ *
+ * Removed CSIR command code.
+ *
+ * Better race condition avoidance (xchgb function added).
+ *
+ * Set ICM and OGM status to zero at probe (24F)
+ *
+ * reset sends soft reset to UltraStor adapter
+ *
+ * reset adapter if adapter interrupts with an invalid MSCP address
+ *
+ * handle aborted command interrupt (24F)
+ *
+ */
+
+/* Changes from version 1.5 to version 1.6:
+ *
+ * Read MSCP address from ICM _before_ clearing the interrupt flag.
+ * This fixes a race condition.
+ */
+
+/* Changes from version 1.4 to version 1.5:
+ *
+ * Abort now calls done when multiple commands are enabled.
+ *
+ * Clear busy when aborted command finishes, not when abort is called.
+ *
+ * More debugging messages for aborts.
+ */
+
+/* Changes from version 1.3 to version 1.4:
+ *
+ * Enable automatic request of sense data on error (requires newer version
+ * of scsi.c to be useful).
+ *
+ * Fix PORT_OVERRIDE for 14F.
+ *
+ * Fix abort and reset to work properly (config.aborted wasn't cleared
+ * after it was tested, so after a command abort no further commands would
+ * work).
+ *
+ * Boot time test to enable SCSI bus reset (defaults to not allowing reset).
+ *
+ * Fix test for OGM busy -- the busy bit is in different places on the 24F.
+ *
+ * Release ICM slot by clearing first byte on 24F.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <linux/stddef.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/kernel.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <asm/io.h>
+#include <asm/bitops.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+
+#define ULTRASTOR_PRIVATE /* Get the private stuff from ultrastor.h */
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "ultrastor.h"
+#include "sd.h"
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_ultrastor = {
+ PROC_SCSI_ULTRASTOR, 9, "ultrastor",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+#define FALSE 0
+#define TRUE 1
+
+#ifndef ULTRASTOR_DEBUG
+#define ULTRASTOR_DEBUG (UD_ABORT|UD_CSIR|UD_RESET)
+#endif
+
+#define VERSION "1.12"
+
+#define ARRAY_SIZE(arr) (sizeof (arr) / sizeof (arr)[0])
+
+#define PACKED __attribute__((packed))
+#define ALIGNED(x) __attribute__((aligned(x)))
+
+
+/* The 14F uses an array of 4-byte ints for its scatter/gather list.
+ The data can be unaligned, but need not be. It's easier to give
+ the list normal alignment since it doesn't need to fit into a
+ packed structure. */
+
+typedef struct {
+ unsigned int address;
+ unsigned int num_bytes;
+} ultrastor_sg_list;
+
+
+/* MailBox SCSI Command Packet. Basic command structure for communicating
+ with controller. */
+struct mscp {
+ unsigned char opcode: 3; /* type of command */
+ unsigned char xdir: 2; /* data transfer direction */
+ unsigned char dcn: 1; /* disable disconnect */
+ unsigned char ca: 1; /* use cache (if available) */
+ unsigned char sg: 1; /* scatter/gather operation */
+ unsigned char target_id: 3; /* target SCSI id */
+ unsigned char ch_no: 2; /* SCSI channel (always 0 for 14f) */
+ unsigned char lun: 3; /* logical unit number */
+ unsigned int transfer_data PACKED; /* transfer data pointer */
+ unsigned int transfer_data_length PACKED; /* length in bytes */
+ unsigned int command_link PACKED; /* for linking command chains */
+ unsigned char scsi_command_link_id; /* identifies command in chain */
+ unsigned char number_of_sg_list; /* (if sg is set) 8 bytes per list */
+ unsigned char length_of_sense_byte;
+ unsigned char length_of_scsi_cdbs; /* 6, 10, or 12 */
+ unsigned char scsi_cdbs[12]; /* SCSI commands */
+ unsigned char adapter_status; /* non-zero indicates HA error */
+ unsigned char target_status; /* non-zero indicates target error */
+ unsigned int sense_data PACKED;
+ /* The following fields are for software only. They are included in
+ the MSCP structure because they are associated with SCSI requests. */
+ void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCint;
+ ultrastor_sg_list sglist[ULTRASTOR_24F_MAX_SG]; /* use larger size for 24F */
+};
+
+
+/* Port addresses (relative to the base address) */
+#define U14F_PRODUCT_ID(port) ((port) + 0x4)
+#define CONFIG(port) ((port) + 0x6)
+
+/* Port addresses relative to the doorbell base address. */
+#define LCL_DOORBELL_MASK(port) ((port) + 0x0)
+#define LCL_DOORBELL_INTR(port) ((port) + 0x1)
+#define SYS_DOORBELL_MASK(port) ((port) + 0x2)
+#define SYS_DOORBELL_INTR(port) ((port) + 0x3)
+
+
+/* Used to store configuration info read from config i/o registers. Most of
+ this is not used yet, but might as well save it.
+
+ This structure also holds port addresses that are not at the same offset
+ on the 14F and 24F.
+
+ This structure holds all data that must be duplicated to support multiple
+ adapters. */
+
+static struct ultrastor_config
+{
+ unsigned short port_address; /* base address of card */
+ unsigned short doorbell_address; /* base address of doorbell CSRs */
+ unsigned short ogm_address; /* base address of OGM */
+ unsigned short icm_address; /* base address of ICM */
+ const void *bios_segment;
+ unsigned char interrupt: 4;
+ unsigned char dma_channel: 3;
+ unsigned char bios_drive_number: 1;
+ unsigned char heads;
+ unsigned char sectors;
+ unsigned char ha_scsi_id: 3;
+ unsigned char subversion: 4;
+ unsigned char revision;
+ /* The slot number is used to distinguish the 24F (slot != 0) from
+ the 14F and 34F (slot == 0). */
+ unsigned char slot;
+
+#ifdef PRINT_U24F_VERSION
+ volatile int csir_done;
+#endif
+
+ /* A pool of MSCP structures for this adapter, and a bitmask of
+ busy structures. (If ULTRASTOR_14F_MAX_CMDS == 1, a 1 byte
+ busy flag is used instead.) */
+
+#if ULTRASTOR_MAX_CMDS == 1
+ unsigned char mscp_busy;
+#else
+ unsigned short mscp_free;
+#endif
+ volatile unsigned char aborted[ULTRASTOR_MAX_CMDS];
+ struct mscp mscp[ULTRASTOR_MAX_CMDS];
+} config = {0};
+
+/* Set this to 1 to reset the SCSI bus on error. */
+int ultrastor_bus_reset = 0;
+
+
+/* Allowed BIOS base addresses (NULL indicates reserved) */
+static const void *const bios_segment_table[8] = {
+ NULL, (void *)0xC4000, (void *)0xC8000, (void *)0xCC000,
+ (void *)0xD0000, (void *)0xD4000, (void *)0xD8000, (void *)0xDC000,
+};
+
+/* Allowed IRQs for 14f */
+static const unsigned char interrupt_table_14f[4] = { 15, 14, 11, 10 };
+
+/* Allowed DMA channels for 14f (0 indicates reserved) */
+static const unsigned char dma_channel_table_14f[4] = { 5, 6, 7, 0 };
+
+/* Head/sector mappings allowed by 14f */
+static const struct {
+ unsigned char heads;
+ unsigned char sectors;
+} mapping_table[4] = { { 16, 63 }, { 64, 32 }, { 64, 63 }, { 64, 32 } };
+
+#ifndef PORT_OVERRIDE
+/* ??? A probe of address 0x310 screws up NE2000 cards */
+static const unsigned short ultrastor_ports_14f[] = {
+ 0x330, 0x340, /*0x310,*/ 0x230, 0x240, 0x210, 0x130, 0x140,
+};
+#endif
+
+static void ultrastor_interrupt(int, struct pt_regs *);
+static inline void build_sg_list(struct mscp *, Scsi_Cmnd *SCpnt);
+
+
+static inline int find_and_clear_bit_16(unsigned short *field)
+{
+ int rv;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+ if (*field == 0) panic("No free mscp");
+ asm("xorl %0,%0\n0:\tbsfw %1,%w0\n\tbtr %0,%1\n\tjnc 0b"
+ : "=&r" (rv), "=m" (*field) : "1" (*field));
+ restore_flags(flags);
+ return rv;
+}
+
+/* This has been re-implemented with the help of Richard Earnshaw,
+ <rwe@pegasus.esprit.ec.org> and works with gcc-2.5.8 and gcc-2.6.0.
+ The instability noted by jfc below appears to be a bug in
+ gcc-2.5.x when compiling w/o optimization. --Caleb
+
+ This asm is fragile: it doesn't work without the casts and it may
+ not work without optimization. Maybe I should add a swap builtin
+ to gcc. --jfc */
+static inline unsigned char xchgb(unsigned char reg,
+ volatile unsigned char *mem)
+{
+ __asm__ ("xchgb %0,%1" : "=q" (reg), "=m" (*mem) : "0" (reg));
+ return reg;
+}
+
+#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)
+
+static void log_ultrastor_abort(register struct ultrastor_config *config,
+ int command)
+{
+ static char fmt[80] = "abort %d (%x); MSCP free pool: %x;";
+ register int i;
+ int flags;
+ save_flags(flags);
+ cli();
+
+ for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)
+ {
+ fmt[20 + i*2] = ' ';
+ if (! (config->mscp_free & (1 << i)))
+ fmt[21 + i*2] = '0' + config->mscp[i].target_id;
+ else
+ fmt[21 + i*2] = '-';
+ }
+ fmt[20 + ULTRASTOR_MAX_CMDS * 2] = '\n';
+ fmt[21 + ULTRASTOR_MAX_CMDS * 2] = 0;
+ printk(fmt, command, &config->mscp[command], config->mscp_free);
+ restore_flags(flags);
+}
+#endif
+
+static int ultrastor_14f_detect(Scsi_Host_Template * tpnt)
+{
+ size_t i;
+ unsigned char in_byte, version_byte = 0;
+ struct config_1 {
+ unsigned char bios_segment: 3;
+ unsigned char removable_disks_as_fixed: 1;
+ unsigned char interrupt: 2;
+ unsigned char dma_channel: 2;
+ } config_1;
+ struct config_2 {
+ unsigned char ha_scsi_id: 3;
+ unsigned char mapping_mode: 2;
+ unsigned char bios_drive_number: 1;
+ unsigned char tfr_port: 2;
+ } config_2;
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: called\n");
+#endif
+
+ /* If a 24F has already been configured, don't look for a 14F. */
+ if (config.bios_segment)
+ return FALSE;
+
+#ifdef PORT_OVERRIDE
+ if(check_region(PORT_OVERRIDE, 0xc)) {
+ printk("Ultrastor I/O space already in use\n");
+ return FALSE;
+ };
+ config.port_address = PORT_OVERRIDE;
+#else
+ for (i = 0; i < ARRAY_SIZE(ultrastor_ports_14f); i++) {
+ if(check_region(ultrastor_ports_14f[i], 0x0c)) continue;
+ config.port_address = ultrastor_ports_14f[i];
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: testing port address %03X\n", config.port_address);
+#endif
+
+ in_byte = inb(U14F_PRODUCT_ID(config.port_address));
+ if (in_byte != US14F_PRODUCT_ID_0) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+# ifdef PORT_OVERRIDE
+ printk("US14F: detect: wrong product ID 0 - %02X\n", in_byte);
+# else
+ printk("US14F: detect: no adapter at port %03X\n", config.port_address);
+# endif
+#endif
+#ifdef PORT_OVERRIDE
+ return FALSE;
+#else
+ continue;
+#endif
+ }
+ in_byte = inb(U14F_PRODUCT_ID(config.port_address) + 1);
+ /* Only upper nibble is significant for Product ID 1 */
+ if ((in_byte & 0xF0) != US14F_PRODUCT_ID_1) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+# ifdef PORT_OVERRIDE
+ printk("US14F: detect: wrong product ID 1 - %02X\n", in_byte);
+# else
+ printk("US14F: detect: no adapter at port %03X\n", config.port_address);
+# endif
+#endif
+#ifdef PORT_OVERRIDE
+ return FALSE;
+#else
+ continue;
+#endif
+ }
+ version_byte = in_byte;
+#ifndef PORT_OVERRIDE
+ break;
+ }
+ if (i == ARRAY_SIZE(ultrastor_ports_14f)) {
+# if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: no port address found!\n");
+# endif
+ return FALSE;
+ }
+#endif
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: adapter found at port address %03X\n",
+ config.port_address);
+#endif
+
+ /* Set local doorbell mask to disallow bus reset unless
+ ultrastor_bus_reset is true. */
+ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(config.port_address));
+
+ /* All above tests passed, must be the right thing. Get some useful
+ info. */
+
+ request_region(config.port_address, 0x0c,"ultrastor");
+ /* Register the I/O space that we use */
+
+ *(char *)&config_1 = inb(CONFIG(config.port_address + 0));
+ *(char *)&config_2 = inb(CONFIG(config.port_address + 1));
+ config.bios_segment = bios_segment_table[config_1.bios_segment];
+ config.doorbell_address = config.port_address;
+ config.ogm_address = config.port_address + 0x8;
+ config.icm_address = config.port_address + 0xC;
+ config.interrupt = interrupt_table_14f[config_1.interrupt];
+ config.ha_scsi_id = config_2.ha_scsi_id;
+ config.heads = mapping_table[config_2.mapping_mode].heads;
+ config.sectors = mapping_table[config_2.mapping_mode].sectors;
+ config.bios_drive_number = config_2.bios_drive_number;
+ config.subversion = (version_byte & 0x0F);
+ if (config.subversion == U34F)
+ config.dma_channel = 0;
+ else
+ config.dma_channel = dma_channel_table_14f[config_1.dma_channel];
+
+ if (!config.bios_segment) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: not detected.\n");
+#endif
+ return FALSE;
+ }
+
+ /* Final consistency check, verify previous info. */
+ if (config.subversion != U34F)
+ if (!config.dma_channel || !(config_2.tfr_port & 0x2)) {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: consistency check failed\n");
+#endif
+ return FALSE;
+ }
+
+ /* If we were TRULY paranoid, we could issue a host adapter inquiry
+ command here and verify the data returned. But frankly, I'm
+ exhausted! */
+
+ /* Finally! Now I'm satisfied... */
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US14F: detect: detect succeeded\n"
+ " Port address: %03X\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %u\n"
+ " DMA channel: %u\n"
+ " H/A SCSI ID: %u\n"
+ " Subversion: %u\n",
+ config.port_address, config.bios_segment, config.interrupt,
+ config.dma_channel, config.ha_scsi_id, config.subversion);
+#endif
+ tpnt->this_id = config.ha_scsi_id;
+ tpnt->unchecked_isa_dma = (config.subversion != U34F);
+
+#if ULTRASTOR_MAX_CMDS > 1
+ config.mscp_free = ~0;
+#endif
+
+ if (request_irq(config.interrupt, ultrastor_interrupt, 0, "Ultrastor")) {
+ printk("Unable to allocate IRQ%u for UltraStor controller.\n",
+ config.interrupt);
+ return FALSE;
+ }
+ if (config.dma_channel && request_dma(config.dma_channel,"Ultrastor")) {
+ printk("Unable to allocate DMA channel %u for UltraStor controller.\n",
+ config.dma_channel);
+ free_irq(config.interrupt);
+ return FALSE;
+ }
+ tpnt->sg_tablesize = ULTRASTOR_14F_MAX_SG;
+ printk("UltraStor driver version" VERSION ". Using %d SG lists.\n",
+ ULTRASTOR_14F_MAX_SG);
+
+ return TRUE;
+}
+
+static int ultrastor_24f_detect(Scsi_Host_Template * tpnt)
+{
+ register int i;
+ struct Scsi_Host * shpnt = NULL;
+
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US24F: detect");
+#endif
+
+ /* probe each EISA slot at slot address C80 */
+ for (i = 1; i < 15; i++)
+ {
+ unsigned char config_1, config_2;
+ unsigned short addr = (i << 12) | ULTRASTOR_24F_PORT;
+
+ if (inb(addr) != US24F_PRODUCT_ID_0 &&
+ inb(addr+1) != US24F_PRODUCT_ID_1 &&
+ inb(addr+2) != US24F_PRODUCT_ID_2)
+ continue;
+
+ config.revision = inb(addr+3);
+ config.slot = i;
+ if (! (inb(addr+4) & 1))
+ {
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("U24F: found disabled card in slot %u\n", i);
+#endif
+ continue;
+ }
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("U24F: found card in slot %u\n", i);
+#endif
+ config_1 = inb(addr + 5);
+ config.bios_segment = bios_segment_table[config_1 & 7];
+ switch(config_1 >> 4)
+ {
+ case 1:
+ config.interrupt = 15;
+ break;
+ case 2:
+ config.interrupt = 14;
+ break;
+ case 4:
+ config.interrupt = 11;
+ break;
+ case 8:
+ config.interrupt = 10;
+ break;
+ default:
+ printk("U24F: invalid IRQ\n");
+ return FALSE;
+ }
+ if (request_irq(config.interrupt, ultrastor_interrupt, 0, "Ultrastor"))
+ {
+ printk("Unable to allocate IRQ%u for UltraStor controller.\n",
+ config.interrupt);
+ return FALSE;
+ }
+ /* BIOS addr set */
+ /* base port set */
+ config.port_address = addr;
+ config.doorbell_address = addr + 12;
+ config.ogm_address = addr + 0x17;
+ config.icm_address = addr + 0x1C;
+ config_2 = inb(addr + 7);
+ config.ha_scsi_id = config_2 & 7;
+ config.heads = mapping_table[(config_2 >> 3) & 3].heads;
+ config.sectors = mapping_table[(config_2 >> 3) & 3].sectors;
+#if (ULTRASTOR_DEBUG & UD_DETECT)
+ printk("US24F: detect: detect succeeded\n"
+ " Port address: %03X\n"
+ " BIOS segment: %05X\n"
+ " Interrupt: %u\n"
+ " H/A SCSI ID: %u\n",
+ config.port_address, config.bios_segment,
+ config.interrupt, config.ha_scsi_id);
+#endif
+ tpnt->this_id = config.ha_scsi_id;
+ tpnt->unchecked_isa_dma = 0;
+ tpnt->sg_tablesize = ULTRASTOR_24F_MAX_SG;
+
+ shpnt = scsi_register(tpnt, 0);
+ shpnt->irq = config.interrupt;
+ shpnt->dma_channel = config.dma_channel;
+ shpnt->io_port = config.port_address;
+
+#if ULTRASTOR_MAX_CMDS > 1
+ config.mscp_free = ~0;
+#endif
+ /* Mark ICM and OGM free */
+ outb(0, addr + 0x16);
+ outb(0, addr + 0x1B);
+
+ /* Set local doorbell mask to disallow bus reset unless
+ ultrastor_bus_reset is true. */
+ outb(ultrastor_bus_reset ? 0xc2 : 0x82, LCL_DOORBELL_MASK(addr+12));
+ outb(0x02, SYS_DOORBELL_MASK(addr+12));
+ printk("UltraStor driver version " VERSION ". Using %d SG lists.\n",
+ tpnt->sg_tablesize);
+ return TRUE;
+ }
+ return FALSE;
+}
+
+int ultrastor_detect(Scsi_Host_Template * tpnt)
+{
+ tpnt->proc_dir = &proc_scsi_ultrastor;
+ return ultrastor_14f_detect(tpnt) || ultrastor_24f_detect(tpnt);
+}
+
+const char *ultrastor_info(struct Scsi_Host * shpnt)
+{
+ static char buf[64];
+
+ if (config.slot)
+ sprintf(buf, "UltraStor 24F SCSI @ Slot %u IRQ%u\n",
+ config.slot, config.interrupt);
+ else if (config.subversion)
+ sprintf(buf, "UltraStor 34F SCSI @ Port %03X BIOS %05X IRQ%u\n",
+ config.port_address, (int)config.bios_segment,
+ config.interrupt);
+ else
+ sprintf(buf, "UltraStor 14F SCSI @ Port %03X BIOS %05X IRQ%u DMA%u\n",
+ config.port_address, (int)config.bios_segment,
+ config.interrupt, config.dma_channel);
+ return buf;
+}
+
+static inline void build_sg_list(register struct mscp *mscp, Scsi_Cmnd *SCpnt)
+{
+ struct scatterlist *sl;
+ long transfer_length = 0;
+ int i, max;
+
+ sl = (struct scatterlist *) SCpnt->request_buffer;
+ max = SCpnt->use_sg;
+ for (i = 0; i < max; i++) {
+ mscp->sglist[i].address = (unsigned int)sl[i].address;
+ mscp->sglist[i].num_bytes = sl[i].length;
+ transfer_length += sl[i].length;
+ }
+ mscp->number_of_sg_list = max;
+ mscp->transfer_data = (unsigned int)mscp->sglist;
+ /* ??? May not be necessary. Docs are unclear as to whether transfer
+ length field is ignored or whether it should be set to the total
+ number of bytes of the transfer. */
+ mscp->transfer_data_length = transfer_length;
+}
+
+int ultrastor_queuecommand(Scsi_Cmnd *SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ register struct mscp *my_mscp;
+#if ULTRASTOR_MAX_CMDS > 1
+ int mscp_index;
+#endif
+ unsigned int status;
+ int flags;
+
+ /* Next test is for debugging; "can't happen" */
+ if ((config.mscp_free & ((1U << ULTRASTOR_MAX_CMDS) - 1)) == 0)
+ panic("ultrastor_queuecommand: no free MSCP\n");
+ mscp_index = find_and_clear_bit_16(&config.mscp_free);
+
+ /* Has the command been aborted? */
+ if (xchgb(0xff, &config.aborted[mscp_index]) != 0)
+ {
+ status = DID_ABORT << 16;
+ goto aborted;
+ }
+
+ my_mscp = &config.mscp[mscp_index];
+
+#if 1
+ /* This way is faster. */
+ *(unsigned char *)my_mscp = OP_SCSI | (DTD_SCSI << 3);
+#else
+ my_mscp->opcode = OP_SCSI;
+ my_mscp->xdir = DTD_SCSI;
+ my_mscp->dcn = FALSE;
+#endif
+ /* Tape drives don't work properly if the cache is used. The SCSI
+ READ command for a tape doesn't have a block offset, and the adapter
+ incorrectly assumes that all reads from the tape read the same
+ blocks. Results will depend on read buffer size and other disk
+ activity.
+
+ ??? Which other device types should never use the cache? */
+ my_mscp->ca = SCpnt->device->type != TYPE_TAPE;
+ my_mscp->target_id = SCpnt->target;
+ my_mscp->ch_no = 0;
+ my_mscp->lun = SCpnt->lun;
+ if (SCpnt->use_sg) {
+ /* Set scatter/gather flag in SCSI command packet */
+ my_mscp->sg = TRUE;
+ build_sg_list(my_mscp, SCpnt);
+ } else {
+ /* Unset scatter/gather flag in SCSI command packet */
+ my_mscp->sg = FALSE;
+ my_mscp->transfer_data = (unsigned int)SCpnt->request_buffer;
+ my_mscp->transfer_data_length = SCpnt->request_bufflen;
+ }
+ my_mscp->command_link = 0; /*???*/
+ my_mscp->scsi_command_link_id = 0; /*???*/
+ my_mscp->length_of_sense_byte = sizeof SCpnt->sense_buffer;
+ my_mscp->length_of_scsi_cdbs = SCpnt->cmd_len;
+ memcpy(my_mscp->scsi_cdbs, SCpnt->cmnd, my_mscp->length_of_scsi_cdbs);
+ my_mscp->adapter_status = 0;
+ my_mscp->target_status = 0;
+ my_mscp->sense_data = (unsigned int)&SCpnt->sense_buffer;
+ my_mscp->done = done;
+ my_mscp->SCint = SCpnt;
+ SCpnt->host_scribble = (unsigned char *)my_mscp;
+
+ /* Find free OGM slot. On 24F, look for OGM status byte == 0.
+ On 14F and 34F, wait for local interrupt pending flag to clear. */
+
+ retry:
+ if (config.slot)
+ while (inb(config.ogm_address - 1) != 0 &&
+ config.aborted[mscp_index] == 0xff) barrier();
+
+ /* else??? */
+
+ while ((inb(LCL_DOORBELL_INTR(config.doorbell_address)) &
+ (config.slot ? 2 : 1))
+ && config.aborted[mscp_index] == 0xff) barrier();
+
+ /* To avoid race conditions, make the code to write to the adapter
+ atomic. This simplifies the abort code. */
+
+ save_flags(flags);
+ cli();
+
+ if (inb(LCL_DOORBELL_INTR(config.doorbell_address)) &
+ (config.slot ? 2 : 1))
+ {
+ restore_flags(flags);
+ goto retry;
+ }
+
+ status = xchgb(0, &config.aborted[mscp_index]);
+ if (status != 0xff) {
+ restore_flags(flags);
+
+#if ULTRASTOR_DEBUG & (UD_COMMAND | UD_ABORT)
+ printk("USx4F: queuecommand: aborted\n");
+#if ULTRASTOR_MAX_CMDS > 1
+ log_ultrastor_abort(&config, mscp_index);
+#endif
+#endif
+ status <<= 16;
+
+ aborted:
+ set_bit(mscp_index, &config.mscp_free);
+ /* If the driver queues commands, call the done proc here. Otherwise
+ return an error. */
+#if ULTRASTOR_MAX_CMDS > 1
+ SCpnt->result = status;
+ done(SCpnt);
+ return 0;
+#else
+ return status;
+#endif
+ }
+
+ /* Store pointer in OGM address bytes */
+ outl((unsigned int)my_mscp, config.ogm_address);
+
+ /* Issue OGM interrupt */
+ if (config.slot) {
+ /* Write OGM command register on 24F */
+ outb(1, config.ogm_address - 1);
+ outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));
+ } else {
+ outb(0x1, LCL_DOORBELL_INTR(config.doorbell_address));
+ }
+
+ restore_flags(flags);
+
+#if (ULTRASTOR_DEBUG & UD_COMMAND)
+ printk("USx4F: queuecommand: returning\n");
+#endif
+
+ return 0;
+}
+
+/* This code must deal with 2 cases:
+
+ 1. The command has not been written to the OGM. In this case, set
+ the abort flag and return.
+
+ 2. The command has been written to the OGM and is stuck somewhere in
+ the adapter.
+
+ 2a. On a 24F, ask the adapter to abort the command. It will interrupt
+ when it does.
+
+ 2b. Call the command's done procedure.
+
+ */
+
+int ultrastor_abort(Scsi_Cmnd *SCpnt)
+{
+#if ULTRASTOR_DEBUG & UD_ABORT
+ char out[108];
+ unsigned char icm_status = 0, ogm_status = 0;
+ unsigned int icm_addr = 0, ogm_addr = 0;
+#endif
+ unsigned int mscp_index;
+ unsigned char old_aborted;
+ void (*done)(Scsi_Cmnd *);
+
+ if(config.slot)
+ return SCSI_ABORT_SNOOZE; /* Do not attempt an abort for the 24f */
+
+ /* Simple consistency checking */
+ if(!SCpnt->host_scribble)
+ return SCSI_ABORT_NOT_RUNNING;
+
+ mscp_index = ((struct mscp *)SCpnt->host_scribble) - config.mscp;
+ if (mscp_index >= ULTRASTOR_MAX_CMDS)
+ panic("Ux4F aborting invalid MSCP");
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ if (config.slot)
+ {
+ int port0 = (config.slot << 12) | 0xc80;
+ int i;
+ int flags;
+ save_flags(flags);
+ cli();
+ strcpy(out, "OGM %d:%x ICM %d:%x ports: ");
+ for (i = 0; i < 16; i++)
+ {
+ unsigned char p = inb(port0 + i);
+ out[28 + i * 3] = "0123456789abcdef"[p >> 4];
+ out[29 + i * 3] = "0123456789abcdef"[p & 15];
+ out[30 + i * 3] = ' ';
+ }
+ out[28 + i * 3] = '\n';
+ out[29 + i * 3] = 0;
+ ogm_status = inb(port0 + 22);
+ ogm_addr = inl(port0 + 23);
+ icm_status = inb(port0 + 27);
+ icm_addr = inl(port0 + 28);
+ restore_flags(flags);
+ }
+
+ /* First check to see if an interrupt is pending. I suspect the SiS
+ chipset loses interrupts. (I also suspect is mangles data, but
+ one bug at a time... */
+ if (config.slot ? inb(config.icm_address - 1) == 2 :
+ (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+ {
+ int flags;
+ save_flags(flags);
+ printk("Ux4F: abort while completed command pending\n");
+ restore_flags(flags);
+ cli();
+ ultrastor_interrupt(0, NULL);
+ restore_flags(flags);
+ return SCSI_ABORT_SUCCESS; /* FIXME - is this correct? -ERY */
+ }
+#endif
+
+ old_aborted = xchgb(DID_ABORT, &config.aborted[mscp_index]);
+
+ /* aborted == 0xff is the signal that queuecommand has not yet sent
+ the command. It will notice the new abort flag and fail. */
+ if (old_aborted == 0xff)
+ return SCSI_ABORT_SUCCESS;
+
+ /* On 24F, send an abort MSCP request. The adapter will interrupt
+ and the interrupt handler will call done. */
+ if (config.slot && inb(config.ogm_address - 1) == 0)
+ {
+ int flags;
+
+ save_flags(flags);
+ cli();
+ outl((int)&config.mscp[mscp_index], config.ogm_address);
+ inb(0xc80); /* delay */
+ outb(0x80, config.ogm_address - 1);
+ outb(0x2, LCL_DOORBELL_INTR(config.doorbell_address));
+#if ULTRASTOR_DEBUG & UD_ABORT
+ log_ultrastor_abort(&config, mscp_index);
+ printk(out, ogm_status, ogm_addr, icm_status, icm_addr);
+#endif
+ restore_flags(flags);
+ return SCSI_ABORT_PENDING;
+ }
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ log_ultrastor_abort(&config, mscp_index);
+#endif
+
+ /* Can't request a graceful abort. Either this is not a 24F or
+ the OGM is busy. Don't free the command -- the adapter might
+ still be using it. Setting SCint = 0 causes the interrupt
+ handler to ignore the command. */
+
+ /* FIXME - devices that implement soft resets will still be running
+ the command after a bus reset. We would probably rather leave
+ the command in the queue. The upper level code will automatically
+ leave the command in the active state instead of requeueing it. ERY */
+
+#if ULTRASTOR_DEBUG & UD_ABORT
+ if (config.mscp[mscp_index].SCint != SCpnt)
+ printk("abort: command mismatch, %p != %p\n",
+ config.mscp[mscp_index].SCint, SCpnt);
+#endif
+ if (config.mscp[mscp_index].SCint == 0)
+ return SCSI_ABORT_NOT_RUNNING;
+
+ if (config.mscp[mscp_index].SCint != SCpnt) panic("Bad abort");
+ config.mscp[mscp_index].SCint = 0;
+ done = config.mscp[mscp_index].done;
+ config.mscp[mscp_index].done = 0;
+ SCpnt->result = DID_ABORT << 16;
+ /* I worry about reentrancy in scsi.c */
+ done(SCpnt);
+
+ /* Need to set a timeout here in case command never completes. */
+ return SCSI_ABORT_SUCCESS;
+}
+
+int ultrastor_reset(Scsi_Cmnd * SCpnt)
+{
+ int flags;
+ register int i;
+#if (ULTRASTOR_DEBUG & UD_RESET)
+ printk("US14F: reset: called\n");
+#endif
+
+ if(config.slot)
+ return SCSI_RESET_PUNT; /* Do not attempt a reset for the 24f */
+
+ save_flags(flags);
+ cli();
+
+ /* Reset the adapter and SCSI bus. The SCSI bus reset can be
+ inhibited by clearing ultrastor_bus_reset before probe. */
+ outb(0xc0, LCL_DOORBELL_INTR(config.doorbell_address));
+ if (config.slot)
+ {
+ outb(0, config.ogm_address - 1);
+ outb(0, config.icm_address - 1);
+ }
+
+#if ULTRASTOR_MAX_CMDS == 1
+ if (config.mscp_busy && config.mscp->done && config.mscp->SCint)
+ {
+ config.mscp->SCint->result = DID_RESET << 16;
+ config.mscp->done(config.mscp->SCint);
+ }
+ config.mscp->SCint = 0;
+#else
+ for (i = 0; i < ULTRASTOR_MAX_CMDS; i++)
+ {
+ if (! (config.mscp_free & (1 << i)) &&
+ config.mscp[i].done && config.mscp[i].SCint)
+ {
+ config.mscp[i].SCint->result = DID_RESET << 16;
+ config.mscp[i].done(config.mscp[i].SCint);
+ config.mscp[i].done = 0;
+ }
+ config.mscp[i].SCint = 0;
+ }
+#endif
+
+ /* FIXME - if the device implements soft resets, then the command
+ will still be running. ERY */
+
+ memset((unsigned char *)config.aborted, 0, sizeof config.aborted);
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = 0;
+#else
+ config.mscp_free = ~0;
+#endif
+
+ restore_flags(flags);
+ return SCSI_RESET_SUCCESS;
+
+}
+
+int ultrastor_biosparam(Disk * disk, kdev_t dev, int * dkinfo)
+{
+ int size = disk->capacity;
+ unsigned int s = config.heads * config.sectors;
+
+ dkinfo[0] = config.heads;
+ dkinfo[1] = config.sectors;
+ dkinfo[2] = size / s; /* Ignore partial cylinders */
+#if 0
+ if (dkinfo[2] > 1024)
+ dkinfo[2] = 1024;
+#endif
+ return 0;
+}
+
+static void ultrastor_interrupt(int irq, struct pt_regs *regs)
+{
+ unsigned int status;
+#if ULTRASTOR_MAX_CMDS > 1
+ unsigned int mscp_index;
+#endif
+ register struct mscp *mscp;
+ void (*done)(Scsi_Cmnd *);
+ Scsi_Cmnd *SCtmp;
+
+#if ULTRASTOR_MAX_CMDS == 1
+ mscp = &config.mscp[0];
+#else
+ mscp = (struct mscp *)inl(config.icm_address);
+ mscp_index = mscp - config.mscp;
+ if (mscp_index >= ULTRASTOR_MAX_CMDS) {
+ printk("Ux4F interrupt: bad MSCP address %x\n", (unsigned int) mscp);
+ /* A command has been lost. Reset and report an error
+ for all commands. */
+ ultrastor_reset(NULL);
+ return;
+ }
+#endif
+
+ /* Clean ICM slot (set ICMINT bit to 0) */
+ if (config.slot) {
+ unsigned char icm_status = inb(config.icm_address - 1);
+#if ULTRASTOR_DEBUG & (UD_INTERRUPT|UD_ERROR|UD_ABORT)
+ if (icm_status != 1 && icm_status != 2)
+ printk("US24F: ICM status %x for MSCP %d (%x)\n", icm_status,
+ mscp_index, (unsigned int) mscp);
+#endif
+ /* The manual says clear interrupt then write 0 to ICM status.
+ This seems backwards, but I'll do it anyway. --jfc */
+ outb(2, SYS_DOORBELL_INTR(config.doorbell_address));
+ outb(0, config.icm_address - 1);
+ if (icm_status == 4) {
+ printk("UltraStor abort command failed\n");
+ return;
+ }
+ if (icm_status == 3) {
+ void (*done)(Scsi_Cmnd *) = mscp->done;
+ if (done) {
+ mscp->done = 0;
+ mscp->SCint->result = DID_ABORT << 16;
+ done(mscp->SCint);
+ }
+ return;
+ }
+ } else {
+ outb(1, SYS_DOORBELL_INTR(config.doorbell_address));
+ }
+
+ SCtmp = mscp->SCint;
+ mscp->SCint = NULL;
+
+ if (SCtmp == 0)
+ {
+#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)
+ printk("MSCP %d (%x): no command\n", mscp_index, (unsigned int) mscp);
+#endif
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = FALSE;
+#else
+ set_bit(mscp_index, &config.mscp_free);
+#endif
+ config.aborted[mscp_index] = 0;
+ return;
+ }
+
+ /* Save done locally and zero before calling. This is needed as
+ once we call done, we may get another command queued before this
+ interrupt service routine can return. */
+ done = mscp->done;
+ mscp->done = 0;
+
+ /* Let the higher levels know that we're done */
+ switch (mscp->adapter_status)
+ {
+ case 0:
+ status = DID_OK << 16;
+ break;
+ case 0x01: /* invalid command */
+ case 0x02: /* invalid parameters */
+ case 0x03: /* invalid data list */
+ default:
+ status = DID_ERROR << 16;
+ break;
+ case 0x84: /* SCSI bus abort */
+ status = DID_ABORT << 16;
+ break;
+ case 0x91:
+ status = DID_TIME_OUT << 16;
+ break;
+ }
+
+ SCtmp->result = status | mscp->target_status;
+
+ SCtmp->host_scribble = 0;
+
+ /* Free up mscp block for next command */
+#if ULTRASTOR_MAX_CMDS == 1
+ config.mscp_busy = FALSE;
+#else
+ set_bit(mscp_index, &config.mscp_free);
+#endif
+
+#if ULTRASTOR_DEBUG & (UD_ABORT|UD_INTERRUPT)
+ if (config.aborted[mscp_index])
+ printk("Ux4 interrupt: MSCP %d (%x) aborted = %d\n",
+ mscp_index, (unsigned int) mscp, config.aborted[mscp_index]);
+#endif
+ config.aborted[mscp_index] = 0;
+
+ if (done)
+ done(SCtmp);
+ else
+ printk("US14F: interrupt: unexpected interrupt\n");
+
+ if (config.slot ? inb(config.icm_address - 1) : (inb(SYS_DOORBELL_INTR(config.doorbell_address)) & 1))
+ printk("Ux4F: multiple commands completed\n");
+
+#if (ULTRASTOR_DEBUG & UD_INTERRUPT)
+ printk("USx4F: interrupt: returning\n");
+#endif
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = ULTRASTOR_14F;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/ultrastor.h b/i386/i386at/gpl/linux/scsi/ultrastor.h
new file mode 100644
index 00000000..10cf63f2
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/ultrastor.h
@@ -0,0 +1,102 @@
+/*
+ * ultrastor.c (C) 1991 David B. Gentzel
+ * Low-level scsi driver for UltraStor 14F
+ * by David B. Gentzel, Whitfield Software Services, Carnegie, PA
+ * (gentzel@nova.enet.dec.com)
+ * scatter/gather added by Scott Taylor (n217cg@tamuts.tamu.edu)
+ * 24F support by John F. Carr (jfc@athena.mit.edu)
+ * John's work modified by Caleb Epstein (cae@jpmorgan.com) and
+ * Eric Youngdale (eric@tantalus.nrl.navy.mil).
+ * Thanks to UltraStor for providing the necessary documentation
+ */
+
+#ifndef _ULTRASTOR_H
+#define _ULTRASTOR_H
+#include <linux/kdev_t.h>
+
+int ultrastor_detect(Scsi_Host_Template *);
+const char *ultrastor_info(struct Scsi_Host * shpnt);
+int ultrastor_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int ultrastor_abort(Scsi_Cmnd *);
+int ultrastor_reset(Scsi_Cmnd *);
+int ultrastor_biosparam(Disk *, kdev_t, int *);
+
+
+#define ULTRASTOR_14F_MAX_SG 16
+#define ULTRASTOR_24F_MAX_SG 33
+
+#define ULTRASTOR_MAX_CMDS_PER_LUN 5
+#define ULTRASTOR_MAX_CMDS 16
+
+#define ULTRASTOR_24F_PORT 0xC80
+
+
+#define ULTRASTOR_14F { NULL, NULL, /* Ptr for modules*/ \
+ NULL, \
+ NULL, \
+ "UltraStor 14F/24F/34F", \
+ ultrastor_detect, \
+ NULL, /* Release */ \
+ ultrastor_info, \
+ 0, \
+ ultrastor_queuecommand, \
+ ultrastor_abort, \
+ ultrastor_reset, \
+ 0, \
+ ultrastor_biosparam, \
+ ULTRASTOR_MAX_CMDS, \
+ 0, \
+ ULTRASTOR_14F_MAX_SG, \
+ ULTRASTOR_MAX_CMDS_PER_LUN, \
+ 0, \
+ 1, \
+ ENABLE_CLUSTERING }
+
+
+#ifdef ULTRASTOR_PRIVATE
+
+#define UD_ABORT 0x0001
+#define UD_COMMAND 0x0002
+#define UD_DETECT 0x0004
+#define UD_INTERRUPT 0x0008
+#define UD_RESET 0x0010
+#define UD_MULTI_CMD 0x0020
+#define UD_CSIR 0x0040
+#define UD_ERROR 0x0080
+
+/* #define PORT_OVERRIDE 0x330 */
+
+/* Values for the PRODUCT_ID ports for the 14F */
+#define US14F_PRODUCT_ID_0 0x56
+#define US14F_PRODUCT_ID_1 0x40 /* NOTE: Only upper nibble is used */
+
+#define US24F_PRODUCT_ID_0 0x56
+#define US24F_PRODUCT_ID_1 0x63
+#define US24F_PRODUCT_ID_2 0x02
+
+/* Subversion values */
+#define U14F 0
+#define U34F 1
+
+/* MSCP field values */
+
+/* Opcode */
+#define OP_HOST_ADAPTER 0x1
+#define OP_SCSI 0x2
+#define OP_RESET 0x4
+
+/* Date Transfer Direction */
+#define DTD_SCSI 0x0
+#define DTD_IN 0x1
+#define DTD_OUT 0x2
+#define DTD_NONE 0x3
+
+/* Host Adapter command subcodes */
+#define HA_CMD_INQUIRY 0x1
+#define HA_CMD_SELF_DIAG 0x2
+#define HA_CMD_READ_BUFF 0x3
+#define HA_CMD_WRITE_BUFF 0x4
+
+#endif
+
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/wd7000.c b/i386/i386at/gpl/linux/scsi/wd7000.c
new file mode 100644
index 00000000..61d92b10
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/wd7000.c
@@ -0,0 +1,1237 @@
+/* $Id: wd7000.c,v 1.1.1.1 1997/02/25 21:27:53 thomas Exp $
+ * linux/drivers/scsi/wd7000.c
+ *
+ * Copyright (C) 1992 Thomas Wuensche
+ * closely related to the aha1542 driver from Tommy Thorn
+ * ( as close as different hardware allows on a lowlevel-driver :-) )
+ *
+ * Revised (and renamed) by John Boyd <boyd@cis.ohio-state.edu> to
+ * accommodate Eric Youngdale's modifications to scsi.c. Nov 1992.
+ *
+ * Additional changes to support scatter/gather. Dec. 1992. tw/jb
+ *
+ * No longer tries to reset SCSI bus at boot (it wasn't working anyway).
+ * Rewritten to support multiple host adapters.
+ * Miscellaneous cleanup.
+ * So far, still doesn't do reset or abort correctly, since I have no idea
+ * how to do them with this board (8^(. Jan 1994 jb
+ *
+ * This driver now supports both of the two standard configurations (per
+ * the 3.36 Owner's Manual, my latest reference) by the same method as
+ * before; namely, by looking for a BIOS signature. Thus, the location of
+ * the BIOS signature determines the board configuration. Until I have
+ * time to do something more flexible, users should stick to one of the
+ * following:
+ *
+ * Standard configuration for single-adapter systems:
+ * - BIOS at CE00h
+ * - I/O base address 350h
+ * - IRQ level 15
+ * - DMA channel 6
+ * Standard configuration for a second adapter in a system:
+ * - BIOS at C800h
+ * - I/O base address 330h
+ * - IRQ level 11
+ * - DMA channel 5
+ *
+ * Anyone who can recompile the kernel is welcome to add others as need
+ * arises, but unpredictable results may occur if there are conflicts.
+ * In any event, if there are multiple adapters in a system, they MUST
+ * use different I/O bases, IRQ levels, and DMA channels, since they will be
+ * indistinguishable (and in direct conflict) otherwise.
+ *
+ * As a point of information, the NO_OP command toggles the CMD_RDY bit
+ * of the status port, and this fact could be used as a test for the I/O
+ * base address (or more generally, board detection). There is an interrupt
+ * status port, so IRQ probing could also be done. I suppose the full
+ * DMA diagnostic could be used to detect the DMA channel being used. I
+ * haven't done any of this, though, because I think there's too much of
+ * a chance that such explorations could be destructive, if some other
+ * board's resources are used inadvertently. So, call me a wimp, but I
+ * don't want to try it. The only kind of exploration I trust is memory
+ * exploration, since it's more certain that reading memory won't be
+ * destructive.
+ *
+ * More to my liking would be a LILO boot command line specification, such
+ * as is used by the aha152x driver (and possibly others). I'll look into
+ * it, as I have time...
+ *
+ * I get mail occasionally from people who either are using or are
+ * considering using a WD7000 with Linux. There is a variety of
+ * nomenclature describing WD7000's. To the best of my knowledge, the
+ * following is a brief summary (from an old WD doc - I don't work for
+ * them or anything like that):
+ *
+ * WD7000-FASST2: This is a WD7000 board with the real-mode SST ROM BIOS
+ * installed. Last I heard, the BIOS was actually done by Columbia
+ * Data Products. The BIOS is only used by this driver (and thus
+ * by Linux) to identify the board; none of it can be executed under
+ * Linux.
+ *
+ * WD7000-ASC: This is the original adapter board, with or without BIOS.
+ * The board uses a WD33C93 or WD33C93A SBIC, which in turn is
+ * controlled by an onboard Z80 processor. The board interface
+ * visible to the host CPU is defined effectively by the Z80's
+ * firmware, and it is this firmware's revision level that is
+ * determined and reported by this driver. (The version of the
+ * on-board BIOS is of no interest whatsoever.) The host CPU has
+ * no access to the SBIC; hence the fact that it is a WD33C93 is
+ * also of no interest to this driver.
+ *
+ * WD7000-AX:
+ * WD7000-MX:
+ * WD7000-EX: These are newer versions of the WD7000-ASC. The -ASC is
+ * largely built from discrete components; these boards use more
+ * integration. The -AX is an ISA bus board (like the -ASC),
+ * the -MX is an MCA (i.e., PS/2) bus board), and the -EX is an
+ * EISA bus board.
+ *
+ * At the time of my documentation, the -?X boards were "future" products,
+ * and were not yet available. However, I vaguely recall that Thomas
+ * Wuensche had an -AX, so I believe at least it is supported by this
+ * driver. I have no personal knowledge of either -MX or -EX boards.
+ *
+ * P.S. Just recently, I've discovered (directly from WD and Future
+ * Domain) that all but the WD7000-EX have been out of production for
+ * two years now. FD has production rights to the 7000-EX, and are
+ * producing it under a new name, and with a new BIOS. If anyone has
+ * one of the FD boards, it would be nice to come up with a signature
+ * for it.
+ * J.B. Jan 1994.
+ */
+
+#ifdef MODULE
+#include <linux/module.h>
+#endif
+
+#include <stdarg.h>
+#include <linux/kernel.h>
+#include <linux/head.h>
+#include <linux/types.h>
+#include <linux/string.h>
+#include <linux/sched.h>
+#include <linux/malloc.h>
+#include <asm/system.h>
+#include <asm/dma.h>
+#include <asm/io.h>
+#include <linux/ioport.h>
+#include <linux/proc_fs.h>
+#include <linux/blk.h>
+#include "scsi.h"
+#include "hosts.h"
+#include "sd.h"
+
+#define ANY2SCSI_INLINE /* undef this to use old macros */
+#undef DEBUG
+
+#include "wd7000.h"
+
+#include<linux/stat.h>
+
+struct proc_dir_entry proc_scsi_wd7000 = {
+ PROC_SCSI_7000FASST, 6, "wd7000",
+ S_IFDIR | S_IRUGO | S_IXUGO, 2
+};
+
+
+/*
+ * Mailbox structure sizes.
+ * I prefer to keep the number of ICMBs much larger than the number of
+ * OGMBs. OGMBs are used very quickly by the driver to start one or
+ * more commands, while ICMBs are used by the host adapter per command.
+ */
+#define OGMB_CNT 16
+#define ICMB_CNT 32
+
+/*
+ * Scb's are shared by all active adapters. So, if they all become busy,
+ * callers may be made to wait in alloc_scbs for them to free. That can
+ * be avoided by setting MAX_SCBS to NUM_CONFIG * WD7000_Q. If you'd
+ * rather conserve memory, use a smaller number (> 0, of course) - things
+ * will should still work OK.
+ */
+#define MAX_SCBS 32
+
+/*
+ * WD7000-specific mailbox structure
+ *
+ */
+typedef volatile struct mailbox{
+ unchar status;
+ unchar scbptr[3]; /* SCSI-style - MSB first (big endian) */
+} Mailbox;
+
+/*
+ * This structure should contain all per-adapter global data. I.e., any
+ * new global per-adapter data should put in here.
+ *
+ */
+typedef struct adapter {
+ struct Scsi_Host *sh; /* Pointer to Scsi_Host structure */
+ int iobase; /* This adapter's I/O base address */
+ int irq; /* This adapter's IRQ level */
+ int dma; /* This adapter's DMA channel */
+ struct { /* This adapter's mailboxes */
+ Mailbox ogmb[OGMB_CNT]; /* Outgoing mailboxes */
+ Mailbox icmb[ICMB_CNT]; /* Incoming mailboxes */
+ } mb;
+ int next_ogmb; /* to reduce contention at mailboxes */
+ unchar control; /* shadows CONTROL port value */
+ unchar rev1, rev2; /* filled in by wd7000_revision */
+} Adapter;
+
+/*
+ * The following is set up by wd7000_detect, and used thereafter by
+ * wd7000_intr_handle to map the irq level to the corresponding Adapter.
+ * Note that if SA_INTERRUPT is not used, wd7000_intr_handle must be
+ * changed to pick up the IRQ level correctly.
+ */
+Adapter *irq2host[16] = {NULL}; /* Possible IRQs are 0-15 */
+
+/*
+ * Standard Adapter Configurations - used by wd7000_detect
+ */
+typedef struct {
+ const void *bios; /* (linear) base address for ROM BIOS */
+ int iobase; /* I/O ports base address */
+ int irq; /* IRQ level */
+ int dma; /* DMA channel */
+} Config;
+
+static const Config configs[] = {
+ {(void *) 0xce000, 0x350, 15, 6}, /* defaults for single adapter */
+ {(void *) 0xc8000, 0x330, 11, 5}, /* defaults for second adapter */
+ {(void *) 0xd8000, 0x350, 15, 6}, /* Arghhh.... who added this ? */
+};
+#define NUM_CONFIGS (sizeof(configs)/sizeof(Config))
+
+/*
+ * The following list defines strings to look for in the BIOS that identify
+ * it as the WD7000-FASST2 SST BIOS. I suspect that something should be
+ * added for the Future Domain version.
+ */
+typedef struct signature {
+ const void *sig; /* String to look for */
+ unsigned ofs; /* offset from BIOS base address */
+ unsigned len; /* length of string */
+} Signature;
+
+static const Signature signatures[] = {
+ {"SSTBIOS",0x0000d,7} /* "SSTBIOS" @ offset 0x0000d */
+};
+#define NUM_SIGNATURES (sizeof(signatures)/sizeof(Signature))
+
+
+/*
+ * I/O Port Offsets and Bit Definitions
+ * 4 addresses are used. Those not defined here are reserved.
+ */
+#define ASC_STAT 0 /* Status, Read */
+#define ASC_COMMAND 0 /* Command, Write */
+#define ASC_INTR_STAT 1 /* Interrupt Status, Read */
+#define ASC_INTR_ACK 1 /* Acknowledge, Write */
+#define ASC_CONTROL 2 /* Control, Write */
+
+/* ASC Status Port
+ */
+#define INT_IM 0x80 /* Interrupt Image Flag */
+#define CMD_RDY 0x40 /* Command Port Ready */
+#define CMD_REJ 0x20 /* Command Port Byte Rejected */
+#define ASC_INIT 0x10 /* ASC Initialized Flag */
+#define ASC_STATMASK 0xf0 /* The lower 4 Bytes are reserved */
+
+/* COMMAND opcodes
+ *
+ * Unfortunately, I have no idea how to properly use some of these commands,
+ * as the OEM manual does not make it clear. I have not been able to use
+ * enable/disable unsolicited interrupts or the reset commands with any
+ * discernible effect whatsoever. I think they may be related to certain
+ * ICB commands, but again, the OEM manual doesn't make that clear.
+ */
+#define NO_OP 0 /* NO-OP toggles CMD_RDY bit in ASC_STAT */
+#define INITIALIZATION 1 /* initialization (10 bytes) */
+#define DISABLE_UNS_INTR 2 /* disable unsolicited interrupts */
+#define ENABLE_UNS_INTR 3 /* enable unsolicited interrupts */
+#define INTR_ON_FREE_OGMB 4 /* interrupt on free OGMB */
+#define SOFT_RESET 5 /* SCSI bus soft reset */
+#define HARD_RESET_ACK 6 /* SCSI bus hard reset acknowledge */
+#define START_OGMB 0x80 /* start command in OGMB (n) */
+#define SCAN_OGMBS 0xc0 /* start multiple commands, signature (n) */
+ /* where (n) = lower 6 bits */
+/* For INITIALIZATION:
+ */
+typedef struct initCmd {
+ unchar op; /* command opcode (= 1) */
+ unchar ID; /* Adapter's SCSI ID */
+ unchar bus_on; /* Bus on time, x 125ns (see below) */
+ unchar bus_off; /* Bus off time, "" "" */
+ unchar rsvd; /* Reserved */
+ unchar mailboxes[3]; /* Address of Mailboxes, MSB first */
+ unchar ogmbs; /* Number of outgoing MBs, max 64, 0,1 = 1 */
+ unchar icmbs; /* Number of incoming MBs, "" "" */
+} InitCmd;
+
+#define BUS_ON 64 /* x 125ns = 8000ns (BIOS default) */
+#define BUS_OFF 15 /* x 125ns = 1875ns (BIOS default) */
+
+/* Interrupt Status Port - also returns diagnostic codes at ASC reset
+ *
+ * if msb is zero, the lower bits are diagnostic status
+ * Diagnostics:
+ * 01 No diagnostic error occurred
+ * 02 RAM failure
+ * 03 FIFO R/W failed
+ * 04 SBIC register read/write failed
+ * 05 Initialization D-FF failed
+ * 06 Host IRQ D-FF failed
+ * 07 ROM checksum error
+ * Interrupt status (bitwise):
+ * 10NNNNNN outgoing mailbox NNNNNN is free
+ * 11NNNNNN incoming mailbox NNNNNN needs service
+ */
+#define MB_INTR 0xC0 /* Mailbox Service possible/required */
+#define IMB_INTR 0x40 /* 1 Incoming / 0 Outgoing */
+#define MB_MASK 0x3f /* mask for mailbox number */
+
+/* CONTROL port bits
+ */
+#define INT_EN 0x08 /* Interrupt Enable */
+#define DMA_EN 0x04 /* DMA Enable */
+#define SCSI_RES 0x02 /* SCSI Reset */
+#define ASC_RES 0x01 /* ASC Reset */
+
+/*
+ Driver data structures:
+ - mb and scbs are required for interfacing with the host adapter.
+ An SCB has extra fields not visible to the adapter; mb's
+ _cannot_ do this, since the adapter assumes they are contiguous in
+ memory, 4 bytes each, with ICMBs following OGMBs, and uses this fact
+ to access them.
+ - An icb is for host-only (non-SCSI) commands. ICBs are 16 bytes each;
+ the additional bytes are used only by the driver.
+ - For now, a pool of SCBs are kept in global storage by this driver,
+ and are allocated and freed as needed.
+
+ The 7000-FASST2 marks OGMBs empty as soon as it has _started_ a command,
+ not when it has finished. Since the SCB must be around for completion,
+ problems arise when SCBs correspond to OGMBs, which may be reallocated
+ earlier (or delayed unnecessarily until a command completes).
+ Mailboxes are used as transient data structures, simply for
+ carrying SCB addresses to/from the 7000-FASST2.
+
+ Note also since SCBs are not "permanently" associated with mailboxes,
+ there is no need to keep a global list of Scsi_Cmnd pointers indexed
+ by OGMB. Again, SCBs reference their Scsi_Cmnds directly, so mailbox
+ indices need not be involved.
+*/
+
+/*
+ * WD7000-specific scatter/gather element structure
+ */
+typedef struct sgb {
+ unchar len[3];
+ unchar ptr[3]; /* Also SCSI-style - MSB first */
+} Sgb;
+
+typedef struct scb { /* Command Control Block 5.4.1 */
+ unchar op; /* Command Control Block Operation Code */
+ unchar idlun; /* op=0,2:Target Id, op=1:Initiator Id */
+ /* Outbound data transfer, length is checked*/
+ /* Inbound data transfer, length is checked */
+ /* Logical Unit Number */
+ unchar cdb[12]; /* SCSI Command Block */
+ volatile unchar status; /* SCSI Return Status */
+ volatile unchar vue; /* Vendor Unique Error Code */
+ unchar maxlen[3]; /* Maximum Data Transfer Length */
+ unchar dataptr[3]; /* SCSI Data Block Pointer */
+ unchar linkptr[3]; /* Next Command Link Pointer */
+ unchar direc; /* Transfer Direction */
+ unchar reserved2[6]; /* SCSI Command Descriptor Block */
+ /* end of hardware SCB */
+ Scsi_Cmnd *SCpnt; /* Scsi_Cmnd using this SCB */
+ Sgb sgb[WD7000_SG]; /* Scatter/gather list for this SCB */
+ Adapter *host; /* host adapter */
+ struct scb *next; /* for lists of scbs */
+} Scb;
+
+/*
+ * This driver is written to allow host-only commands to be executed.
+ * These use a 16-byte block called an ICB. The format is extended by the
+ * driver to 18 bytes, to support the status returned in the ICMB and
+ * an execution phase code.
+ *
+ * There are other formats besides these; these are the ones I've tried
+ * to use. Formats for some of the defined ICB opcodes are not defined
+ * (notably, get/set unsolicited interrupt status) in my copy of the OEM
+ * manual, and others are ambiguous/hard to follow.
+ */
+#define ICB_OP_MASK 0x80 /* distinguishes scbs from icbs */
+#define ICB_OP_OPEN_RBUF 0x80 /* open receive buffer */
+#define ICB_OP_RECV_CMD 0x81 /* receive command from initiator */
+#define ICB_OP_RECV_DATA 0x82 /* receive data from initiator */
+#define ICB_OP_RECV_SDATA 0x83 /* receive data with status from init. */
+#define ICB_OP_SEND_DATA 0x84 /* send data with status to initiator */
+#define ICB_OP_SEND_STAT 0x86 /* send command status to initiator */
+ /* 0x87 is reserved */
+#define ICB_OP_READ_INIT 0x88 /* read initialization bytes */
+#define ICB_OP_READ_ID 0x89 /* read adapter's SCSI ID */
+#define ICB_OP_SET_UMASK 0x8A /* set unsolicited interrupt mask */
+#define ICB_OP_GET_UMASK 0x8B /* read unsolicited interrupt mask */
+#define ICB_OP_GET_REVISION 0x8C /* read firmware revision level */
+#define ICB_OP_DIAGNOSTICS 0x8D /* execute diagnostics */
+#define ICB_OP_SET_EPARMS 0x8E /* set execution parameters */
+#define ICB_OP_GET_EPARMS 0x8F /* read execution parameters */
+
+typedef struct icbRecvCmd {
+ unchar op;
+ unchar IDlun; /* Initiator SCSI ID/lun */
+ unchar len[3]; /* command buffer length */
+ unchar ptr[3]; /* command buffer address */
+ unchar rsvd[7]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbRecvCmd;
+
+typedef struct icbSendStat {
+ unchar op;
+ unchar IDlun; /* Target SCSI ID/lun */
+ unchar stat; /* (outgoing) completion status byte 1 */
+ unchar rsvd[12]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbSendStat;
+
+typedef struct icbRevLvl {
+ unchar op;
+ volatile unchar primary; /* primary revision level (returned) */
+ volatile unchar secondary; /* secondary revision level (returned) */
+ unchar rsvd[12]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbRevLvl;
+
+typedef struct icbUnsMask { /* I'm totally guessing here */
+ unchar op;
+ volatile unchar mask[14]; /* mask bits */
+#ifdef 0
+ unchar rsvd[12]; /* reserved */
+#endif
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbUnsMask;
+
+typedef struct icbDiag {
+ unchar op;
+ unchar type; /* diagnostics type code (0-3) */
+ unchar len[3]; /* buffer length */
+ unchar ptr[3]; /* buffer address */
+ unchar rsvd[7]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbDiag;
+
+#define ICB_DIAG_POWERUP 0 /* Power-up diags only */
+#define ICB_DIAG_WALKING 1 /* walking 1's pattern */
+#define ICB_DIAG_DMA 2 /* DMA - system memory diags */
+#define ICB_DIAG_FULL 3 /* do both 1 & 2 */
+
+typedef struct icbParms {
+ unchar op;
+ unchar rsvd1; /* reserved */
+ unchar len[3]; /* parms buffer length */
+ unchar ptr[3]; /* parms buffer address */
+ unchar idx[2]; /* index (MSB-LSB) */
+ unchar rsvd2[5]; /* reserved */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbParms;
+
+typedef struct icbAny {
+ unchar op;
+ unchar data[14]; /* format-specific data */
+ volatile unchar vue; /* vendor-unique error code */
+ volatile unchar status; /* returned (icmb) status */
+ volatile unchar phase; /* used by interrupt handler */
+} IcbAny;
+
+typedef union icb {
+ unchar op; /* ICB opcode */
+ IcbRecvCmd recv_cmd; /* format for receive command */
+ IcbSendStat send_stat; /* format for send status */
+ IcbRevLvl rev_lvl; /* format for get revision level */
+ IcbDiag diag; /* format for execute diagnostics */
+ IcbParms eparms; /* format for get/set exec parms */
+ IcbAny icb; /* generic format */
+ unchar data[18];
+} Icb;
+
+
+/*
+ * Driver SCB structure pool.
+ *
+ * The SCBs declared here are shared by all host adapters; hence, this
+ * structure is not part of the Adapter structure.
+ */
+static Scb scbs[MAX_SCBS];
+static Scb *scbfree = NULL; /* free list */
+static int freescbs = MAX_SCBS; /* free list counter */
+
+/*
+ * END of data/declarations - code follows.
+ */
+
+
+#ifdef ANY2SCSI_INLINE
+/*
+ Since they're used a lot, I've redone the following from the macros
+ formerly in wd7000.h, hopefully to speed them up by getting rid of
+ all the shifting (it may not matter; GCC might have done as well anyway).
+
+ xany2scsi and xscsi2int were not being used, and are no longer defined.
+ (They were simply 4-byte versions of these routines).
+*/
+
+typedef union { /* let's cheat... */
+ int i;
+ unchar u[sizeof(int)]; /* the sizeof(int) makes it more portable */
+} i_u;
+
+
+static inline void any2scsi( unchar *scsi, int any )
+{
+ *scsi++ = ((i_u) any).u[2];
+ *scsi++ = ((i_u) any).u[1];
+ *scsi++ = ((i_u) any).u[0];
+}
+
+
+static inline int scsi2int( unchar *scsi )
+{
+ i_u result;
+
+ result.i = 0; /* clears unused bytes */
+ *(result.u+2) = *scsi++;
+ *(result.u+1) = *scsi++;
+ *(result.u) = *scsi++;
+ return result.i;
+}
+#else
+/*
+ These are the old ones - I've just moved them here...
+*/
+#undef any2scsi
+#define any2scsi(up, p) \
+(up)[0] = (((unsigned long)(p)) >> 16); \
+(up)[1] = ((unsigned long)(p)) >> 8; \
+(up)[2] = ((unsigned long)(p));
+
+#undef scsi2int
+#define scsi2int(up) ( (((unsigned long)*(up)) << 16) + \
+ (((unsigned long)(up)[1]) << 8) + ((unsigned long)(up)[2]) )
+#endif
+
+
+static inline void wd7000_enable_intr(Adapter *host)
+{
+ host->control |= INT_EN;
+ outb(host->control, host->iobase+ASC_CONTROL);
+}
+
+
+static inline void wd7000_enable_dma(Adapter *host)
+{
+ host->control |= DMA_EN;
+ outb(host->control,host->iobase+ASC_CONTROL);
+ set_dma_mode(host->dma, DMA_MODE_CASCADE);
+ enable_dma(host->dma);
+}
+
+
+#define WAITnexttimeout 200 /* 2 seconds */
+
+#define WAIT(port, mask, allof, noneof) \
+ { register volatile unsigned WAITbits; \
+ register unsigned long WAITtimeout = jiffies + WAITnexttimeout; \
+ while (1) { \
+ WAITbits = inb(port) & (mask); \
+ if ((WAITbits & (allof)) == (allof) && ((WAITbits & (noneof)) == 0)) \
+ break; \
+ if (jiffies > WAITtimeout) goto fail; \
+ } \
+ }
+
+
+static inline void delay( unsigned how_long )
+{
+ register unsigned long time = jiffies + how_long;
+
+ while (jiffies < time);
+}
+
+
+static inline int command_out(Adapter *host, unchar *cmd, int len)
+{
+ WAIT(host->iobase+ASC_STAT,ASC_STATMASK,CMD_RDY,0);
+ while (len--) {
+ do {
+ outb(*cmd, host->iobase+ASC_COMMAND);
+ WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+ } while (inb(host->iobase+ASC_STAT) & CMD_REJ);
+ cmd++;
+ }
+ return 1;
+
+fail:
+ printk("wd7000 command_out: WAIT failed(%d)\n", len+1);
+ return 0;
+}
+
+
+/*
+ * This version of alloc_scbs is in preparation for supporting multiple
+ * commands per lun and command chaining, by queueing pending commands.
+ * We will need to allocate Scbs in blocks since they will wait to be
+ * executed so there is the possibility of deadlock otherwise.
+ * Also, to keep larger requests from being starved by smaller requests,
+ * we limit access to this routine with an internal busy flag, so that
+ * the satisfiability of a request is not dependent on the size of the
+ * request.
+ */
+static inline Scb *alloc_scbs(int needed)
+{
+ register Scb *scb, *p;
+ register unsigned long flags;
+ register unsigned long timeout = jiffies + WAITnexttimeout;
+ register unsigned long now;
+ static int busy = 0;
+ int i;
+
+ if (needed <= 0) return NULL; /* sanity check */
+
+ save_flags(flags);
+ cli();
+ while (busy) { /* someone else is allocating */
+ sti(); /* Yes this is really needed here */
+ now = jiffies; while (jiffies == now) /* wait a jiffy */;
+ cli();
+ }
+ busy = 1; /* not busy now; it's our turn */
+
+ while (freescbs < needed) {
+ timeout = jiffies + WAITnexttimeout;
+ do {
+ sti(); /* Yes this is really needed here */
+ now = jiffies; while (jiffies == now) /* wait a jiffy */;
+ cli();
+ } while (freescbs < needed && jiffies <= timeout);
+ /*
+ * If we get here with enough free Scbs, we can take them.
+ * Otherwise, we timed out and didn't get enough.
+ */
+ if (freescbs < needed) {
+ busy = 0;
+ panic("wd7000: can't get enough free SCBs.\n");
+ restore_flags(flags);
+ return NULL;
+ }
+ }
+ scb = scbfree; freescbs -= needed;
+ for (i = 0; i < needed; i++) { p = scbfree; scbfree = p->next; }
+ p->next = NULL;
+
+ busy = 0; /* we're done */
+
+ restore_flags(flags);
+
+ return scb;
+}
+
+
+static inline void free_scb( Scb *scb )
+{
+ register unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ memset(scb, 0, sizeof(Scb));
+ scb->next = scbfree; scbfree = scb;
+ freescbs++;
+
+ restore_flags(flags);
+}
+
+
+static inline void init_scbs(void)
+{
+ int i;
+ unsigned long flags;
+
+ save_flags(flags);
+ cli();
+
+ scbfree = &(scbs[0]);
+ memset(scbs, 0, sizeof(scbs));
+ for (i = 0; i < MAX_SCBS-1; i++) {
+ scbs[i].next = &(scbs[i+1]); scbs[i].SCpnt = NULL;
+ }
+ scbs[MAX_SCBS-1].next = NULL;
+ scbs[MAX_SCBS-1].SCpnt = NULL;
+
+ restore_flags(flags);
+}
+
+
+static int mail_out( Adapter *host, Scb *scbptr )
+/*
+ * Note: this can also be used for ICBs; just cast to the parm type.
+ */
+{
+ register int i, ogmb;
+ register unsigned long flags;
+ unchar start_ogmb;
+ Mailbox *ogmbs = host->mb.ogmb;
+ int *next_ogmb = &(host->next_ogmb);
+#ifdef DEBUG
+ printk("wd7000 mail_out: %06x",(unsigned int) scbptr);
+#endif
+ /* We first look for a free outgoing mailbox */
+ save_flags(flags);
+ cli();
+ ogmb = *next_ogmb;
+ for (i = 0; i < OGMB_CNT; i++) {
+ if (ogmbs[ogmb].status == 0) {
+#ifdef DEBUG
+ printk(" using OGMB %x",ogmb);
+#endif
+ ogmbs[ogmb].status = 1;
+ any2scsi((unchar *) ogmbs[ogmb].scbptr, (int) scbptr);
+
+ *next_ogmb = (ogmb+1) % OGMB_CNT;
+ break;
+ } else
+ ogmb = (++ogmb) % OGMB_CNT;
+ }
+ restore_flags(flags);
+#ifdef DEBUG
+ printk(", scb is %x",(unsigned int) scbptr);
+#endif
+ if (i >= OGMB_CNT) {
+ /*
+ * Alternatively, we might issue the "interrupt on free OGMB",
+ * and sleep, but it must be ensured that it isn't the init
+ * task running. Instead, this version assumes that the caller
+ * will be persistent, and try again. Since it's the adapter
+ * that marks OGMB's free, waiting even with interrupts off
+ * should work, since they are freed very quickly in most cases.
+ */
+ #ifdef DEBUG
+ printk(", no free OGMBs.\n");
+#endif
+ return 0;
+ }
+
+ wd7000_enable_intr(host);
+
+ start_ogmb = START_OGMB | ogmb;
+ command_out( host, &start_ogmb, 1 );
+#ifdef DEBUG
+ printk(", awaiting interrupt.\n");
+#endif
+ return 1;
+}
+
+
+int make_code(unsigned hosterr, unsigned scsierr)
+{
+#ifdef DEBUG
+ int in_error = hosterr;
+#endif
+
+ switch ((hosterr>>8)&0xff){
+ case 0: /* Reserved */
+ hosterr = DID_ERROR;
+ break;
+ case 1: /* Command Complete, no errors */
+ hosterr = DID_OK;
+ break;
+ case 2: /* Command complete, error logged in scb status (scsierr) */
+ hosterr = DID_OK;
+ break;
+ case 4: /* Command failed to complete - timeout */
+ hosterr = DID_TIME_OUT;
+ break;
+ case 5: /* Command terminated; Bus reset by external device */
+ hosterr = DID_RESET;
+ break;
+ case 6: /* Unexpected Command Received w/ host as target */
+ hosterr = DID_BAD_TARGET;
+ break;
+ case 80: /* Unexpected Reselection */
+ case 81: /* Unexpected Selection */
+ hosterr = DID_BAD_INTR;
+ break;
+ case 82: /* Abort Command Message */
+ hosterr = DID_ABORT;
+ break;
+ case 83: /* SCSI Bus Software Reset */
+ case 84: /* SCSI Bus Hardware Reset */
+ hosterr = DID_RESET;
+ break;
+ default: /* Reserved */
+ hosterr = DID_ERROR;
+ break;
+ }
+#ifdef DEBUG
+ if (scsierr||hosterr)
+ printk("\nSCSI command error: SCSI %02x host %04x return %d",
+ scsierr,in_error,hosterr);
+#endif
+ return scsierr | (hosterr << 16);
+}
+
+
+static void wd7000_scsi_done(Scsi_Cmnd * SCpnt)
+{
+#ifdef DEBUG
+ printk("wd7000_scsi_done: %06x\n",(unsigned int) SCpnt);
+#endif
+ SCpnt->SCp.phase = 0;
+}
+
+
+#define wd7000_intr_ack(host) outb(0,host->iobase+ASC_INTR_ACK)
+
+void wd7000_intr_handle(int irq, struct pt_regs * regs)
+{
+ register int flag, icmb, errstatus, icmb_status;
+ register int host_error, scsi_error;
+ register Scb *scb; /* for SCSI commands */
+ register IcbAny *icb; /* for host commands */
+ register Scsi_Cmnd *SCpnt;
+ Adapter *host = irq2host[irq]; /* This MUST be set!!! */
+ Mailbox *icmbs = host->mb.icmb;
+
+#ifdef DEBUG
+ printk("wd7000_intr_handle: irq = %d, host = %06x\n", irq, host);
+#endif
+
+ flag = inb(host->iobase+ASC_INTR_STAT);
+#ifdef DEBUG
+ printk("wd7000_intr_handle: intr stat = %02x\n",flag);
+#endif
+
+ if (!(inb(host->iobase+ASC_STAT) & INT_IM)) {
+ /* NB: these are _very_ possible if IRQ 15 is being used, since
+ it's the "garbage collector" on the 2nd 8259 PIC. Specifically,
+ any interrupt signal into the 8259 which can't be identified
+ comes out as 7 from the 8259, which is 15 to the host. Thus, it
+ is a good thing the WD7000 has an interrupt status port, so we
+ can sort these out. Otherwise, electrical noise and other such
+ problems would be indistinguishable from valid interrupts...
+ */
+#ifdef DEBUG
+ printk("wd7000_intr_handle: phantom interrupt...\n");
+#endif
+ wd7000_intr_ack(host);
+ return;
+ }
+
+ if (flag & MB_INTR) {
+ /* The interrupt is for a mailbox */
+ if (!(flag & IMB_INTR)) {
+#ifdef DEBUG
+ printk("wd7000_intr_handle: free outgoing mailbox");
+#endif
+ /*
+ * If sleep_on() and the "interrupt on free OGMB" command are
+ * used in mail_out(), wake_up() should correspondingly be called
+ * here. For now, we don't need to do anything special.
+ */
+ wd7000_intr_ack(host);
+ return;
+ } else {
+ /* The interrupt is for an incoming mailbox */
+ icmb = flag & MB_MASK;
+ icmb_status = icmbs[icmb].status;
+ if (icmb_status & 0x80) { /* unsolicited - result in ICMB */
+#ifdef DEBUG
+ printk("wd7000_intr_handle: unsolicited interrupt %02xh\n",
+ icmb_status);
+#endif
+ wd7000_intr_ack(host);
+ return;
+ }
+ scb = (struct scb *) scsi2int((unchar *)icmbs[icmb].scbptr);
+ icmbs[icmb].status = 0;
+ if (!(scb->op & ICB_OP_MASK)) { /* an SCB is done */
+ SCpnt = scb->SCpnt;
+ if (--(SCpnt->SCp.phase) <= 0) { /* all scbs are done */
+ host_error = scb->vue | (icmb_status << 8);
+ scsi_error = scb->status;
+ errstatus = make_code(host_error,scsi_error);
+ SCpnt->result = errstatus;
+
+ free_scb(scb);
+
+ SCpnt->scsi_done(SCpnt);
+ }
+ } else { /* an ICB is done */
+ icb = (IcbAny *) scb;
+ icb->status = icmb_status;
+ icb->phase = 0;
+ }
+ } /* incoming mailbox */
+ }
+
+ wd7000_intr_ack(host);
+ return;
+}
+
+
+int wd7000_queuecommand(Scsi_Cmnd * SCpnt, void (*done)(Scsi_Cmnd *))
+{
+ register Scb *scb;
+ register Sgb *sgb;
+ register unchar *cdb = (unchar *) SCpnt->cmnd;
+ register unchar idlun;
+ register short cdblen;
+ Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+ cdblen = SCpnt->cmd_len;
+ idlun = ((SCpnt->target << 5) & 0xe0) | (SCpnt->lun & 7);
+ SCpnt->scsi_done = done;
+ SCpnt->SCp.phase = 1;
+ scb = alloc_scbs(1);
+ scb->idlun = idlun;
+ memcpy(scb->cdb, cdb, cdblen);
+ scb->direc = 0x40; /* Disable direction check */
+
+ scb->SCpnt = SCpnt; /* so we can find stuff later */
+ SCpnt->host_scribble = (unchar *) scb;
+ scb->host = host;
+
+ if (SCpnt->use_sg) {
+ struct scatterlist *sg = (struct scatterlist *) SCpnt->request_buffer;
+ unsigned i;
+
+ if (SCpnt->host->sg_tablesize == SG_NONE) {
+ panic("wd7000_queuecommand: scatter/gather not supported.\n");
+ }
+#ifdef DEBUG
+ printk("Using scatter/gather with %d elements.\n",SCpnt->use_sg);
+#endif
+
+ sgb = scb->sgb;
+ scb->op = 1;
+ any2scsi(scb->dataptr, (int) sgb);
+ any2scsi(scb->maxlen, SCpnt->use_sg * sizeof (Sgb) );
+
+ for (i = 0; i < SCpnt->use_sg; i++) {
+ any2scsi(sgb[i].ptr, (int) sg[i].address);
+ any2scsi(sgb[i].len, sg[i].length);
+ }
+ } else {
+ scb->op = 0;
+ any2scsi(scb->dataptr, (int) SCpnt->request_buffer);
+ any2scsi(scb->maxlen, SCpnt->request_bufflen);
+ }
+ while (!mail_out(host, scb)) /* keep trying */;
+
+ return 1;
+}
+
+
+int wd7000_command(Scsi_Cmnd *SCpnt)
+{
+ wd7000_queuecommand(SCpnt, wd7000_scsi_done);
+
+ while (SCpnt->SCp.phase > 0) barrier(); /* phase counts scbs down to 0 */
+
+ return SCpnt->result;
+}
+
+
+int wd7000_diagnostics( Adapter *host, int code )
+{
+ static IcbDiag icb = {ICB_OP_DIAGNOSTICS};
+ static unchar buf[256];
+ unsigned long timeout;
+
+ icb.type = code;
+ any2scsi(icb.len, sizeof(buf));
+ any2scsi(icb.ptr, (int) &buf);
+ icb.phase = 1;
+ /*
+ * This routine is only called at init, so there should be OGMBs
+ * available. I'm assuming so here. If this is going to
+ * fail, I can just let the timeout catch the failure.
+ */
+ mail_out(host, (struct scb *) &icb);
+ timeout = jiffies + WAITnexttimeout; /* wait up to 2 seconds */
+ while (icb.phase && jiffies < timeout)
+ barrier(); /* wait for completion */
+
+ if (icb.phase) {
+ printk("wd7000_diagnostics: timed out.\n");
+ return 0;
+ }
+ if (make_code(icb.vue|(icb.status << 8),0)) {
+ printk("wd7000_diagnostics: failed (%02x,%02x)\n",
+ icb.vue, icb.status);
+ return 0;
+ }
+
+ return 1;
+}
+
+
+int wd7000_init( Adapter *host )
+{
+ InitCmd init_cmd = {
+ INITIALIZATION, 7, BUS_ON, BUS_OFF, 0, {0,0,0}, OGMB_CNT, ICMB_CNT
+ };
+ int diag;
+
+ /*
+ Reset the adapter - only. The SCSI bus was initialized at power-up,
+ and we need to do this just so we control the mailboxes, etc.
+ */
+ outb(ASC_RES, host->iobase+ASC_CONTROL);
+ delay(1); /* reset pulse: this is 10ms, only need 25us */
+ outb(0,host->iobase+ASC_CONTROL);
+ host->control = 0; /* this must always shadow ASC_CONTROL */
+ WAIT(host->iobase+ASC_STAT, ASC_STATMASK, CMD_RDY, 0);
+
+ if ((diag = inb(host->iobase+ASC_INTR_STAT)) != 1) {
+ printk("wd7000_init: ");
+ switch (diag) {
+ case 2:
+ printk("RAM failure.\n");
+ break;
+ case 3:
+ printk("FIFO R/W failed\n");
+ break;
+ case 4:
+ printk("SBIC register R/W failed\n");
+ break;
+ case 5:
+ printk("Initialization D-FF failed.\n");
+ break;
+ case 6:
+ printk("Host IRQ D-FF failed.\n");
+ break;
+ case 7:
+ printk("ROM checksum error.\n");
+ break;
+ default:
+ printk("diagnostic code %02Xh received.\n", diag);
+ break;
+ }
+ return 0;
+ }
+
+ /* Clear mailboxes */
+ memset(&(host->mb), 0, sizeof(host->mb));
+
+ /* Execute init command */
+ any2scsi((unchar *) &(init_cmd.mailboxes), (int) &(host->mb));
+ if (!command_out(host, (unchar *) &init_cmd, sizeof(init_cmd))) {
+ printk("wd7000_init: adapter initialization failed.\n");
+ return 0;
+ }
+ WAIT(host->iobase+ASC_STAT, ASC_STATMASK, ASC_INIT, 0);
+
+ if (request_irq(host->irq, wd7000_intr_handle, SA_INTERRUPT, "wd7000")) {
+ printk("wd7000_init: can't get IRQ %d.\n", host->irq);
+ return 0;
+ }
+ if (request_dma(host->dma,"wd7000")) {
+ printk("wd7000_init: can't get DMA channel %d.\n", host->dma);
+ free_irq(host->irq);
+ return 0;
+ }
+ wd7000_enable_dma(host);
+ wd7000_enable_intr(host);
+
+ if (!wd7000_diagnostics(host,ICB_DIAG_FULL)) {
+ free_dma(host->dma);
+ free_irq(host->irq);
+ return 0;
+ }
+
+ return 1;
+
+ fail:
+ printk("wd7000_init: WAIT timed out.\n");
+ return 0; /* 0 = not ok */
+}
+
+
+void wd7000_revision(Adapter *host)
+{
+ static IcbRevLvl icb = {ICB_OP_GET_REVISION};
+
+ icb.phase = 1;
+ /*
+ * Like diagnostics, this is only done at init time, in fact, from
+ * wd7000_detect, so there should be OGMBs available. If it fails,
+ * the only damage will be that the revision will show up as 0.0,
+ * which in turn means that scatter/gather will be disabled.
+ */
+ mail_out(host, (struct scb *) &icb);
+ while (icb.phase)
+ barrier(); /* wait for completion */
+ host->rev1 = icb.primary;
+ host->rev2 = icb.secondary;
+}
+
+
+int wd7000_detect(Scsi_Host_Template * tpnt)
+/*
+ * Returns the number of adapters this driver is supporting.
+ *
+ * The source for hosts.c says to wait to call scsi_register until 100%
+ * sure about an adapter. We need to do it a little sooner here; we
+ * need the storage set up by scsi_register before wd7000_init, and
+ * changing the location of an Adapter structure is more trouble than
+ * calling scsi_unregister.
+ *
+ */
+{
+ int i,j, present = 0;
+ const Config *cfg;
+ const Signature *sig;
+ Adapter *host = NULL;
+ struct Scsi_Host *sh;
+
+ tpnt->proc_dir = &proc_scsi_wd7000;
+
+ /* Set up SCB free list, which is shared by all adapters */
+ init_scbs();
+
+ cfg = configs;
+ for (i = 0; i < NUM_CONFIGS; i++) {
+ sig = signatures;
+ for (j = 0; j < NUM_SIGNATURES; j++) {
+ if (!memcmp(cfg->bios+sig->ofs, sig->sig, sig->len)) {
+ /* matched this one */
+#ifdef DEBUG
+ printk("WD-7000 SST BIOS detected at %04X: checking...\n",
+ (int) cfg->bios);
+#endif
+ /*
+ * We won't explicitly test the configuration (in this
+ * version); instead, we'll just see if it works to
+ * setup the adapter; if it does, we'll use it.
+ */
+ if (check_region(cfg->iobase, 4)) { /* ports in use */
+ printk("IO %xh already in use.\n", host->iobase);
+ continue;
+ }
+ /*
+ * We register here, to get a pointer to the extra space,
+ * which we'll use as the Adapter structure (host) for
+ * this adapter. It is located just after the registered
+ * Scsi_Host structure (sh), and is located by the empty
+ * array hostdata.
+ */
+ sh = scsi_register(tpnt, sizeof(Adapter) );
+ host = (Adapter *) sh->hostdata;
+#ifdef DEBUG
+ printk("wd7000_detect: adapter allocated at %06x\n",
+ (int)host);
+#endif
+ memset( host, 0, sizeof(Adapter) );
+ host->sh = sh;
+ host->irq = cfg->irq;
+ host->iobase = cfg->iobase;
+ host->dma = cfg->dma;
+ irq2host[host->irq] = host;
+
+ if (!wd7000_init(host)) { /* Initialization failed */
+ scsi_unregister (sh);
+ continue;
+ }
+
+ /*
+ * OK from here - we'll use this adapter/configuration.
+ */
+ wd7000_revision(host); /* important for scatter/gather */
+
+ printk("Western Digital WD-7000 (%d.%d) ",
+ host->rev1, host->rev2);
+ printk("using IO %xh IRQ %d DMA %d.\n",
+ host->iobase, host->irq, host->dma);
+
+ request_region(host->iobase, 4,"wd7000"); /* Register our ports */
+ /*
+ * For boards before rev 6.0, scatter/gather isn't supported.
+ */
+ if (host->rev1 < 6) sh->sg_tablesize = SG_NONE;
+
+ present++; /* count it */
+ break; /* don't try any more sigs */
+ }
+ sig++; /* try next signature with this configuration */
+ }
+ cfg++; /* try next configuration */
+ }
+
+ return present;
+}
+
+
+/*
+ * I have absolutely NO idea how to do an abort with the WD7000...
+ */
+int wd7000_abort(Scsi_Cmnd * SCpnt)
+{
+ Adapter *host = (Adapter *) SCpnt->host->hostdata;
+
+ if (inb(host->iobase+ASC_STAT) & INT_IM) {
+ printk("wd7000_abort: lost interrupt\n");
+ wd7000_intr_handle(host->irq, NULL);
+ return SCSI_ABORT_SUCCESS;
+ }
+
+ return SCSI_ABORT_SNOOZE;
+}
+
+
+/*
+ * I also have no idea how to do a reset...
+ */
+int wd7000_reset(Scsi_Cmnd * SCpnt)
+{
+ return SCSI_RESET_PUNT;
+}
+
+
+/*
+ * This was borrowed directly from aha1542.c, but my disks are organized
+ * this way, so I think it will work OK. Someone who is ambitious can
+ * borrow a newer or more complete version from another driver.
+ */
+int wd7000_biosparam(Disk * disk, kdev_t dev, int* ip)
+{
+ int size = disk->capacity;
+ ip[0] = 64;
+ ip[1] = 32;
+ ip[2] = size >> 11;
+/* if (ip[2] >= 1024) ip[2] = 1024; */
+ return 0;
+}
+
+#ifdef MODULE
+/* Eventually this will go into an include file, but this will be later */
+Scsi_Host_Template driver_template = WD7000;
+
+#include "scsi_module.c"
+#endif
diff --git a/i386/i386at/gpl/linux/scsi/wd7000.h b/i386/i386at/gpl/linux/scsi/wd7000.h
new file mode 100644
index 00000000..5a194dbc
--- /dev/null
+++ b/i386/i386at/gpl/linux/scsi/wd7000.h
@@ -0,0 +1,55 @@
+#ifndef _WD7000_H
+
+/* $Id: wd7000.h,v 1.1.1.1 1997/02/25 21:27:53 thomas Exp $
+ *
+ * Header file for the WD-7000 driver for Linux
+ *
+ * John Boyd <boyd@cis.ohio-state.edu> Jan 1994:
+ * This file has been reduced to only the definitions needed for the
+ * WD7000 host structure.
+ *
+ */
+
+#include <linux/types.h>
+#include <linux/kdev_t.h>
+
+int wd7000_detect(Scsi_Host_Template *);
+int wd7000_command(Scsi_Cmnd *);
+int wd7000_queuecommand(Scsi_Cmnd *, void (*done)(Scsi_Cmnd *));
+int wd7000_abort(Scsi_Cmnd *);
+int wd7000_reset(Scsi_Cmnd *);
+int wd7000_biosparam(Disk *, kdev_t, int *);
+
+#ifndef NULL
+#define NULL 0L
+#endif
+
+/*
+ * In this version, sg_tablesize now defaults to WD7000_SG, and will
+ * be set to SG_NONE for older boards. This is the reverse of the
+ * previous default, and was changed so that the driver-level
+ * Scsi_Host_Template would reflect the driver's support for scatter/
+ * gather.
+ *
+ * Also, it has been reported that boards at Revision 6 support scatter/
+ * gather, so the new definition of an "older" board has been changed
+ * accordingly.
+ */
+#define WD7000_Q 16
+#define WD7000_SG 16
+
+#define WD7000 { NULL, NULL, \
+ NULL, \
+ NULL, \
+ "Western Digital WD-7000", \
+ wd7000_detect, \
+ NULL, \
+ NULL, \
+ wd7000_command, \
+ wd7000_queuecommand, \
+ wd7000_abort, \
+ wd7000_reset, \
+ NULL, \
+ wd7000_biosparam, \
+ WD7000_Q, 7, WD7000_SG, 1, 0, 1, ENABLE_CLUSTERING}
+#endif
diff --git a/i386/i386at/i386at_ds_routines.c b/i386/i386at/i386at_ds_routines.c
new file mode 100644
index 00000000..b1375afd
--- /dev/null
+++ b/i386/i386at/i386at_ds_routines.c
@@ -0,0 +1,270 @@
+/*
+ * Mach device server routines (i386at version).
+ *
+ * Copyright (c) 1996 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Shantanu Goel, University of Utah CSL
+ */
+
+#include <mach/boolean.h>
+#include <mach/kern_return.h>
+#include <mach/mig_errors.h>
+#include <mach/port.h>
+#include <mach/notify.h>
+
+#include <device/device_types.h>
+#include <device/device_port.h>
+#include "device_interface.h"
+
+#include <i386at/dev_hdr.h>
+#include <i386at/device_emul.h>
+
+extern struct device_emulation_ops mach_device_emulation_ops;
+#ifdef LINUX_DEV
+extern struct device_emulation_ops linux_block_emulation_ops;
+extern struct device_emulation_ops linux_net_emulation_ops;
+#endif
+
+/* List of emulations. */
+static struct device_emulation_ops *emulation_list[] =
+{
+#ifdef LINUX_DEV
+ &linux_block_emulation_ops,
+ &linux_net_emulation_ops,
+#endif
+ &mach_device_emulation_ops,
+};
+
+#define NUM_EMULATION (sizeof (emulation_list) / sizeof (emulation_list[0]))
+
+io_return_t
+ds_device_open (ipc_port_t open_port, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ char *name, device_t *devp)
+{
+ int i;
+ device_t dev;
+ io_return_t err;
+
+ /* Open must be called on the master device port. */
+ if (open_port != master_device_port)
+ return D_INVALID_OPERATION;
+
+ /* There must be a reply port. */
+ if (! IP_VALID (reply_port))
+ {
+ printf ("ds_* invalid reply port\n");
+ Debugger ("ds_* reply_port");
+ return MIG_NO_REPLY;
+ }
+
+ /* Call each emulation's open routine to find the device. */
+ for (i = 0; i < NUM_EMULATION; i++)
+ {
+ err = (*emulation_list[i]->open) (reply_port, reply_port_type,
+ mode, name, devp);
+ if (err != D_NO_SUCH_DEVICE)
+ break;
+ }
+
+ return err;
+}
+
+io_return_t
+ds_device_close (device_t dev)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ return (dev->emul_ops->close
+ ? (*dev->emul_ops->close) (dev->emul_data)
+ : D_SUCCESS);
+}
+
+io_return_t
+ds_device_write (device_t dev, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ recnum_t recnum, io_buf_ptr_t data, unsigned int count,
+ int *bytes_written)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! data)
+ return D_INVALID_SIZE;
+ if (! dev->emul_ops->write)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->write) (dev->emul_data, reply_port,
+ reply_port_type, mode, recnum,
+ data, count, bytes_written);
+}
+
+io_return_t
+ds_device_write_inband (device_t dev, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type,
+ dev_mode_t mode, recnum_t recnum,
+ io_buf_ptr_inband_t data, unsigned count,
+ int *bytes_written)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! data)
+ return D_INVALID_SIZE;
+ if (! dev->emul_ops->write_inband)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->write_inband) (dev->emul_data, reply_port,
+ reply_port_type, mode, recnum,
+ data, count, bytes_written);
+}
+
+io_return_t
+ds_device_read (device_t dev, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ recnum_t recnum, int count, io_buf_ptr_t *data,
+ unsigned *bytes_read)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->read)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->read) (dev->emul_data, reply_port,
+ reply_port_type, mode, recnum,
+ count, data, bytes_read);
+}
+
+io_return_t
+ds_device_read_inband (device_t dev, ipc_port_t reply_port,
+ mach_msg_type_name_t reply_port_type, dev_mode_t mode,
+ recnum_t recnum, int count, char *data,
+ unsigned *bytes_read)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->read_inband)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->read_inband) (dev->emul_data, reply_port,
+ reply_port_type, mode, recnum,
+ count, data, bytes_read);
+}
+
+io_return_t
+ds_device_set_status (device_t dev, dev_flavor_t flavor,
+ dev_status_t status, mach_msg_type_number_t status_count)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->set_status)
+ return D_INVALID_OPERATION;
+
+ return (*dev->emul_ops->set_status) (dev->emul_data, flavor, status,
+ status_count);
+}
+
+io_return_t
+ds_device_get_status (device_t dev, dev_flavor_t flavor, dev_status_t status,
+ mach_msg_type_number_t *status_count)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->get_status)
+ return D_INVALID_OPERATION;
+
+ return (*dev->emul_ops->get_status) (dev->emul_data, flavor, status,
+ status_count);
+}
+
+io_return_t
+ds_device_set_filter (device_t dev, ipc_port_t receive_port, int priority,
+ filter_t *filter, unsigned filter_count)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->set_filter)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->set_filter) (dev->emul_data, receive_port,
+ priority, filter, filter_count);
+}
+
+io_return_t
+ds_device_map (device_t dev, vm_prot_t prot, vm_offset_t offset,
+ vm_size_t size, ipc_port_t *pager, boolean_t unmap)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->map)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->map) (dev->emul_data, prot,
+ offset, size, pager, unmap);
+}
+
+boolean_t
+ds_notify (mach_msg_header_t *msg)
+{
+ if (msg->msgh_id == MACH_NOTIFY_NO_SENDERS)
+ {
+ device_t dev;
+ mach_no_senders_notification_t *ns;
+
+ ns = (mach_no_senders_notification_t *) msg;
+ dev = (device_t) ns->not_header.msgh_remote_port;
+ if (dev->emul_ops->no_senders)
+ (*dev->emul_ops->no_senders) (ns);
+ return TRUE;
+ }
+
+ printf ("ds_notify: strange notification %d\n", msg->msgh_id);
+ return FALSE;
+}
+
+io_return_t
+ds_device_write_trap (device_t dev, dev_mode_t mode,
+ recnum_t recnum, vm_offset_t data, vm_size_t count)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->write_trap)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->write_trap) (dev->emul_data,
+ mode, recnum, data, count);
+}
+
+io_return_t
+ds_device_writev_trap (device_t dev, dev_mode_t mode,
+ recnum_t recnum, io_buf_vec_t *iovec, vm_size_t count)
+{
+ if (dev == DEVICE_NULL)
+ return D_NO_SUCH_DEVICE;
+ if (! dev->emul_ops->writev_trap)
+ return D_INVALID_OPERATION;
+ return (*dev->emul_ops->writev_trap) (dev->emul_data,
+ mode, recnum, iovec, count);
+}
+
+void
+device_reference (device_t dev)
+{
+ if (dev->emul_ops->reference)
+ (*dev->emul_ops->reference) (dev->emul_data);
+}
+
+void
+device_deallocate (device_t dev)
+{
+ if (dev->emul_ops->dealloc)
+ (*dev->emul_ops->dealloc) (dev->emul_data);
+}
diff --git a/i386/i386at/i8250.h b/i386/i386at/i8250.h
new file mode 100644
index 00000000..fa81173e
--- /dev/null
+++ b/i386/i386at/i8250.h
@@ -0,0 +1,129 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Header file for i8250 chip
+ */
+
+/* port offsets from the base i/o address */
+
+#define RDAT 0
+#define RIE 1
+#define RID 2
+#define RFC 2
+#define RLC 3
+#define RMC 4
+#define RLS 5
+#define RMS 6
+#define RDLSB 0
+#define RDMSB 1
+
+/* interrupt control register */
+
+#define IERD 0x01 /* read int */
+#define IETX 0x02 /* xmit int */
+#define IELS 0x04 /* line status int */
+#define IEMS 0x08 /* modem int */
+
+/* interrupt status register */
+
+#define IDIP 0x01 /* not interrupt pending */
+#define IDMS 0x00 /* modem int */
+#define IDTX 0x02 /* xmit int */
+#define IDRD 0x04 /* read int */
+#define IDLS 0x06 /* line status int */
+#define IDMASK 0x0f /* interrupt ID mask */
+
+/* line control register */
+
+#define LC5 0x00 /* word length 5 */
+#define LC6 0x01 /* word length 6 */
+#define LC7 0x02 /* word length 7 */
+#define LC8 0x03 /* word length 8 */
+#define LCSTB 0x04 /* 2 stop */
+#define LCPEN 0x08 /* parity enable */
+#define LCEPS 0x10 /* even parity select */
+#define LCSP 0x20 /* stick parity */
+#define LCBRK 0x40 /* send break */
+#define LCDLAB 0x80 /* divisor latch access bit */
+#define LCPAR 0x38 /* parity mask */
+
+/* line status register */
+
+#define LSDR 0x01 /* data ready */
+#define LSOR 0x02 /* overrun error */
+#define LSPE 0x04 /* parity error */
+#define LSFE 0x08 /* framing error */
+#define LSBI 0x10 /* break interrupt */
+#define LSTHRE 0x20 /* xmit holding reg empty */
+#define LSTSRE 0x40 /* xmit shift reg empty */
+
+/* modem control register */
+
+#define MCDTR 0x01 /* DTR */
+#define MCRTS 0x02 /* RTS */
+#define MCOUT1 0x04 /* OUT1 */
+#define MCOUT2 0x08 /* OUT2 */
+#define MCLOOP 0x10 /* loopback */
+
+/* modem status register */
+
+#define MSDCTS 0x01 /* delta CTS */
+#define MSDDSR 0x02 /* delta DSR */
+#define MSTERI 0x04 /* delta RE */
+#define MSDRLSD 0x08 /* delta CD */
+#define MSCTS 0x10 /* CTS */
+#define MSDSR 0x20 /* DSR */
+#define MSRI 0x40 /* RE */
+#define MSRLSD 0x80 /* CD */
+
+/* divisor latch register settings for various baud rates */
+
+#define BCNT1200 0x60
+#define BCNT2400 0x30
+#define BCNT4800 0x18
+#define BCNT9600 0x0c
diff --git a/i386/i386at/i82586.h b/i386/i386at/i82586.h
new file mode 100644
index 00000000..fd205897
--- /dev/null
+++ b/i386/i386at/i82586.h
@@ -0,0 +1,264 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Defines for managing the status word of the 82586 cpu. For details see
+ * the Intel LAN Component User's Manual starting at p. 2-14.
+ *
+ */
+
+#define SCB_SW_INT 0xf000
+#define SCB_SW_CX 0x8000 /* CU finished w/ int. bit set */
+#define SCB_SW_FR 0x4000 /* RU finished receiving a frame */
+#define SCB_SW_CNA 0x2000 /* CU left active state */
+#define SCB_SW_RNR 0x1000 /* RU left ready state */
+
+/*
+ * Defines for managing the Command Unit Status portion of the 82586
+ * System Control Block.
+ *
+ */
+
+#define SCB_CUS_IDLE 0x0000
+#define SCB_CUS_SUSPND 0x0100
+#define SCB_CUS_ACTV 0x0200
+
+/*
+ * Defines for managing the Receive Unit Status portion of the System
+ * Control Block.
+ *
+ */
+
+#define SCB_RUS_IDLE 0x0000
+#define SCB_RUS_SUSPND 0x0010
+#define SCB_RUS_NORESRC 0x0020
+#define SCB_RUS_READY 0x0040
+
+/*
+ * Defines that manage portions of the Command Word in the System Control
+ * Block of the 82586. Below are the Interrupt Acknowledge Bits and their
+ * appropriate masks.
+ *
+ */
+
+#define SCB_ACK_CX 0x8000
+#define SCB_ACK_FR 0x4000
+#define SCB_ACK_CNA 0x2000
+#define SCB_ACK_RNR 0x1000
+
+/*
+ * Defines for managing the Command Unit Control word, and the Receive
+ * Unit Control word. The software RESET bit is also defined.
+ *
+ */
+
+#define SCB_CU_STRT 0x0100
+#define SCB_CU_RSUM 0x0200
+#define SCB_CU_SUSPND 0x0300
+#define SCB_CU_ABRT 0x0400
+
+#define SCB_RESET 0x0080
+
+#define SCB_RU_STRT 0x0010
+#define SCB_RU_RSUM 0x0020
+#define SCB_RU_SUSPND 0x0030
+#define SCB_RU_ABRT 0x0040
+
+
+/*
+ * The following define Action Commands for the 82586 chip.
+ *
+ */
+
+#define AC_NOP 0x00
+#define AC_IASETUP 0x01
+#define AC_CONFIGURE 0x02
+#define AC_MCSETUP 0x03
+#define AC_TRANSMIT 0x04
+#define AC_TDR 0x05
+#define AC_DUMP 0x06
+#define AC_DIAGNOSE 0x07
+
+
+/*
+ * Defines for General Format for Action Commands, both Status Words, and
+ * Command Words.
+ *
+ */
+
+#define AC_SW_C 0x8000
+#define AC_SW_B 0x4000
+#define AC_SW_OK 0x2000
+#define AC_SW_A 0x1000
+#define TC_CARRIER 0x0400
+#define TC_CLS 0x0200
+#define TC_DMA 0x0100
+#define TC_DEFER 0x0080
+#define TC_SQE 0x0040
+#define TC_COLLISION 0x0020
+#define AC_CW_EL 0x8000
+#define AC_CW_S 0x4000
+#define AC_CW_I 0x2000
+
+/*
+ * Specific defines for the transmit action command.
+ *
+ */
+
+#define TBD_SW_EOF 0x8000
+#define TBD_SW_COUNT 0x3fff
+
+/*
+ * Specific defines for the receive frame actions.
+ *
+ */
+
+#define RBD_SW_EOF 0x8000
+#define RBD_SW_COUNT 0x3fff
+
+#define RFD_DONE 0x8000
+#define RFD_BUSY 0x4000
+#define RFD_OK 0x2000
+#define RFD_CRC 0x0800
+#define RFD_ALN 0x0400
+#define RFD_RSC 0x0200
+#define RFD_DMA 0x0100
+#define RFD_SHORT 0x0080
+#define RFD_EOF 0x0040
+#define RFD_EL 0x8000
+#define RFD_SUSP 0x4000
+/*
+ * 82586 chip specific structure definitions. For details, see the Intel
+ * LAN Components manual.
+ *
+ */
+
+
+typedef struct {
+ u_short scp_sysbus;
+ u_short scp_unused[2];
+ u_short scp_iscp;
+ u_short scp_iscp_base;
+} scp_t;
+
+
+typedef struct {
+ u_short iscp_busy;
+ u_short iscp_scb_offset;
+ u_short iscp_scb;
+ u_short iscp_scb_base;
+} iscp_t;
+
+
+typedef struct {
+ u_short scb_status;
+ u_short scb_command;
+ u_short scb_cbl_offset;
+ u_short scb_rfa_offset;
+ u_short scb_crcerrs;
+ u_short scb_alnerrs;
+ u_short scb_rscerrs;
+ u_short scb_ovrnerrs;
+} scb_t;
+
+
+typedef struct {
+ u_short tbd_offset;
+ u_char dest_addr[6];
+ u_short length;
+} transmit_t;
+
+
+typedef struct {
+ u_short fifolim_bytecnt;
+ u_short addrlen_mode;
+ u_short linprio_interframe;
+ u_short slot_time;
+ u_short hardware;
+ u_short min_frame_len;
+} configure_t;
+
+
+typedef struct {
+ u_short ac_status;
+ u_short ac_command;
+ u_short ac_link_offset;
+ union {
+ transmit_t transmit;
+ configure_t configure;
+ u_char iasetup[6];
+ } cmd;
+} ac_t;
+
+
+typedef struct {
+ u_short act_count;
+ u_short next_tbd_offset;
+ u_short buffer_addr;
+ u_short buffer_base;
+} tbd_t;
+
+
+typedef struct {
+ u_short status;
+ u_short command;
+ u_short link_offset;
+ u_short rbd_offset;
+ u_char destination[6];
+ u_char source[6];
+ u_short length;
+} fd_t;
+
+
+typedef struct {
+ u_short status;
+ u_short next_rbd_offset;
+ u_short buffer_addr;
+ u_short buffer_base;
+ u_short size;
+} rbd_t;
diff --git a/i386/i386at/idt.h b/i386/i386at/idt.h
new file mode 100644
index 00000000..7903310b
--- /dev/null
+++ b/i386/i386at/idt.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+
+#ifndef _I386AT_IDT_
+#define _I386AT_IDT_
+
+/* On a standard PC, we only need 16 interrupt vectors,
+ because that's all the PIC hardware supports. */
+/* XX But for some reason we program the PIC
+ to use vectors 0x40-0x4f rather than 0x20-0x2f. Fix. */
+#define IDTSZ (0x20+0x20+0x10)
+
+#define PIC_INT_BASE 0x40
+
+#include "idt-gen.h"
+
+#endif _I386AT_IDT_
diff --git a/i386/i386at/if_3c501.c b/i386/i386at/if_3c501.c
new file mode 100644
index 00000000..b822d273
--- /dev/null
+++ b/i386/i386at/if_3c501.c
@@ -0,0 +1,1240 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: if_3c501.c
+ * Author: Philippe Bernadat
+ * Date: 1989
+ * Copyright (c) 1989 OSF Research Institute
+ *
+ * 3COM Etherlink 3C501 Mach Ethernet drvier
+ */
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <at3c501.h>
+
+#ifdef MACH_KERNEL
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#else MACH_KERNEL
+#include <sys/param.h>
+#include <mach/machine/vm_param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/vmmac.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+#endif MACH_KERNEL
+
+#include <i386/ipl.h>
+#include <chips/busses.h>
+#include <i386at/if_3c501.h>
+
+#define SPLNET spl6
+
+int at3c501probe();
+void at3c501attach();
+int at3c501intr();
+int at3c501init();
+int at3c501output();
+int at3c501ioctl();
+int at3c501reset();
+int at3c501watch();
+
+static vm_offset_t at3c501_std[NAT3C501] = { 0 };
+static struct bus_device *at3c501_info[NAT3C501];
+struct bus_driver at3c501driver =
+ {at3c501probe, 0, at3c501attach, 0, at3c501_std, "et", at3c501_info, };
+
+int watchdog_id;
+
+typedef struct {
+#ifdef MACH_KERNEL
+ struct ifnet ds_if; /* generic interface header */
+ u_char ds_addr[6]; /* Ethernet hardware address */
+#else MACH_KERNEL
+ struct arpcom at3c501_ac;
+#define ds_if at3c501_ac.ac_if
+#define ds_addr at3c501_ac.ac_enaddr
+#endif MACH_KERNEL
+ int flags;
+ int timer;
+ char *base;
+ u_char address[ETHER_ADD_SIZE];
+ short mode;
+ int badxmt;
+ int badrcv;
+ int spurious;
+ int rcv;
+ int xmt;
+} at3c501_softc_t;
+
+at3c501_softc_t at3c501_softc[NAT3C501];
+
+/*
+ * at3c501probe:
+ *
+ * This function "probes" or checks for the 3c501 board on the bus to see
+ * if it is there. As far as I can tell, the best break between this
+ * routine and the attach code is to simply determine whether the board
+ * is configured in properly. Currently my approach to this is to write
+ * and read a string from the Packet Buffer on the board being probed.
+ * If the string comes back properly then we assume the board is there.
+ * The config code expects to see a successful return from the probe
+ * routine before attach will be called.
+ *
+ * input : address device is mapped to, and unit # being checked
+ * output : a '1' is returned if the board exists, and a 0 otherwise
+ *
+ */
+at3c501probe(port, dev)
+struct bus_device *dev;
+{
+ caddr_t base = (caddr_t)dev->address;
+ int unit = dev->unit;
+ char inbuf[50];
+ char *str = "3c501 ethernet board %d out of range\n";
+ int strsize = strlen(str);
+
+ if ((unit < 0) || (unit >= NAT3C501)) {
+ printf(str, unit);
+ return(0);
+ }
+
+ /* reset */
+ outb(IE_CSR(base), IE_RESET);
+
+ /* write a string to the packet buffer */
+
+ outb(IE_CSR(base), IE_RIDE | IE_SYSBFR);
+ outw(IE_GP(base), 0);
+ loutb(IE_BFR(base), str, strsize);
+
+ /* read it back */
+
+ outb(IE_CSR(base), IE_RIDE | IE_SYSBFR);
+ outw(IE_GP(base), 0);
+ linb(IE_BFR(base), inbuf, strsize);
+ /* compare them */
+
+#ifdef MACH_KERNEL
+ if (strncmp(str, inbuf, strsize))
+#else MACH_KERNEL
+ if (bcmp(str, inbuf, strsize))
+#endif MACH_KERNEL
+ {
+ return(0);
+ }
+ at3c501_softc[unit].base = base;
+
+ return(1);
+}
+
+/*
+ * at3c501attach:
+ *
+ * This function attaches a 3C501 board to the "system". The rest of
+ * runtime structures are initialized here (this routine is called after
+ * a successful probe of the board). Once the ethernet address is read
+ * and stored, the board's ifnet structure is attached and readied.
+ *
+ * input : bus_device structure setup in autoconfig
+ * output : board structs and ifnet is setup
+ *
+ */
+void at3c501attach(dev)
+struct bus_device *dev;
+{
+ at3c501_softc_t *sp;
+ struct ifnet *ifp;
+ u_char unit;
+ caddr_t base;
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ extern int tcp_recvspace;
+ tcp_recvspace = 0x300; /* empircal messure */
+#endif MACH_KERNEL
+
+ take_dev_irq(dev);
+ unit = (u_char)dev->unit;
+ printf(", port = %x, spl = %d, pic = %d. ",
+ dev->address, dev->sysdep, dev->sysdep1);
+
+ sp = &at3c501_softc[unit];
+ base = sp->base;
+ if (base != (caddr_t)dev->address) {
+ printf("3C501 board %d attach address error\n", unit);
+ return;
+ }
+ sp->timer = -1;
+ sp->flags = 0;
+ sp->mode = 0;
+ outb(IE_CSR(sp->base), IE_RESET);
+ at3c501geteh(base, sp->ds_addr);
+ at3c501geteh(base, sp->address);
+ at3c501seteh(base, sp->address);
+ printf("ethernet id [%x:%x:%x:%x:%x:%x]",
+ sp->address[0],sp->address[1],sp->address[2],
+ sp->address[3],sp->address[4],sp->address[5]);
+ ifp = &(sp->ds_if);
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+#ifdef MACH_KERNEL
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&sp->address[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = "et";
+ ifp->if_init = at3c501init;
+ ifp->if_output = at3c501output;
+ ifp->if_ioctl = at3c501ioctl;
+ ifp->if_reset = at3c501reset;
+ ifp->if_next = NULL;
+ if_attach(ifp);
+#ifdef notdef
+ watchdog_id = timeout(at3c501watch, &(ifp->if_unit), 20*HZ);
+#endif
+#endif MACH_KERNEL
+}
+
+/*
+ * at3c501watch():
+ *
+ */
+at3c501watch(b_ptr)
+
+caddr_t b_ptr;
+
+{
+ int x,
+ y,
+ opri,
+ unit;
+ at3c501_softc_t *is;
+
+ unit = *b_ptr;
+#ifdef MACH_KERNEL
+ timeout(at3c501watch,b_ptr,20*hz);
+#else MACH_KERNEL
+ watchdog_id = timeout(at3c501watch,b_ptr,20*HZ);
+#endif MACH_KERNEL
+ is = &at3c501_softc[unit];
+ printf("\nxmt/bad rcv/bad spurious\n");
+ printf("%d/%d %d/%d %d\n", is->xmt, is->badxmt, \
+ is->rcv, is->badrcv, is->spurious);
+ is->rcv=is->badrcv=is->xmt=is->badxmt=is->spurious=0;
+}
+
+/*
+ * at3c501geteh:
+ *
+ * This function gets the ethernet address (array of 6 unsigned
+ * bytes) from the 3c501 board prom.
+ *
+ */
+
+at3c501geteh(base, ep)
+caddr_t base;
+char *ep;
+{
+ int i;
+
+ for (i = 0; i < ETHER_ADD_SIZE; i++) {
+ outw(IE_GP(base), i);
+ *ep++ = inb(IE_SAPROM(base));
+ }
+}
+
+/*
+ * at3c501seteh:
+ *
+ * This function sets the ethernet address (array of 6 unsigned
+ * bytes) on the 3c501 board.
+ *
+ */
+
+at3c501seteh(base, ep)
+caddr_t base;
+char *ep;
+{
+ int i;
+
+ for (i = 0; i < ETHER_ADD_SIZE; i++) {
+ outb(EDLC_ADDR(base) + i, *ep++);
+ }
+}
+
+#ifdef MACH_KERNEL
+int at3c501start(); /* forward */
+
+at3c501output(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NAT3C501 ||
+ at3c501_softc[unit].base == 0)
+ return (ENXIO);
+
+ return (net_write(&at3c501_softc[unit].ds_if, at3c501start, ior));
+}
+
+at3c501setinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t filter[];
+ u_int filter_count;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NAT3C501 ||
+ at3c501_softc[unit].base == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&at3c501_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+
+#else MACH_KERNEL
+/*
+ * at3c501output:
+ *
+ * This routine is called by the "if" layer to output a packet to
+ * the network. This code resolves the local ethernet address, and
+ * puts it into the mbuf if there is room. If not, then a new mbuf
+ * is allocated with the header information and precedes the data
+ * to be transmitted.
+ *
+ * input: ifnet structure pointer, an mbuf with data, and address
+ * to be resolved
+ * output: mbuf is updated to hold enet address, or a new mbuf
+ * with the address is added
+ *
+ */
+at3c501output(ifp, m0, dst)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+{
+ int type, error;
+ spl_t opri;
+ u_char edst[6];
+ struct in_addr idst;
+ register at3c501_softc_t *is;
+ register struct mbuf *m = m0;
+ register struct ether_header *eh;
+ register int off;
+ int usetrailers;
+
+ is = &at3c501_softc[ifp->if_unit];
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("3C501 Turning off board %d\n", ifp->if_unit);
+ at3c501intoff(ifp->if_unit);
+ error = ENETDOWN;
+ goto bad;
+ }
+ switch (dst->sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ idst = ((struct sockaddr_in *)dst)->sin_addr;
+ if (!arpresolve(&is->at3c501_ac, m, &idst, edst, &usetrailers)){
+ return (0); /* if not yet resolved */
+ }
+ off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+
+ if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
+ m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+ type = ETHERTYPE_TRAIL + (off>>9);
+ m->m_off -= 2 * sizeof (u_short);
+ m->m_len += 2 * sizeof (u_short);
+ *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
+ *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
+ goto gottrailertype;
+ }
+ type = ETHERTYPE_IP;
+ off = 0;
+ goto gottype;
+#endif
+#ifdef NS
+ case AF_NS:
+ type = ETHERTYPE_NS;
+ bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
+ (caddr_t)edst, sizeof (edst));
+ off = 0;
+ goto gottype;
+#endif
+
+ case AF_UNSPEC:
+ eh = (struct ether_header *)dst->sa_data;
+ bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
+ type = eh->ether_type;
+ goto gottype;
+
+ default:
+ printf("at3c501%d: can't handle af%d\n", ifp->if_unit,
+ dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+gottrailertype:
+ /*
+ * Packet to be sent as trailer: move first packet
+ * (control information) to end of chain.
+ */
+ while (m->m_next)
+ m = m->m_next;
+ m->m_next = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ m0 = m;
+
+gottype:
+ /*
+ * Add local net header. If no space in first mbuf,
+ * allocate another.
+ */
+ if (m->m_off > MMAXOFF ||
+ MMINOFF + sizeof (struct ether_header) > m->m_off) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ m->m_next = m0;
+ m->m_off = MMINOFF;
+ m->m_len = sizeof (struct ether_header);
+ } else {
+ m->m_off -= sizeof (struct ether_header);
+ m->m_len += sizeof (struct ether_header);
+ }
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = htons((u_short)type);
+ bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)is->address,(caddr_t)eh->ether_shost,
+ sizeof(edst));
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ opri = SPLNET();
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(opri);
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m);
+ /*
+ * Some action needs to be added here for checking whether the
+ * board is already transmitting. If it is, we don't want to
+ * start it up (ie call at3c501start()). We will attempt to send
+ * packets that are queued up after an interrupt occurs. Some
+ * flag checking action has to happen here and/or in the start
+ * routine. This note is here to remind me that some thought
+ * is needed and there is a potential problem here.
+ *
+ */
+ at3c501start(ifp->if_unit);
+ splx(opri);
+ return (0);
+
+bad:
+ m_freem(m0);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * at3c501reset:
+ *
+ * This routine is in part an entry point for the "if" code. Since most
+ * of the actual initialization has already (we hope already) been done
+ * by calling at3c501attach().
+ *
+ * input : unit number or board number to reset
+ * output : board is reset
+ *
+ */
+at3c501reset(unit)
+int unit;
+{
+ at3c501_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ return(at3c501init(unit));
+}
+
+
+
+/*
+ * at3c501init:
+ *
+ * Another routine that interfaces the "if" layer to this driver.
+ * Simply resets the structures that are used by "upper layers".
+ * As well as calling at3c501hwrst that does reset the at3c501 board.
+ *
+ * input : board number
+ * output : structures (if structs) and board are reset
+ *
+ */
+at3c501init(unit)
+int unit;
+{
+ struct ifnet *ifp;
+ int stat;
+ spl_t oldpri;
+
+ ifp = &(at3c501_softc[unit].ds_if);
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) {
+ return;
+ }
+#endif MACH_KERNEL
+ oldpri = SPLNET();
+ if ((stat = at3c501hwrst(unit)) == TRUE) {
+ at3c501_softc[unit].ds_if.if_flags |= IFF_RUNNING;
+ at3c501_softc[unit].flags |= DSF_RUNNING;
+ at3c501start(unit);
+ }
+ else
+ printf("3C501 trouble resetting board %d\n", unit);
+ at3c501_softc[unit].timer = 5;
+ splx(oldpri);
+ return(stat);
+
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+at3c501open(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NAT3C501 ||
+ at3c501_softc[unit].base == 0)
+ return (ENXIO);
+
+ at3c501_softc[unit].ds_if.if_flags |= IFF_UP;
+ at3c501init(unit);
+ return(0);
+}
+#endif MACH_KERNEL
+
+/*
+ * at3c501start:
+ *
+ * This is yet another interface routine that simply tries to output a
+ * in an mbuf after a reset.
+ *
+ * input : board number
+ * output : stuff sent to board if any there
+ *
+ */
+at3c501start(unit)
+int unit;
+
+{
+#ifdef MACH_KERNEL
+ io_req_t m;
+#else MACH_KERNEL
+ struct mbuf *m;
+#endif MACH_KERNEL
+ struct ifnet *ifp;
+
+ ifp = &(at3c501_softc[unit].ds_if);
+ for(;;) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+#ifdef MACH_KERNEL
+ if (m != 0)
+#else MACH_KERNEL
+ if (m != (struct mbuf *)0)
+#endif MACH_KERNEL
+ at3c501xmt(unit, m);
+ else
+ return;
+ }
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+at3c501getstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status; /* pointer to OUT array */
+ u_int *count; /* out */
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NAT3C501 ||
+ at3c501_softc[unit].base == 0)
+ return (ENXIO);
+
+ return (net_getstat(&at3c501_softc[unit].ds_if,
+ flavor,
+ status,
+ count));
+}
+
+at3c501setstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ u_int count;
+{
+ register int unit = minor(dev);
+ register at3c501_softc_t *sp;
+
+ if (unit < 0 || unit >= NAT3C501 ||
+ at3c501_softc[unit].base == 0)
+ return (ENXIO);
+
+ sp = &at3c501_softc[unit];
+
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ /*
+ * All we can change are flags, and not many of those.
+ */
+ register struct net_status *ns = (struct net_status *)status;
+ int mode = 0;
+
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ /*
+ * Force a compilete reset if the receive mode changes
+ * so that these take effect immediately.
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ at3c501init(unit);
+ }
+ }
+ break;
+ }
+ case NET_ADDRESS:
+ {
+ register union ether_cvt {
+ char addr[6];
+ int lwd[2];
+ } *ec = (union ether_cvt *)status;
+
+ if (count < sizeof(*ec)/sizeof(int))
+ return (D_INVALID_SIZE);
+
+ ec->lwd[0] = ntohl(ec->lwd[0]);
+ ec->lwd[1] = ntohl(ec->lwd[1]);
+ at3c501seteh(sp->base, ec->addr);
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+#else MACH_KERNEL
+
+/*
+ * at3c501ioctl:
+ *
+ * This routine processes an ioctl request from the "if" layer
+ * above.
+ *
+ * input : pointer the appropriate "if" struct, command, and data
+ * output : based on command appropriate action is taken on the
+ * at3c501 board(s) or related structures
+ * return : error is returned containing exit conditions
+ *
+ */
+int curr_ipl;
+u_short curr_pic_mask;
+u_short pic_mask[];
+
+at3c501ioctl(ifp, cmd, data)
+struct ifnet *ifp;
+int cmd;
+caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ register at3c501_softc_t *is;
+ int error;
+ spl_t opri;
+ short mode = 0;
+
+ is = &at3c501_softc[ifp->if_unit];
+ opri = SPLNET();
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ at3c501init(ifp->if_unit);
+ switch (ifa->ifa_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp,
+ &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina =
+ &(IA_SNS(ifa)->sns_addr);
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(ds->ds_addr);
+ else
+ at3c501seteh(ina->x_host.c_host,
+ at3c501_softc[ifp->if_unit].base);
+ break;
+ }
+#endif
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ifp->if_flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+ /*
+ * force a complete reset if the receive multicast/
+ * promiscuous mode changes so that these take
+ * effect immediately.
+ *
+ */
+ if (is->mode != mode) {
+ is->mode = mode;
+ if (is->flags & DSF_RUNNING) {
+ is->flags &=
+ ~(DSF_LOCK|DSF_RUNNING);
+ at3c501init(ifp->if_unit);
+ }
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ is->flags & DSF_RUNNING) {
+ printf("AT3C501 ioctl: turning off board %d\n",
+ ifp->if_unit);
+ is->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ is->timer = -1;
+ at3c501intoff(ifp->if_unit);
+ } else
+ if (ifp->if_flags & IFF_UP &&
+ (is->flags & DSF_RUNNING) == 0)
+ at3c501init(ifp->if_unit);
+ break;
+ default:
+ error = EINVAL;
+ }
+ splx(opri);
+ return (error);
+}
+#endif MACH_KERNEL
+
+
+/*
+ * at3c501hwrst:
+ *
+ * This routine resets the at3c501 board that corresponds to the
+ * board number passed in.
+ *
+ * input : board number to do a hardware reset
+ * output : board is reset
+ *
+ */
+#define XMT_STAT (EDLC_16|EDLC_JAM|EDLC_UNDER|EDLC_IDLE)
+#define RCV_STAT (EDLC_STALE|EDLC_ANY|EDLC_SHORT|EDLC_DRIBBLE|EDLC_OVER|EDLC_FCS)
+int
+at3c501hwrst(unit)
+int unit;
+{
+ u_char stat;
+ caddr_t base = at3c501_softc[unit].base;
+
+ outb(IE_CSR(base), IE_RESET);
+ outb(IE_CSR(base), 0);
+ at3c501seteh(base, at3c501_softc[unit].address);
+ if ((stat = inb(IE_CSR(base))) != IE_RESET) {
+ printf("at3c501reset: can't reset CSR: %x\n", stat);
+ return(FALSE);
+ }
+ if ((stat = inb(EDLC_XMT(base))) & XMT_STAT) {
+ printf("at3c501reset: can't reset XMT: %x\n", stat);
+ return(FALSE);
+ }
+ if (((stat = inb(EDLC_RCV(base))) & RCV_STAT) != EDLC_STALE) {
+ printf("at3c501reset: can't reset RCV: %x\n", stat);
+ return(FALSE);
+ }
+ if (at3c501config(unit) == FALSE) {
+ printf("at3c501hwrst(): failed to config\n");
+ return(FALSE);
+ }
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ return(TRUE);
+}
+
+/*
+ * at3c501intr:
+ *
+ * This function is the interrupt handler for the at3c501 ethernet
+ * board. This routine will be called whenever either a packet
+ * is received, or a packet has successfully been transfered and
+ * the unit is ready to transmit another packet.
+ *
+ * input : board number that interrupted
+ * output : either a packet is received, or a packet is transfered
+ *
+ */
+at3c501intr(unit)
+int unit;
+{
+ at3c501rcv(unit);
+ at3c501start(unit);
+
+ return(0);
+}
+
+
+/*
+ * at3c501rcv:
+ *
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the board to the "if" layer above this
+ * driver. This routine checks if a buffer has been successfully
+ * received by the at3c501. If so, the routine at3c501read is called
+ * to do the actual transfer of the board data (including the
+ * ethernet header) into a packet (consisting of an mbuf chain).
+ *
+ * input : number of the board to check
+ * output : if a packet is available, it is "sent up"
+ *
+ */
+at3c501rcv(unit)
+int unit;
+{
+ int stat;
+ caddr_t base;
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+#else MACH_KERNEL
+ struct mbuf *m, *tm;
+#endif MACH_KERNEL
+ u_short len;
+ register struct ifnet *ifp;
+ struct ether_header header;
+ int tlen;
+ register at3c501_softc_t *is;
+ register struct ifqueue *inq;
+ spl_t opri;
+ struct ether_header eh;
+
+ is = &at3c501_softc[unit];
+ ifp = &is->ds_if;
+ base = at3c501_softc[unit].base;
+ is->rcv++;
+ if (inb(IE_CSR(base)) & IE_RCVBSY)
+ is->spurious++;
+ while (!((stat=inb(EDLC_RCV(base))) & EDLC_STALE)) {
+ outb(IE_CSR(base), IE_SYSBFR);
+ if (!(stat & EDLC_ANY)) {
+ outw(IE_GP(base), 0);
+ len = inw(IE_RP(base))-sizeof(struct ether_header);
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ is->badrcv++;
+#ifdef DEBUG
+ printf("at3c501rcv: received %d bad bytes", len);
+ if (stat & EDLC_SHORT)
+ printf(" Short frame");
+ if (stat & EDLC_OVER)
+ printf(" Data overflow");
+ if (stat & EDLC_DRIBBLE)
+ printf(" Dribble error");
+ if (stat & EDLC_FCS)
+ printf(" CRC error");
+ printf("\n");
+#endif DEBUG
+ } else {
+ outw(IE_GP(base), 0);
+ len = inw(IE_RP(base));
+ if (len < 60) {
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ return;
+ }
+ linb(IE_BFR(base), &eh, sizeof(struct ether_header));
+#ifdef MACH_KERNEL
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the packet.
+ */
+ is->ds_if.if_rcvdrops++;
+
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ return;
+ }
+
+ ehp = (struct ether_header *)
+ (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *)
+ (&net_kmsg(new_kmsg)->packet[0]);
+
+ /*
+ * Get header.
+ */
+ *ehp = eh;
+
+ /*
+ * Get body
+ */
+ linb(IE_BFR(base),
+ (char *)(pkt + 1),
+ len - sizeof(struct ether_header));
+
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+
+ pkt->type = ehp->ether_type;
+ pkt->length = len - sizeof(struct ether_header)
+ + sizeof(struct packet_header);
+
+ /*
+ * Hand the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length,
+ ethernet_priority(new_kmsg));
+
+#else MACH_KERNEL
+ eh.ether_type = htons(eh.ether_type);
+ m =(struct mbuf *)0;
+#ifdef DEBUG
+ printf("received %d bytes\n", len);
+#endif DEBUG
+ len -= sizeof(struct ether_header);
+ while ( len ) {
+ if (m == (struct mbuf *)0) {
+ m = m_get(M_DONTWAIT, MT_DATA);
+ if (m == (struct mbuf *)0) {
+ printf("at3c501rcv: Lost frame\n");
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+
+ return;
+ }
+ tm = m;
+ tm->m_off = MMINOFF;
+ /*
+ * first mbuf in the packet must contain a pointer to the
+ * ifnet structure. other mbufs that follow and make up
+ * the packet do not need this pointer in the mbuf.
+ *
+ */
+ *(mtod(tm, struct ifnet **)) = ifp;
+ tm->m_len = sizeof(struct ifnet **);
+ }
+ else {
+ tm->m_next = m_get(M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ tm->m_off = MMINOFF;
+ tm->m_len = 0;
+ if (tm == (struct mbuf *)0) {
+ m_freem(m);
+ printf("at3c501rcv: No mbufs, lost frame\n");
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ return;
+ }
+ }
+ tlen = MIN( MLEN - tm->m_len, len );
+ tm->m_next = (struct mbuf *)0;
+ linb(IE_BFR(base), mtod(tm, char *)+tm->m_len, tlen );
+ tm->m_len += tlen;
+ len -= tlen;
+ }
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ /*
+ * received packet is now in a chain of mbuf's. next step is
+ * to pass the packet upwards.
+ *
+ */
+ switch (eh.ether_type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+ case ETHERTYPE_ARP:
+ arpinput(&is->at3c501_ac, m);
+ return;
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+#endif
+ default:
+ m_freem(m);
+ return;
+ }
+ opri = SPLNET();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(opri);
+ m_freem(m);
+ return;
+ }
+ IF_ENQUEUE(inq, m);
+ splx(opri);
+#endif MACH_KERNEL
+ }
+ }
+}
+
+
+/*
+ * at3c501xmt:
+ *
+ * This routine fills in the appropriate registers and memory
+ * locations on the 3C501 board and starts the board off on
+ * the transmit.
+ *
+ * input : board number of interest, and a pointer to the mbuf
+ * output : board memory and registers are set for xfer and attention
+ *
+ */
+at3c501xmt(unit, m)
+int unit;
+#ifdef MACH_KERNEL
+io_req_t m;
+#else MACH_KERNEL
+struct mbuf *m;
+#endif MACH_KERNEL
+{
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ register struct mbuf *tm_p;
+#endif MACH_KERNEL
+ int i;
+ at3c501_softc_t *is = &at3c501_softc[unit];
+ caddr_t base = is->base;
+ u_short count = 0;
+ u_short bytes_in_msg;
+
+ is->xmt++;
+ outb(IE_CSR(base), IE_SYSBFR);
+#ifdef MACH_KERNEL
+ count = m->io_count;
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+ bytes_in_msg = max(count,
+ ETHERMIN + sizeof(struct ether_header));
+#else MACH_KERNEL
+ bytes_in_msg = max(m_length(m), ETHERMIN + sizeof(struct ether_header));
+#endif MACH_KERNEL
+ outw(IE_GP(base), BFRSIZ-bytes_in_msg);
+#ifdef MACH_KERNEL
+ loutb(IE_BFR(base), m->io_data, count);
+#else MACH_KERNEL
+ for (tm_p = m; tm_p != (struct mbuf *)0; tm_p = tm_p->m_next) {
+ if (count + tm_p->m_len > ETHERMTU + sizeof(struct ether_header))
+ break;
+ if (tm_p->m_len == 0)
+ continue;
+ loutb(IE_BFR(base), mtod(tm_p, caddr_t), tm_p->m_len);
+ count += tm_p->m_len;
+ }
+#endif MACH_KERNEL
+ while (count < bytes_in_msg) {
+ outb(IE_BFR(base), 0);
+ count++;
+ }
+ do {
+ if (!(int)m) {
+ outb(IE_CSR(base), IE_SYSBFR);
+ }
+ outw(IE_GP(base), BFRSIZ-bytes_in_msg);
+ outb(IE_CSR(base), IE_RIDE|IE_XMTEDLC);
+ if (m) {
+#ifdef MACH_KERNEL
+ iodone(m);
+ m = 0;
+#else MACH_KERNEL
+ m_freem(m);
+ m = (struct mbuf *) 0;
+#endif MACH_KERNEL
+ }
+ for (i=0; inb(IE_CSR(base)) & IE_XMTBSY; i++);
+ if ((i=inb(EDLC_XMT(base))) & EDLC_JAM) {
+ is->badxmt++;
+#ifdef DEBUG
+ printf("at3c501xmt jam\n");
+#endif DEBUG
+ }
+ } while ((i & EDLC_JAM) && !(i & EDLC_16));
+
+ if (i & EDLC_16) {
+ printf("%");
+ }
+ return;
+
+}
+
+/*
+ * at3c501config:
+ *
+ * This routine does a standard config of the at3c501 board.
+ *
+ */
+at3c501config(unit)
+int unit;
+{
+ caddr_t base = at3c501_softc[unit].base;
+ u_char stat;
+
+ /* Enable DMA & Interrupts */
+
+ outb(IE_CSR(base), IE_RIDE|IE_SYSBFR);
+
+ /* No Transmit Interrupts */
+
+ outb(EDLC_XMT(base), 0);
+ inb(EDLC_XMT(base));
+
+ /* Setup Receive Interrupts */
+
+ outb(EDLC_RCV(base), EDLC_BROAD|EDLC_SHORT|EDLC_GOOD|EDLC_DRIBBLE|EDLC_OVER);
+ inb(EDLC_RCV(base));
+
+ outb(IE_CSR(base), IE_RIDE|IE_SYSBFR);
+ outb(IE_RP(base), 0);
+ outb(IE_CSR(base), IE_RIDE|IE_RCVEDLC);
+ return(TRUE);
+}
+
+/*
+ * at3c501intoff:
+ *
+ * This function turns interrupts off for the at3c501 board indicated.
+ *
+ */
+at3c501intoff(unit)
+int unit;
+{
+ caddr_t base = at3c501_softc[unit].base;
+ outb(IE_CSR(base), 0);
+}
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+/*
+ * The length of an mbuf chain
+ */
+m_length(m)
+ register struct mbuf *m;
+{
+ register int len = 0;
+
+ while (m) {
+ len += m->m_len;
+ m = m->m_next;
+ }
+ return len;
+}
+#endif MACH_KERNEL
diff --git a/i386/i386at/if_3c501.h b/i386/i386at/if_3c501.h
new file mode 100644
index 00000000..ac0641f5
--- /dev/null
+++ b/i386/i386at/if_3c501.h
@@ -0,0 +1,175 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: if_3c501.h
+ * Author: Philippe Bernadat
+ * Date: 1989
+ * Copyright (c) 1989 OSF Research Institute
+ *
+ * 3COM Etherlink 3C501 Mach Ethernet drvier
+ */
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* The various IE command registers */
+
+#define EDLC_ADDR(base) (base) /* EDLC station address, 6 bytes*/
+#define EDLC_RCV(base) ((base)+0x6) /* EDLC receive cmd. & stat. */
+#define EDLC_XMT(base) ((base)+0x7) /* EDLC transmit cmd. & stat. */
+#define IE_GP(base) ((base)+0x8) /* General Purpose pointer */
+#define IE_RP(base) ((base)+0xa) /* Receive buffer pointer */
+#define IE_SAPROM(base) ((base)+0xc) /* station addr prom window */
+#define IE_CSR(base) ((base)+0xe) /* IE command and status */
+#define IE_BFR(base) ((base)+0xf) /* 1 byte window on packet buffer*/
+
+/* CSR Status Register (read)
+ *
+ * _______________________________________________________________________
+ * | | | | | | | | |
+ * | XMTBSY | RIDE | DMA | EDMA | BUFCTL | | RCVBSY |
+ * |________|________|________|________|________|________|________|________|
+ *
+ */
+
+/* CSR Command Register (write)
+ *
+ * _______________________________________________________________________
+ * | | | | | | | | |
+ * | RESET | RIDE | DMA | | BUFCTL | | IRE |
+ * |________|________|________|________|________|________|________|________|
+ *
+ */
+
+#define IE_XMTBSY 0x80 /* Transmitter busy (ro) */
+#define IE_RESET 0x80 /* reset the controller (wo) */
+#define IE_RIDE 0x40 /* request interrupt/DMA enable (rw) */
+#define IE_DMA 0x20 /* DMA request (rw) */
+#define IE_EDMA 0x10 /* DMA done (ro) */
+#define IE_BUFCTL 0x0c /* mask for buffer control field (rw) */
+#define IE_RCVBSY 0x01 /* receive in progress (ro) */
+#define IE_IRE 0x01 /* Interrupt request enable */
+
+/* BUFCTL values */
+
+#define IE_LOOP 0x0c /* 2 bit field in bits 2,3, loopback */
+#define IE_RCVEDLC 0x08 /* gives buffer to receiver */
+#define IE_XMTEDLC 0x04 /* gives buffer to transmit */
+#define IE_SYSBFR 0x00 /* gives buffer to processor */
+
+/* XMTCSR Transmit Status Register (read)
+ *
+ * _______________________________________________________________________
+ * | | | | | | | | |
+ * | | | | | IDLE | 16 | JAM | UNDER |
+ * |________|________|________|________|________|________|________|________|
+ *
+ */
+
+/* XMTCSR Transmit Command Register (write) enables interrupts when written
+ *
+ * _______________________________________________________________________
+ * | | | | | | | | |
+ * | | | | | | | | |
+ * |________|________|________|________|________|________|________|________|
+ *
+ */
+
+#define EDLC_IDLE 0x08 /* transmit idle */
+#define EDLC_16 0x04 /* packet experienced 16 collisions */
+#define EDLC_JAM 0x02 /* packet experienced a collision */
+#define EDLC_UNDER 0x01 /* data underflow */
+
+/* RCVCSR Receive Status Register (read)
+ *
+ * _______________________________________________________________________
+ * | | | | | | | | |
+ * | STALE | | GOOD | ANY | SHORT | DRIBBLE| FCS | OVER |
+ * |________|________|________|________|________|________|________|________|
+ *
+ */
+
+/* RCVCSR Receive Command Register (write) enables interrupt when written
+ *
+ * _______________________________________________________________________
+ * | | | | | | | | |
+ * | ADDR MATCH MODE | GOOD | ANY | SHORT | DRIBBLE| FCS | OVER |
+ * |________|________|________|________|________|________|________|________|
+ *
+ */
+
+#define EDLC_STALE 0x80 /* receive CSR status previously read */
+#define EDLC_GOOD 0x20 /* well formed packets only */
+#define EDLC_ANY 0x10 /* any packet, even those with errors */
+#define EDLC_SHORT 0x08 /* short frame */
+#define EDLC_DRIBBLE 0x04 /* dribble error */
+#define EDLC_FCS 0x02 /* CRC error */
+#define EDLC_OVER 0x01 /* data overflow */
+
+/* Address Match Mode */
+
+#define EDLC_NONE 0x00 /* match mode in bits 5-6, write only */
+#define EDLC_ALL 0x40 /* promiscuous receive, write only */
+#define EDLC_BROAD 0x80 /* station address plus broadcast */
+#define EDLC_MULTI 0xc0 /* station address plus multicast */
+
+/* Packet Buffer size */
+
+#define BFRSIZ 2048
+
+#define NAT3C501 1
+#define ETHER_ADD_SIZE 6 /* size of a MAC address */
+
+#ifndef TRUE
+#define TRUE 1
+#endif TRUE
+#define HZ 100
+
+#define DSF_LOCK 1
+#define DSF_RUNNING 2
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
diff --git a/i386/i386at/if_3c503.h b/i386/i386at/if_3c503.h
new file mode 100644
index 00000000..865882cb
--- /dev/null
+++ b/i386/i386at/if_3c503.h
@@ -0,0 +1,116 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991 Carnegie-Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/* Vendor unique hardware addr. prefix. 3Com has 2 because they ran
+ out of available addresses on the first one... */
+
+#define OLD_3COM_ID 0x02608c
+#define NEW_3COM_ID 0x0020af
+
+/* Gate Array Description */
+
+#define PSTR 0x400
+#define PSPR 0x401
+#define DQTR 0x402
+#define BCFR 0x403
+#define PCFR 0x404
+#define GACFR 0x405
+#define CTLR 0x406
+#define STREG 0x407
+#define IDCFR 0x408
+#define DAMSB 0x409
+#define DALSB 0x40A
+#define VPTR2 0x40B
+#define VPTR1 0x40C
+#define VPTR0 0x40D
+#define RFMSB 0x40E
+#define RFLSB 0x40F
+
+ /* PSTR 400 */
+/* int */
+ /* PSPR 401 */
+/* int */
+ /* DQTR 402 */
+/* dma only */
+ /* BCFR 403 */
+#define B7_300 0x300
+#define B6_310 0x310
+#define B5_330 0x330
+#define B4_350 0x350
+#define B3_250 0x250
+#define B2_280 0x280
+#define B1_2A0 0x2a0
+#define B0_2E0 0x2e0
+ /* PCFR 404 */
+
+ /* GACFR 405 */
+#define GACFR_NIM 0x80
+#define GACFR_TCM 0x40
+#define GACFR_OWS 0x20
+#define GACFR_TEST 0x10
+#define GACFR_RSEL 0x08
+#define GACFR_MBS2 0x04
+#define GACFR_MBS1 0x02
+#define GACFR_MBS0 0x01
+ /*
+ * This definition is only for the std 8k window on an 8k board.
+ * It is incorrect for a 32K board. But they do not exists yet
+ * and I don't even know how to tell I am looking at one.
+ */
+#define GACFR_8K (GACFR_RSEL|0x1)
+ /* CTLR 406 */
+#define CTLR_START 0x80
+#define CTLR_DDIR 0x40
+#define CTLR_DBSEL 0x20
+#define CTLR_SHARE 0x10
+#define CTLR_EAHI 0x08
+#define CTLR_EALO 0x04
+#define CTLR_XSEL 0x02
+#define CTLR_RST 0x01
+#define CTLR_EA 0x0c
+#define CTLR_STA_ADDR 0x04
+#define CTLR_THIN 0x02
+#define CTLR_THICK 0x00
+ /* STREG 407 */
+/* DMA */
+ /* IDCFR 408 */
+#define IDCFR_IRQ5 0x80
+#define IDCFR_IRQ4 0x40
+#define IDCFR_IRQ3 0x20
+#define IDCFR_IRQ2 0x10
+#define IDCFR_DRQ3 0x04
+#define IDCFR_DRQ2 0x02
+#define IDCFR_DRQ1 0x01
+ /* DAMSB 409 */
+/* int & dma */
+ /* DALSB 40A */
+/* int & dma */
+ /* VPTR2 40B */
+ /* VPTR1 40C */
+ /* VPTR0 40D */
+ /* RFMSB 40E */
+/* what's a register file */
+ /* RFLSB 40F */
diff --git a/i386/i386at/if_de6c.c b/i386/i386at/if_de6c.c
new file mode 100644
index 00000000..0bad7803
--- /dev/null
+++ b/i386/i386at/if_de6c.c
@@ -0,0 +1,1777 @@
+#define DEBUG 1
+/*
+ * Mach Operating System
+ * Copyright (c) 1994,1993,1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * 17-Feb-94 David Golub (dbg) at Carnegie-Mellon University
+ * Fix from Bob Baron to fix transmitter problems.
+ *
+ * $Log: if_de6c.c,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:26 thomas
+ * Imported from UK22
+ *
+ * Revision 1.1 1994/11/08 20:47:24 baford
+ * merged in CMU's MK83-MK83a diffs
+ *
+ * Revision 2.2 93/11/17 18:29:25 dbg
+ * Moved source into kernel/i386at/DLINK/if_de6c.c, since we
+ * can't release it but don't want to lose it.
+ * [93/11/17 dbg]
+ *
+ * Removed u_long.
+ * [93/03/25 dbg]
+ *
+ * Created.
+ * I have used if_3c501.c as a typical driver template and
+ * spliced in the appropriate particulars for the
+ * d-link 600.
+ * [92/08/13 rvb]
+ *
+ *
+ * File: if_de6c.c
+ * Author: Robert V. Baron
+ */
+
+/*
+ * File: if_3c501.c
+ * Author: Philippe Bernadat
+ * Date: 1989
+ * Copyright (c) 1989 OSF Research Institute
+ *
+ * 3COM Etherlink d-link "600" Mach Ethernet drvier
+ */
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * I have tried to make it clear what is device specific code
+ * and what code supports the general BSD ethernet interface. d-link
+ * specific code is preceded by a line or two of
+ * "d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON"
+ * and followed by a line or two of
+ * "d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF"
+ *
+ * The main routines that do device specific processing are:
+ * de6cintr - interrupt dispatcher
+ * de6crcv - rcv packets and switch to new buffers
+ * de6cxmt - xmt packet and wait for xmtbusy to clear
+ * de6calive - probe for device
+ * de6cinit - device initialization
+ * de6cintoff - turn it off.
+ * There are a couple of interesting macros at the head of this
+ * file and some support subroutines at the end.
+ *
+ * Lastly, to get decent performance on i386SX class machines, it
+ * was necessary to recode the read and write d-link memory routines in
+ * assembler. The deread and dewrite routines that are used are in
+ * if_de6s.s
+ *
+ */
+
+/* Questions:
+
+ Make sure that iopl maps 378, 278 and 3bc.
+
+ If you set command w/o MODE and page bit, what happens?
+
+ Could I get xmt interrupts; currently I spin - this is not an issue?
+
+ enable promiscuous?
+
+ Can you assert TXEN and RXen simulatneously?
+*/
+
+#include <de6c.h>
+#include <par.h>
+
+#ifdef MACH_KERNEL
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <chips/busses.h>
+#else MACH_KERNEL
+#include <sys/param.h>
+#include <mach/machine/vm_param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/vmmac.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+#include <i386at/atbus.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+#endif MACH_KERNEL
+
+#include <vm/vm_kern.h>
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <i386at/if_de6c.h>
+
+#define SPLNET spl6
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+#define de6cwrite(sp, addr, buf, len) \
+ de6cwriteasm(addr, buf, len, DATA(sp->port), sp->latency)
+
+#define de6cread(sp, addr, buf, len) \
+ de6creadasm(addr, buf, len, DATA(sp->port), sp->latency)
+
+#define DATA_OUT(sp, p, f, z) \
+ de6coutb(sp, DATA(p), ((z)<<4) | f);\
+ de6coutb(sp, DATA(p), ((z)&0xf0)| f | STROBE)
+
+#define STAT_IN(sp, p, in) \
+ de6coutb(sp, DATA(p), STATUS); \
+ in = inb(STAT(port)); \
+ de6coutb(sp, DATA(p), NUL_CMD | STROBE)
+
+#define XMTidx 3
+#define XMT_BSY_WAIT 10000
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+int de6cprobe();
+void de6cattach();
+int de6cintr();
+int de6cinit();
+int de6coutput();
+int de6cioctl();
+int de6creset();
+void de6cwatch();
+
+static vm_offset_t de6c_std[NDE6C] = { 0 };
+static struct bus_device *de6c_info[NDE6C];
+
+#ifdef MACH_KERNEL
+struct bus_driver de6cdriver =
+ {de6cprobe, 0, de6cattach, 0, de6c_std, "de", de6c_info, };
+extern struct bus_device *lprinfo[];
+
+#define MM io_req_t
+#define PIC sysdep1
+#define DEV bus_device
+#else MACH_KERNEL
+int (*de6cintrs[])() = { de6cintr, 0};
+struct isa_driver de6cdriver =
+ {de6cprobe, 0, de6cattach, "de", 0, 0, 0};
+extern struct isa_dev *lprinfo[];
+
+#define MM struct mbuf *
+#define PIC dev_pic
+#define DEV isa_dev
+#endif MACH_KERNEL
+
+int watchdog_id;
+
+typedef struct {
+#ifdef MACH_KERNEL
+ struct ifnet ds_if; /* generic interface header */
+ u_char ds_addr[6]; /* Ethernet hardware address */
+#else MACH_KERNEL
+ struct arpcom de6c_ac;
+#define ds_if de6c_ac.ac_if
+#define ds_addr de6c_ac.ac_enaddr
+#endif MACH_KERNEL
+ int flags;
+ int timer;
+ u_char address[6];
+ short mode;
+ int port;
+ int latency;
+ int xmt;
+ int rcv;
+ int rcvoff;
+ int rcvspin;
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ int produce;
+ int consume;
+ int rcvlen[XMTidx];
+ int alive;
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ int (*oldvect)();
+ int oldunit;
+} de6c_softc_t;
+
+de6c_softc_t de6c_softc[NDE6C];
+
+int de6cactive[NDE6C];
+
+/*
+ * Patch to change latency value
+ */
+int de6c_latency = 30; /* works on NEC Versa (pottsylvania.mach) */
+
+#ifdef DEBUG
+int de6crcv0, de6crcv1, de6crcv2, de6crcv3;
+int de6cdo_rcvintr = 0, de6cdo_watch = 0;
+int de6cdo_xmt = 0;
+#define D(X) X
+#else /* DEBUG */
+#define D(X)
+#endif /* DEBUG */
+
+/*
+ * de6cprobe:
+ * We are not directly probed. The lprattach will call de6cattach.
+ * But what we have is plausible for a probe.
+ */
+de6cprobe(port, dev)
+struct DEV *dev;
+{
+#ifdef MACH_KERNEL
+ int unit = dev->unit;
+#else MACH_KERNEL
+ int unit = dev->dev_unit;
+#endif MACH_KERNEL
+
+ if ((unit < 0) || (unit >= NDE6C)) {
+ return(0);
+ }
+ return(1);
+}
+
+/*
+ * de6cattach:
+ *
+ * Called from lprattach
+ *
+ */
+void de6cattach(dev)
+#ifdef MACH_KERNEL
+struct bus_device *dev;
+#else MACH_KERNEL
+struct isa_dev *dev;
+#endif MACH_KERNEL
+{
+ de6c_softc_t *sp;
+ struct ifnet *ifp;
+#ifdef MACH_KERNEL
+ int unit = dev->unit;
+ int port = (int)dev->address;
+#else MACH_KERNEL
+ int unit = dev->dev_unit;
+ int port = (int)dev->dev_addr;
+#endif MACH_KERNEL
+
+ sp = &de6c_softc[unit];
+ sp->port = port;
+ sp->timer = -1;
+ sp->flags = 0;
+ sp->mode = 0;
+
+ ifp = &(sp->ds_if);
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+
+#ifdef MACH_KERNEL
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&sp->address[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = "de";
+ ifp->if_init = de6cinit;
+ ifp->if_output = de6coutput;
+ ifp->if_ioctl = de6cioctl;
+ ifp->if_reset = de6creset;
+ ifp->if_next = NULL;
+ if_attach(ifp);
+#endif MACH_KERNEL
+
+ sp->alive = de6calive(sp);
+}
+
+de6calive(sp)
+de6c_softc_t *sp;
+{
+ int port = sp->port;
+ int unit = sp->ds_if.if_unit;
+ struct DEV *dev = lprinfo[unit];
+ int i;
+
+#ifdef MACH_KERNEL
+#else /* MACH_KERNEL */
+ extern int tcp_recvspace; /* empircal messure */
+#endif /* MACH_KERNEL */
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ de6coutb(sp, CMD(port), SLT_NIC);
+ DATA_OUT(sp, port, COMMAND, RESET);
+ DATA_OUT(sp, port, COMMAND, STOP_RESET);
+ sp->latency = 101;
+ if (!de6cgetid(sp, sp->ds_addr)) {
+ de6coutb(sp, CMD(port), SLT_PRN);
+ return 0;
+ }
+
+#ifdef MACH_KERNEL
+#else /* MACH_KERNEL */
+ tcp_recvspace = 0x300; /* empircal messure */
+#endif /* MACH_KERNEL */
+
+#ifdef de6cwrite
+ sp->latency = de6c_latency;
+#else /* de6cwrite */
+ sp->latency = 0;
+#endif /* de6cwrite */
+
+ for (i = 0; i++ < 10;) {
+ if (de6cmemcheck(sp))
+ break;
+ sp->latency += 10;
+ }
+
+ de6cgetid(sp, sp->ds_addr);
+ de6cgetid(sp, sp->address);
+ de6csetid(sp, sp->address);
+ de6coutb(sp, CMD(port), SLT_PRN);
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+#ifdef MACH_KERNEL
+#if NPAR > 0
+ printf("\n");
+#endif /* NPAR > 0 */
+ printf(" de%d: at lpr%d, port = %x, spl = %d, pic = %d. ",
+ unit, unit, dev->address, dev->sysdep, dev->sysdep1);
+
+ printf("ethernet id [%x:%x:%x:%x:%x:%x]",
+ sp->address[0],sp->address[1],sp->address[2],
+ sp->address[3],sp->address[4],sp->address[5]);
+
+ if (sp->latency > 1) {
+ printf("\n");
+ printf(" LATENCY = %d", sp->latency);
+ printf(" LATENCY = %d", sp->latency);
+ printf(" LATENCY = %d", sp->latency);
+ printf(" LATENCY = %d", sp->latency);
+ }
+#else MACH_KERNEL
+ printf("de%d: port = %x, spl = %d, pic = %d. ",
+ unit, dev->dev_addr, dev->dev_spl, dev->dev_pic);
+
+ printf("ethernet id [%x:%x:%x:%x:%x:%x]\n",
+ sp->address[0],sp->address[1],sp->address[2],
+ sp->address[3],sp->address[4],sp->address[5]);
+
+ if (sp->latency > 1) {
+ printf("de%d:", unit);
+ printf(" LATENCY = %d", sp->latency);
+ printf(" LATENCY = %d", sp->latency);
+ printf(" LATENCY = %d", sp->latency);
+ printf(" LATENCY = %d", sp->latency);
+ printf("\n");
+ }
+#endif MACH_KERNEL
+
+ return 1;
+}
+
+/*
+ * de6cwatch():
+ *
+ */
+void de6cwatch(b_ptr)
+short *b_ptr;
+{
+#ifdef DEBUG_MORE
+ int unit = *b_ptr;
+ de6c_softc_t *sp = &de6c_softc[unit];
+
+ if(!de6cdo_watch) return;
+ de6cintr(unit);
+ if (sp->ds_if.if_flags & IFF_RUNNING)
+ timeout(de6cwatch, b_ptr, de6cdo_watch);
+#endif /* DEBUG_MORE */
+}
+
+#ifdef MACH_KERNEL
+void de6cstart(int); /* forward */
+
+de6coutput(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NDE6C ||
+ de6c_softc[unit].port == 0)
+ return (ENXIO);
+
+ return (net_write(&de6c_softc[unit].ds_if, de6cstart, ior));
+}
+
+io_return_t
+de6csetinput(
+ dev_t dev,
+ mach_port_t receive_port,
+ int priority,
+ filter_t filter[],
+ natural_t filter_count)
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NDE6C ||
+ de6c_softc[unit].port == 0)
+ return ENXIO;
+
+ return net_set_filter(&de6c_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count);
+}
+
+#else MACH_KERNEL
+/*
+ * de6coutput:
+ *
+ * This routine is called by the "if" layer to output a packet to
+ * the network. This code resolves the local ethernet address, and
+ * puts it into the mbuf if there is room. If not, then a new mbuf
+ * is allocated with the header information and precedes the data
+ * to be transmitted.
+ *
+ * input: ifnet structure pointer, an mbuf with data, and address
+ * to be resolved
+ * output: mbuf is updated to hold enet address, or a new mbuf
+ * with the address is added
+ *
+ */
+de6coutput(ifp, m0, dst)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+{
+ register de6c_softc_t *sp = &de6c_softc[ifp->if_unit];
+ int type, opri, error;
+ u_char edst[6];
+ struct in_addr idst;
+ register struct mbuf *m = m0;
+ register struct ether_header *eh;
+ register int off;
+ int usetrailers;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ de6cintoff(ifp->if_unit);
+ error = ENETDOWN;
+ goto bad;
+ }
+ switch (dst->sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ idst = ((struct sockaddr_in *)dst)->sin_addr;
+ if (!arpresolve(&sp->de6c_ac, m, &idst, edst, &usetrailers)){
+ return (0); /* if not yet resolved */
+ }
+ off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+
+ if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
+ m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+ type = ETHERTYPE_TRAIL + (off>>9);
+ m->m_off -= 2 * sizeof (u_short);
+ m->m_len += 2 * sizeof (u_short);
+ *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
+ *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
+ goto gottrailertype;
+ }
+ type = ETHERTYPE_IP;
+ off = 0;
+ goto gottype;
+#endif
+#ifdef NS
+ case AF_NS:
+ type = ETHERTYPE_NS;
+ bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
+ (caddr_t)edst, sizeof (edst));
+ off = 0;
+ goto gottype;
+#endif
+
+ case AF_UNSPEC:
+ eh = (struct ether_header *)dst->sa_data;
+ bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
+ type = eh->ether_type;
+ goto gottype;
+
+ default:
+ printf("de6c%d: can't handle af%d\n", ifp->if_unit,
+ dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+gottrailertype:
+ /*
+ * Packet to be sent as trailer: move first packet
+ * (control information) to end of chain.
+ */
+ while (m->m_next)
+ m = m->m_next;
+ m->m_next = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ m0 = m;
+
+gottype:
+ /*
+ * Add local net header. If no space in first mbuf,
+ * allocate another.
+ */
+ if (m->m_off > MMAXOFF ||
+ MMINOFF + sizeof (struct ether_header) > m->m_off) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ m->m_next = m0;
+ m->m_off = MMINOFF;
+ m->m_len = sizeof (struct ether_header);
+ } else {
+ m->m_off -= sizeof (struct ether_header);
+ m->m_len += sizeof (struct ether_header);
+ }
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = htons((u_short)type);
+ bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)sp->address,(caddr_t)eh->ether_shost, sizeof(edst));
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ opri = SPLNET();
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(opri);
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m);
+ de6cstart(ifp->if_unit);
+ splx(opri);
+ return (0);
+
+bad:
+ m_freem(m0);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * de6creset:
+ *
+ * This routine is in part an entry point for the "if" code. Since most
+ * of the actual initialization has already (we hope already) been done
+ * by calling de6cattach().
+ *
+ * input : unit number or board number to reset
+ * output : board is reset
+ *
+ */
+de6creset(unit)
+int unit;
+{
+ de6c_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ return(de6cinit(unit));
+}
+
+
+
+/*
+ * de6cinit:
+ *
+ * Another routine that interfaces the "if" layer to this driver.
+ * Simply resets the structures that are used by "upper layers".
+ *
+ * input : board number
+ * output : structures (if structs) and board are reset
+ *
+ */
+de6cinit(unit)
+int unit;
+{
+ de6c_softc_t *sp = &de6c_softc[unit];
+ struct ifnet *ifp = &(sp->ds_if);
+ int port = sp->port;
+ int pic = lprinfo[unit]->PIC;
+ spl_t oldpri;
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) {
+ return;
+ }
+#endif MACH_KERNEL
+ oldpri = SPLNET();
+
+ if (ivect[pic] != de6cintr) {
+ sp->oldvect = ivect[pic];
+ ivect[pic] = de6cintr;
+ sp->oldunit = iunit[pic];
+ iunit[pic] = unit;
+ }
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ sp->consume = 0;
+ sp->produce = 0;
+ de6coutb(sp, CMD(port), SLT_NIC);
+ DATA_OUT(sp, port, COMMAND, RESET);
+ DATA_OUT(sp, port, COMMAND, STOP_RESET);
+ de6coutb(sp, CMD(port), IRQEN);
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4));
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN);
+ de6coutb(sp, CMD(port), SLT_PRN);
+#if 0
+ if (sp->mode & IFF_PROMISC) {
+ /* handle promiscuous case */;
+ }
+#endif 0
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ sp->ds_if.if_flags |= IFF_RUNNING;
+ sp->flags |= DSF_RUNNING;
+ sp->timer = 5;
+ timeout(de6cwatch, &(ifp->if_unit), 3);
+ de6cstart(unit);
+ splx(oldpri);
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+de6copen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NDE6C ||
+ de6c_softc[unit].port == 0)
+ return (ENXIO);
+
+ de6c_softc[unit].ds_if.if_flags |= IFF_UP;
+ de6cinit(unit);
+ return(0);
+}
+#endif MACH_KERNEL
+
+/*
+ * de6cstart:
+ *
+ * This is yet another interface routine that simply tries to output a
+ * in an mbuf after a reset.
+ *
+ * input : board number
+ * output : stuff sent to board if any there
+ *
+ */
+
+/* NOTE: called at SPLNET */
+void de6cstart(
+ int unit)
+{
+ struct ifnet *ifp = &(de6c_softc[unit].ds_if);
+ MM m;
+
+ for(;;) {
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m != (MM) 0)
+ de6cxmt(unit, m);
+ else
+ return;
+ }
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+io_return_t
+de6cgetstat(
+ dev_t dev,
+ int flavor,
+ dev_status_t status, /* pointer to OUT array */
+ natural_t *count) /* out */
+{
+ register int unit = minor(dev);
+ register de6c_softc_t *sp;
+
+ if (unit < 0 || unit >= NDE6C ||
+ de6c_softc[unit].port == 0)
+ return (ENXIO);
+
+ sp = &de6c_softc[unit];
+ if (! sp->alive)
+ if (! (sp->alive = de6calive(sp)))
+ return ENXIO;
+
+ return (net_getstat(&de6c_softc[unit].ds_if,
+ flavor,
+ status,
+ count));
+}
+
+io_return_t
+de6csetstat(
+ dev_t dev,
+ int flavor,
+ dev_status_t status,
+ natural_t count)
+{
+ register int unit = minor(dev);
+ register de6c_softc_t *sp;
+
+ if (unit < 0 || unit >= NDE6C ||
+ de6c_softc[unit].port == 0)
+ return (ENXIO);
+
+ sp = &de6c_softc[unit];
+ if (! sp->alive)
+ if (! (sp->alive = de6calive(sp)))
+ return ENXIO;
+
+
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ /*
+ * All we can change are flags, and not many of those.
+ */
+ register struct net_status *ns = (struct net_status *)status;
+ int mode = 0;
+
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ /*
+ * Force a compilete reset if the receive mode changes
+ * so that these take effect immediately.
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ de6cinit(unit);
+ }
+ }
+ break;
+ }
+ case NET_ADDRESS:
+ {
+ register union ether_cvt {
+ char addr[6];
+ int lwd[2];
+ } *ec = (union ether_cvt *)status;
+
+ if (count < sizeof(*ec)/sizeof(int))
+ return (D_INVALID_SIZE);
+
+ ec->lwd[0] = ntohl(ec->lwd[0]);
+ ec->lwd[1] = ntohl(ec->lwd[1]);
+ de6csetid(sp->port, ec->addr);
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+#else MACH_KERNEL
+
+/*
+ * de6cioctl:
+ *
+ * This routine processes an ioctl request from the "if" layer
+ * above.
+ *
+ * input : pointer the appropriate "if" struct, command, and data
+ * output : based on command appropriate action is taken on the
+ * de6c board(s) or related structures
+ * return : error is returned containing exit conditions
+ *
+ */
+de6cioctl(ifp, cmd, data)
+struct ifnet *ifp;
+int cmd;
+caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ register de6c_softc_t *sp = &de6c_softc[ifp->if_unit];
+ int opri, error;
+ short mode = 0;
+
+ if (! sp->alive)
+ if (! (sp->alive = de6calive(sp)))
+ return ENXIO;
+
+ opri = SPLNET();
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ de6cinit(ifp->if_unit);
+ switch (ifa->ifa_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp,
+ &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina =
+ &(IA_SNS(ifa)->sns_addr);
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(ds->ds_addr);
+ else
+ de6cseteh(ina->x_host.c_host,
+ de6c_softc[ifp->if_unit].port);
+ break;
+ }
+#endif
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ifp->if_flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+ /*
+ * force a complete reset if the receive multicast/
+ * promiscuous mode changes so that these take
+ * effect immediately.
+ *
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &=
+ ~(DSF_LOCK|DSF_RUNNING);
+ de6cinit(ifp->if_unit);
+ }
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ sp->flags & DSF_RUNNING) {
+ sp->timer = -1;
+ de6cintoff(ifp->if_unit);
+ } else
+ if (ifp->if_flags & IFF_UP &&
+ (sp->flags & DSF_RUNNING) == 0)
+ de6cinit(ifp->if_unit);
+ break;
+ default:
+ error = EINVAL;
+ }
+ splx(opri);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * de6cintr:
+ *
+ * This function is the interrupt handler for the de6c ethernet
+ * board. This routine will be called whenever either a packet
+ * is received, or a packet has successfully been transfered and
+ * the unit is ready to transmit another packet.
+ *
+ * input : board number that interrupted
+ * output : either a packet is received, or a packet is transfered
+ *
+ */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+#ifdef DEBUG_MORE
+de6crcvintr(unit)
+int unit;
+{
+ if(!de6cdo_rcvintr)
+ return;
+ de6cintr(unit);
+}
+#endif /* DEBUG_MORE */
+
+de6cintr(unit)
+int unit;
+{
+ register de6c_softc_t *sp = &de6c_softc[unit];
+ int port = sp->port;
+ int in;
+
+ if (de6cactive[unit] || !(sp->flags & DSF_RUNNING))
+ return;
+ de6cactive[unit]++;
+ de6coutb(sp, CMD(port), SLT_NIC);
+ STAT_IN(sp, port, in);
+
+ if ((in & (GOOD|TXBUSY)) == (GOOD|TXBUSY)) {
+ /* on L40's means that we are disconnected */
+ printf("de6intr%d: Card was disconnected; turning off network.\n", unit);
+ de6cintoff(unit);
+ de6cactive[unit]--;
+ return;
+ }
+
+ if (in & GOOD)
+ de6crcv(unit, in);
+ else
+/*rvb:tmp printf("intr: %x\n", in)*/;
+
+
+ de6coutb(sp, CMD(port), SLT_PRN);
+ de6cactive[unit]--;
+}
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+/*
+ * de6crcv:
+ *
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the board to the "if" layer above this
+ * driver. This routine checks if a buffer has been successfully
+ * received by the de6c. If so, the routine de6cread is called
+ * to do the actual transfer of the board data (including the
+ * ethernet header) into a packet (consisting of an mbuf chain).
+ *
+ * input : number of the board to check
+ * output : if a packet is available, it is "sent up"
+ *
+ */
+de6crcv(unit, in)
+int unit, in;
+{
+ register de6c_softc_t *sp = &de6c_softc[unit];
+ register struct ifnet *ifp = &sp->ds_if;
+ int port = sp->port;
+ int bo;
+ int collision = 0;
+ int spins = 0;
+ u_short len;
+ struct ether_header header;
+ int tlen;
+ register struct ifqueue *inq;
+ int opri;
+ struct ether_header eh;
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+#else MACH_KERNEL
+ struct mbuf *m, *tm;
+#endif MACH_KERNEL
+
+ sp->rcv++;
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ D(de6crcv0++);
+#define MT (sp->consume == sp->produce)
+ while (in & GOOD || !MT) {
+ spins++;
+ D(de6crcv1++);
+ if (in & GOOD) {
+ sp->rcvlen[sp->produce] = de6clen(sp);
+ if ( ((sp->produce + 1) % XMTidx) != sp->consume) {
+ if (++sp->produce == XMTidx)
+ sp->produce = 0;
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4));
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN);
+ } else collision = 1;
+ }
+ len = sp->rcvlen[sp->consume];
+ bo = sp->consume*BFRSIZ;
+ if (len < 60) {
+ printf("de%d: len(%d) < 60\n", unit, len);
+ goto out;
+ return;
+ }
+ de6cread(sp, bo, &eh, sizeof(struct ether_header));
+ bo += sizeof(struct ether_header);
+ len -= 18;
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+#ifdef MACH_KERNEL
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the packet.
+ */
+ sp->ds_if.if_rcvdrops++;
+ goto out;
+ return;
+ }
+
+ ehp = (struct ether_header *)
+ (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *)
+ (&net_kmsg(new_kmsg)->packet[0]);
+
+ /*
+ * Get header.
+ */
+ *ehp = eh;
+
+ /*
+ * Get body
+ */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ de6cread(sp, bo, (char *)(pkt + 1), len);
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ pkt->type = ehp->ether_type;
+ pkt->length = len + sizeof(struct packet_header);
+
+ /*
+ * Hand the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length,
+ ethernet_priority(new_kmsg));
+
+#else MACH_KERNEL
+ eh.ether_type = htons(eh.ether_type);
+ m =(struct mbuf *)0;
+ while ( len ) {
+ if (m == (struct mbuf *)0) {
+ m = m_get(M_DONTWAIT, MT_DATA);
+ if (m == (struct mbuf *)0) {
+ printf("de6crcv: Lost frame\n");
+ goto out;
+ return;
+ }
+ tm = m;
+ tm->m_off = MMINOFF;
+ /*
+ * first mbuf in the packet must contain a pointer to the
+ * ifnet structure. other mbufs that follow and make up
+ * the packet do not need this pointer in the mbuf.
+ *
+ */
+ *(mtod(tm, struct ifnet **)) = ifp;
+ tm->m_len = sizeof(struct ifnet **);
+ }
+ else {
+ tm->m_next = m_get(M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ tm->m_off = MMINOFF;
+ tm->m_len = 0;
+ if (tm == (struct mbuf *)0) {
+ m_freem(m);
+ printf("de6crcv: No mbufs, lost frame\n");
+ goto out;
+ return;
+ }
+ }
+ tlen = MIN( MLEN - tm->m_len, len );
+ tm->m_next = (struct mbuf *)0;
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ de6cread(sp, bo, mtod(tm, char *)+tm->m_len, tlen);
+ bo += tlen;
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ tm->m_len += tlen;
+ len -= tlen;
+ }
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ STAT_IN(sp, port, in);
+ if (in & GOOD) { /* got another */
+ D(de6crcv2++);
+ sp->rcvlen[sp->produce] = de6clen(sp);
+ if ( ((sp->produce + 1) % XMTidx) != sp->consume) {
+ if (++sp->produce == XMTidx)
+ sp->produce = 0;
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4));
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN);
+ } else collision = 1;
+ }
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+ /*
+ * received packet is now in a chain of mbuf's. next step is
+ * to pass the packet upwards.
+ *
+ */
+ switch (eh.ether_type) {
+
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+ case ETHERTYPE_ARP:
+ arpinput(&sp->de6c_ac, m);
+ goto out;
+ return;
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+#endif
+ default:
+ m_freem(m);
+ goto out;
+ return;
+ }
+ opri = SPLNET();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(opri);
+ m_freem(m);
+ goto out;
+ return;
+ }
+ IF_ENQUEUE(inq, m);
+ splx(opri);
+#endif MACH_KERNEL
+out:
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ STAT_IN(sp, port, in);
+ if (in & GOOD) { /* got another */
+ D(de6crcv3++);
+ }
+/*2*/ /* implies wrap and pause */
+ if (collision) {
+ collision = 0;
+ D(printf("*C* "));
+ sp->rcvoff++;
+ if (++sp->produce == XMTidx)
+ sp->produce = 0;
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4));
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN);
+ }
+/*2*/ /* implies wrap and pause */
+ if (++sp->consume == XMTidx)
+ sp->consume = 0;
+ if (spins > 10) {
+ spins = 0;
+ D(printf("*R* "));
+ sp->rcvspin++;
+ /* how should we recover here ??? */;
+ /* return does not work */;
+ /* de6cinit(unit) gets ugly if we are called from
+ de6cxmt */;
+ }
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ }
+}
+
+
+/*
+ * de6cxmt:
+ *
+ * This routine fills in the appropriate registers and memory
+ * locations on the d-link "600" board and starts the board off on
+ * the transmit.
+ *
+ * input : board number of interest, and a pointer to the mbuf
+ * output : board memory and registers are set for xfer and attention
+ *
+ */
+/* NOTE: called at SPLNET */
+/*
+ * This implies that rcv interrupts will be blocked.
+ */
+#define max(a,b) (((a) > (b)) ? (a) : (b))
+
+char de6mt[ETHERMIN];
+de6cxmt(unit, m)
+int unit;
+MM m;
+{
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ register struct mbuf *tm_p;
+#endif MACH_KERNEL
+ int i;
+ int in;
+ int bo, boo;
+ de6c_softc_t *sp = &de6c_softc[unit];
+ int port = sp->port;
+ u_short count = 0;
+ u_short bytes_in_msg;
+ static int m_length();
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ if (de6cactive[unit] >= 2) /* a funny loop caused by */
+ return; /* a flood of arps */
+ if (de6cactive[unit]++ == 0)
+ de6coutb(sp, CMD(port), SLT_NIC);
+ STAT_IN(sp, port, in);
+
+ D(if (de6cdo_xmt) printf("xmt: stat[-] = %x\n", in));
+ if (in & GOOD) {
+ de6crcv(unit, in);
+ }
+ sp->xmt++;
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+#ifdef MACH_KERNEL
+ count = m->io_count;
+ bytes_in_msg = max(count, ETHERMIN + sizeof(struct ether_header));
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ boo = bo = XMTidx*BFRSIZ+(BFRSIZ-bytes_in_msg);
+ de6cwrite(sp, bo, m->io_data, count);
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ bo += count;
+ iodone(m);
+#else MACH_KERNEL
+ bytes_in_msg = max(m_length(m), ETHERMIN + sizeof(struct ether_header));
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ boo = bo = XMTidx*BFRSIZ+(BFRSIZ-bytes_in_msg);
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+ for (tm_p = m; tm_p != (struct mbuf *)0; tm_p = tm_p->m_next) {
+ if (count + tm_p->m_len > ETHERMTU + sizeof(struct ether_header))
+ break;
+ if (tm_p->m_len == 0)
+ continue;
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ de6cwrite(sp, bo, mtod(tm_p, caddr_t), tm_p->m_len);
+ bo += tm_p->m_len;
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+ count += tm_p->m_len;
+ }
+ m_freem(m);
+#endif MACH_KERNEL
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ if (bytes_in_msg - count > 0)
+ de6cwrite(sp, bo, de6mt, bytes_in_msg - count);
+
+ DATA_OUT(sp, port, TX_ADR, boo & 0xff);
+ DATA_OUT(sp, port, TX_ADR, (boo >> 8) & 0xff);
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4));
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|TXEN);
+
+ for (i = 0; i < XMT_BSY_WAIT; i++) {
+ STAT_IN(sp, port, in);
+ D(if (de6cdo_xmt) printf("xmt: stat[%d] = %x\n", i, in));
+ if (in & GOOD) {
+ /*
+ * this does indeed happen
+ * printf("!#");
+ */
+ de6crcv(unit, in);
+ }
+ if (!(in & TXBUSY)) {
+ goto out;
+ return;
+ }
+ }
+ printf("dexmt: stat[??] = %x\n", i, in);
+out:
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4));
+ DATA_OUT(sp, port, COMMAND, RX_BP|(sp->produce<<4)|RXEN);
+
+ if (--de6cactive[unit] == 0)
+ de6coutb(sp, CMD(port), SLT_PRN);
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+}
+
+/*
+ * de6cintoff:
+ *
+ * This function turns interrupts off for the de6c board indicated.
+ *
+ */
+de6cintoff(unit)
+int unit;
+{
+ de6c_softc_t *sp = &de6c_softc[unit];
+ int port = sp->port;
+ int pic = lprinfo[unit]->PIC;
+
+ printf("de%d: Turning off d-link \"600\"\n", unit);
+ sp->ds_if.if_flags &= ~(IFF_UP|IFF_RUNNING);
+ sp->flags &= ~(DSF_LOCK | DSF_RUNNING);
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+ de6coutb(sp, CMD(port), SLT_NIC);
+ DATA_OUT(sp, port, COMMAND, RESET);
+ DATA_OUT(sp, port, COMMAND, STOP_RESET);
+ de6coutb(sp, CMD(port), SLT_PRN);
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+ outb(CMD(sp->port), 0x07);
+ ivect[pic] = sp->oldvect;
+ iunit[pic] = sp->oldunit;
+}
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+/*
+ * The length of an mbuf chain
+ */
+static
+m_length(m)
+ register struct mbuf *m;
+{
+ register int len = 0;
+
+ while (m) {
+ len += m->m_len;
+ m = m->m_next;
+ }
+ return len;
+}
+#endif MACH_KERNEL
+
+
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+/* d-link 600 ON; d-link 600 ON; d-link 600 ON; d-link 600 ON */
+de6cgetid(sp, buf)
+de6c_softc_t *sp;
+u_char *buf;
+{
+ de6cread(sp, EADDR, buf, 6);
+ if ((buf[0] != 0x00) || (buf[1] != 0xde) || (buf[2] != 0x15))
+ return 0;
+ buf[0] = 0x80;
+ buf[1] = 0x00;
+ buf[2] = 0xc8;
+ /* for this model d-link we assert 0x70 as the high mfr's nibble. */
+ buf[3] = 0x70 | (buf[3] & 0xf);
+ return 1;
+}
+
+de6csetid(sp, buf)
+de6c_softc_t *sp;
+char *buf;
+{
+ de6cwrite(sp, EADDR, buf, 6);
+}
+
+/*
+ * get length of packet just rcv'd.
+ * includes ether header and crc
+ */
+de6clen(sp)
+de6c_softc_t *sp;
+{
+ int port = sp->port;
+ int in;
+ int i;
+
+ de6coutb(sp, DATA(port), RX_LEN);
+ in = inb(STAT(port));
+ de6coutb(sp, DATA(port), RX_LEN|STROBE);
+ i = ((in>>4) | (inb(STAT(port)) & 0xf0));
+
+ de6coutb(sp, DATA(port), RX_LEN);
+ in = inb(STAT(port));
+ de6coutb(sp, DATA(port), RX_LEN|STROBE);
+ i |= ((in>>4) | (inb(STAT(port)) & 0xf0)) << 8;
+
+ return i;
+}
+
+#if 0
+de6cread(sp, address, buf, len)
+de6c_softc_t *sp;
+unsigned char *buf;
+{
+ int port = sp->port;
+ u_char in;
+
+ DATA_OUT(sp, port, RW_ADR, address & 0xff);
+ DATA_OUT(sp, port, RW_ADR, (address >> 8) & 0xff);
+
+ while (len--) {
+ de6coutb(sp, DATA(port), READ);
+ in = inb(STAT(port));
+ de6coutb(sp, DATA(port), READ|STROBE);
+ *buf++ = ((in>>4) | (inb(STAT(port)) & 0xf0));
+ }
+}
+
+de6cwrite(sp, address, buf, len)
+de6c_softc_t *sp;
+unsigned char *buf;
+{
+ int port = sp->port;
+ int out;
+
+ DATA_OUT(sp, port, RW_ADR, address & 0xff);
+ DATA_OUT(sp, port, RW_ADR, (address >> 8) & 0xff);
+
+ while (len--) {
+ out = *buf++;
+ DATA_OUT(sp, port, WRITE, out);
+
+ }
+}
+#endif 0
+
+#ifndef de6cread
+de6cread(sp, address, buf, len)
+de6c_softc_t *sp;
+unsigned char *buf;
+{
+ int port = sp->port;
+ register volatile int i;
+ unsigned char in;
+
+ outb(port, ((address)<<4) | RW_ADR);
+ i = sp->latency; while (i-- > 0);
+ outb(port, ((address)&0xf0)| RW_ADR | 0x8);
+ i = sp->latency; while (i-- > 0);
+
+ outb(port, ((address>>8)<<4) | RW_ADR);
+ i = sp->latency; while (i-- > 0);
+ outb(port, ((address>>8)&0xf0)| RW_ADR | 0x8);
+ i = sp->latency; while (i-- > 0);
+
+ while (len--) {
+ outb(port, READ);
+ i = sp->latency; while (i-- > 0);
+ in = inb(STAT(port));
+ outb(port, READ|0x08);
+ i = sp->latency; while (i-- > 0);
+ *buf++ = ((in>>4) | (inb(STAT(port)) & 0xf0));
+ }
+}
+#endif /* de6cread */
+
+#ifndef de6cwrite
+de6cwrite(sp, address, buf, len)
+de6c_softc_t *sp;
+unsigned char *buf;
+{
+ int port = sp->port;
+ register volatile int i;
+ unsigned char out;
+
+ outb(port, ((address)<<4) | RW_ADR);
+ i = sp->latency; while (i-- > 0);
+ outb(port, ((address)&0xf0)| RW_ADR | 0x8);
+ i = sp->latency; while (i-- > 0);
+
+ outb(port, ((address>>8)<<4) | RW_ADR);
+ i = sp->latency; while (i-- > 0);
+ outb(port, ((address>>8)&0xf0)| RW_ADR | 0x8);
+ i = sp->latency; while (i-- > 0);
+
+ while (len--) {
+ out = *buf++;
+ outb(port, ((out)<<4) | WRITE);
+ i = sp->latency; while (i-- > 0);
+ outb(port, ((out)&0xf0)| WRITE | 0x8);
+ i = sp->latency; while (i-- > 0);
+ }
+}
+#endif /* de6cwrite */
+
+de6coutb(sp, p, v)
+de6c_softc_t *sp;
+{
+register volatile int i = sp->latency;
+
+ outb(p, v);
+ while (i-- > 0);
+}
+
+de6cmemcheck(sp)
+de6c_softc_t *sp;
+{
+ int i;
+ int off = 0;
+ int ret = 1;
+#ifdef MACH_KERNEL
+ unsigned short *memchk;
+ unsigned short *chkmem;
+ if (kmem_alloc(kernel_map, (vm_offset_t *)&memchk, BFRS * BFRSIZ) !=
+ KERN_SUCCESS ||
+ kmem_alloc(kernel_map, (vm_offset_t *)&chkmem, BFRS * BFRSIZ) !=
+ KERN_SUCCESS) {
+ printf("de6c: memory allocation failure!!\n");
+ return 0;
+ }
+#else /* MACH_KERNEL */
+ unsigned short *memchk = (unsigned short *) kmem_alloc(kernel_map, BFRS * BFRSIZ);
+ unsigned short *chkmem = (unsigned short *) kmem_alloc(kernel_map, BFRS * BFRSIZ);
+ if ( ! ((int) memchk) || ! ((int) chkmem)) {
+ printf("de6c: memory allocation failure!!\n");
+ return 0;
+ }
+#endif /* MACH_KERNEL */
+
+ for (i = 0; i < BFRS * BFRSIZ/sizeof (short); i++)
+ memchk[i] = i;
+ bzero(chkmem, BFRS * BFRSIZ);
+
+
+ for (off = 0; off < BFRS * BFRSIZ; off += BFRSIZ/2) {
+ de6cwrite(sp, off, memchk+(off/sizeof (short)), BFRSIZ/2);
+ de6cread (sp, off, chkmem+(off/sizeof (short)), BFRSIZ/2);
+ }
+
+ for (i = 0; i < BFRS * (BFRSIZ/sizeof (short)); i++)
+ if (memchk[i] != chkmem[i]) {
+ printf("de: tilt:seq [%x:%d] %x != %x\n",
+ i, i, memchk[i], chkmem[i]);
+ ret = 0;
+ break;
+ }
+
+ kmem_free(kernel_map, (vm_offset_t) memchk, BFRS * BFRSIZ);
+ kmem_free(kernel_map, (vm_offset_t) chkmem, BFRS * BFRSIZ);
+
+ return ret;
+}
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+/* d-link 600 OFF; d-link 600 OFF; d-link 600 OFF; d-link 600 OFF */
+
+#ifdef DEBUG
+#define STATIC
+STATIC int print_pkt(), print_bdy();
+STATIC int print_e_hdr(), print_ip_hdr(), print_ip();
+STATIC int print_ipa(), print_arp(), print_e(), print_chars();
+
+STATIC
+print_pkt(p, len)
+unsigned char *p;
+{
+ int j, k;
+ int type;
+
+ if (len < 18)
+ printf("print_pkt: too small %d\n", len);
+
+ type = print_e_hdr(p);
+
+ switch (type) {
+ case 0x806:
+ print_arp(p+14);
+ break;
+ case 0x800:
+ print_ip(p+14, len - 18);
+ break;
+ default:
+ for (j = 14; j < len; j +=20) {
+ for (k = 0; k < 20; k++)
+ printf("%2x ", p[j+k]);
+ printf("\n");
+ }
+ }
+}
+
+STATIC
+print_bdy(p, len, type)
+unsigned char *p;
+{
+ int j, k;
+
+ if (len < 18)
+ printf("print_pkt: too small %d|n", len);
+
+ switch (type) {
+ case 0x806:
+ print_arp(p);
+ break;
+ case 0x800:
+ print_ip(p, len);
+ break;
+ default:
+ for (j = 0; j < len; j +=20) {
+ for (k = 0; k < 20; k++)
+ printf("%2x ", p[j+k]);
+ printf("\n");
+ }
+ }
+}
+
+STATIC
+print_e_hdr(p)
+unsigned char *p;
+{
+ int type = ntohs(((unsigned short *)p)[6]);
+
+ printf("S=%x:%x:%x:%x:%x:%x, ", p[6], p[7], p[8], p[9], p[10], p[11]);
+ printf("D=%x:%x:%x:%x:%x:%x, ", p[0], p[1], p[2], p[3], p[4], p[5]);
+ printf("T=%x\n", type);
+
+ return type;
+}
+
+STATIC
+print_ip_hdr(u)
+u_char *u;
+{
+
+ int l = ntohs(*(u_short *)(u+2));
+
+ print_ipa(u+12);
+ printf(" -> ");
+ print_ipa(u+12+4);
+ printf(" L%d(0x%x)\n", l, l);
+}
+
+STATIC
+print_ip(p, len)
+unsigned char *p;
+{
+ int j,k;
+
+ print_ip_hdr(p);
+ for (k =0; k < 12; k++)
+ printf("%2x ", p[k]);
+ print_ipa(p+12);
+ printf(" ");
+ print_ipa(p+12+4);
+ printf("\n");
+ for (j = 20; j < len; j +=16) {
+ for (k = 0; k < 16; k++)
+ printf("%2x ", p[j+k]);
+ print_chars(&p[j], 16);
+ printf("\n");
+ }
+}
+
+STATIC
+print_ipa(u)
+u_char *u;
+{
+ printf("%d.%d.%d.%d", u[0], u[1], u[2], u[3]);
+}
+
+STATIC
+print_arp(p)
+#ifdef MACH_KERNEL
+{}
+#else MACH_KERNEL
+struct arphdr *p;
+{
+ u_char *u = (u_char *)(p+1);
+
+ printf("op = %x, pro = %x, hln = %x, pln = %x\n",
+ ntohs(p->ar_op), ntohs(p->ar_pro), p->ar_hln, p->ar_pln);
+
+ print_e(u);
+ print_ipa(u+p->ar_hln);
+ printf(" seeks\n");
+
+ print_e(u+p->ar_hln+p->ar_pln);
+ print_ipa(u+p->ar_hln+p->ar_pln+p->ar_hln);
+ printf("\n");
+
+}
+#endif MACH_KERNEL
+
+STATIC
+print_e(u)
+u_char *u;
+{
+ printf("%x:%x:%x:%x:%x:%x ", u[0], u[1], u[2], u[3], u[4], u[5]);
+}
+
+STATIC
+print_chars(u, len)
+u_char *u;
+{
+ int c;
+
+ printf("|>");
+ while (len--) {
+ c = *u++;
+ if (c < 0x7f && c > 0x1f)
+ printf("%c", c);
+ else
+ printf(" ");
+ }
+ printf("<|");
+}
+#endif DEBUG
diff --git a/i386/i386at/if_de6c.h b/i386/i386at/if_de6c.h
new file mode 100644
index 00000000..9d8e9b85
--- /dev/null
+++ b/i386/i386at/if_de6c.h
@@ -0,0 +1,113 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: if_de6c.h,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:26 thomas
+ * Imported from UK22
+ *
+ * Revision 1.2 1994/11/08 20:47:25 baford
+ * merged in CMU's MK83-MK83a diffs
+ *
+ * Revision 2.2 93/11/17 18:32:40 dbg
+ * Moved source into kernel/i386at/DLINK/if_de6c.c, since we
+ * can't release it but don't want to lose it.
+ * [93/11/17 dbg]
+ *
+ * Removed local declaration of HZ.
+ * [93/01/29 dbg]
+ *
+ * Created.
+ * [92/08/13 rvb]
+ *
+ */
+
+/* PC/FTP Packet Driver source, conforming to version 1.09 of the spec
+ * Portions (C) Copyright 1990 D-Link, Inc.
+ *
+ * Permission is granted to any individual or institution to use, copy,
+ * modify, or redistribute this software and its documentation provided
+ * this notice and the copyright notices are retained. This software may
+ * not be distributed for profit, either in original form or in derivative
+ * works. D-Link, inc. makes no representations about the suitability
+ * of this software for any purpose. D-LINK GIVES NO WARRANTY,
+ * EITHER EXPRESS OR IMPLIED, FOR THE PROGRAM AND/OR DOCUMENTATION
+ * PROVIDED, INCLUDING, WITHOUT LIMITATION, WARRANTY OF MERCHANTABILITY
+ * AND WARRANTY OF FITNESS FOR A PARTICULAR PURPOSE.
+ */
+
+
+#define DATA(port) (port + 0)
+#define STAT(port) (port + 1)
+#define CMD(port) (port + 2)
+
+/* DE-600's DATA port Command */
+#define WRITE 0x00 /* write memory */
+#define READ 0x01 /* read memory */
+#define STATUS 0x02 /* read status register */
+#define COMMAND 0x03 /* write command register */
+#define RX_NONE 0x00 /* M1=0, M0=0 (bit 1,0) */
+#define RX_ALL 0x01 /* M1=0, M0=1 */
+#define RX_BP 0x02 /* M1=1, M0=0 */
+#define RX_MBP 0x03 /* M1=1, M0=1 */
+#define TXEN 0x04 /* bit 2 */
+#define RXEN 0x08 /* bit 3 */
+#define LOOPBACK 0x0c /* RXEN=1, TXEN=1 */
+#define IRQINV 0x40 /* bit 6 -- IRQ inverse */
+#define RESET 0x80 /* set bit 7 high */
+#define STOP_RESET 0x00 /* set bit 7 low */
+#define NUL_CMD 0x04 /* null command */
+#define RX_LEN 0x05 /* read Rx packet length */
+#define TX_ADR 0x06 /* write Tx address */
+#define RW_ADR 0x07 /* write memory address */
+
+/* DE-600's CMD port Command */
+/* #define CMD(port) (port + 2) */
+#define SLT_NIC 0x04 /* select Network Interface Card */
+#define SLT_PRN 0x1c /* select Printer */
+#define NML_PRN 0xec /* normal Printer situation */
+#define IRQEN 0x10 /* enable IRQ line */
+
+/* DE-600's STAT port bits 7-4 */
+/* #define STAT(port) (port + 1) */
+#define RXBUSY 0x80
+#define GOOD 0x40
+#define RESET_FLAG 0x20
+#define T16 0x10
+#define TXBUSY 0x08
+
+#define STROBE 0x08
+#define EADDR 0x2000 /* HA13=0 => Mem, HA13=1 => Node Num */
+#define BFRSIZ 0x0800 /* number of bytes in a buffer */
+#define BFRS 4
+
+#define DSF_LOCK 1
+#define DSF_RUNNING 2
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
+
diff --git a/i386/i386at/if_de6s.S b/i386/i386at/if_de6s.S
new file mode 100644
index 00000000..cc697a23
--- /dev/null
+++ b/i386/i386at/if_de6s.S
@@ -0,0 +1,278 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1992 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ * HISTORY
+ * $Log: if_de6s.S,v $
+ * Revision 1.1.1.1 1996/10/30 01:39:26 thomas
+ * Imported from UK22
+ *
+# Revision 1.3 1995/04/26 19:22:12 baford
+# got alignment working right for both ELF and a.out
+#
+# Revision 1.2 1995/04/25 16:13:28 baford
+# got kernel working with ELF build tools
+#
+# Revision 1.1 1994/11/08 20:47:25 baford
+# merged in CMU's MK83-MK83a diffs
+#
+ * Revision 2.2 93/11/17 18:33:19 dbg
+ * Moved source into kernel/i386at/DLINK/if_de6c.c, since we
+ * can't release it but don't want to lose it.
+ * [93/11/17 dbg]
+ *
+ * Revision 2.2.2.1 93/09/21 21:00:39 dbg
+ * <no changes>
+ *
+ * Revision 2.2.1.1 93/09/03 15:06:26 dbg
+ * Created.
+ * [92/08/13 rvb]
+ *
+ *
+ * File: if_de6s.s
+ * Author: Robert V. Baron
+ */
+
+#include <mach/machine/asm.h>
+#undef DATA
+
+#include <i386at/if_de6c.h>
+
+ P2ALIGN(2)
+de6csetmemaddr:
+ movl 8(%ebp), %ebx /* addr */
+ movb %bl, %al /* low byte; low nibble */
+ salb $4, %al
+ orb $(RW_ADR), %al
+ outb %al, %dx
+ movl %edi, %ecx
+0: loop 0b
+
+ movb %bl, %al /* low byte; high nibble */
+ andb $0xf0, %al
+ orb $(RW_ADR|STROBE), %al
+ outb %al, %dx
+ movl %edi, %ecx
+0: loop 0b
+
+ movb %bh, %al /* high byte; low nibble */
+ salb $4, %al
+ orb $(RW_ADR), %al
+ outb %al, %dx
+ movl %edi, %ecx
+0: loop 0b
+
+ movb %bh, %al /* high byte; high nibble */
+ andb $0xf0, %al
+ orb $(RW_ADR|STROBE), %al
+ outb %al, %dx
+ movl %edi, %ecx
+0: loop 0b
+ ret
+
+/* de6cwriteasm(address, buf, len, port, delay) */
+ENTRY(de6cwriteasm)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ movl 20(%ebp), %edx /* port */
+ movl 24(%ebp), %edi /* delay */
+
+ call de6csetmemaddr
+
+ cld
+ movl 16(%ebp), %ecx /* cnt */
+ movl 12(%ebp), %esi /* source */
+
+ cmpl $1, %edi /* no delay/latency */
+ je 2f
+1: lodsb /* leave delay/latency */
+ pushl %ecx
+ movb %al, %bl /* high byte; low nibble */
+ salb $4, %al
+ outb %al, %dx
+ movl %edi, %ecx
+0: loop 0b
+
+ movb %bl, %al /* high byte; high nibble */
+ andb $0xf0, %al
+ orb $(WRITE|STROBE), %al /* NB: WRITE == 0 */
+ outb %al, %dx
+ movl %edi, %ecx
+0: loop 0b
+ popl %ecx
+ loop 1b
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+ /* edi and ebx free */
+2: lodsb
+ movb %al, %bl /* high byte; low nibble */
+ salb $4, %al
+ outb %al, %dx
+
+ movb %bl, %al /* high byte; high nibble */
+ andb $0xf0, %al
+ orb $(WRITE|STROBE), %al /* NB: WRITE == 0 */
+ outb %al, %dx
+ loop 2b
+
+6: popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+
+
+/* de6creadasm(address, buf, len, port, delay) */
+ENTRY(de6creadasm)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %edi
+ pushl %esi
+ pushl %ebx
+ movl 20(%ebp), %edx /* port */
+ movl 24(%ebp), %edi /* delay for desetmemaddr*/
+ movl %edi, %esi /* delay; cause edi used by stosl */
+
+ call de6csetmemaddr
+
+ cld
+ movl 16(%ebp), %ecx /* cnt */
+ movl 12(%ebp), %edi /* destination */
+ movw $0x0901, %bx /* bl = 1 = READ; bh = READ|STROBE */
+
+#ifdef out_in
+ cmpl $1, %esi /* no delay/latency */
+ je 2f
+#endif /* out_in */
+1: xorw %ax, %ax /* leave delay/latency */
+ pushl %ecx
+ movb %bl, %al
+ outb %al, %dx
+ movl %esi, %ecx
+0: loop 0b
+ incw %dx /* inb from STAT == port + 1 */
+ inb %dx, %al /* first byte high nibble goes into */
+ decw %dx
+ salw $4, %ax /* ... low nibble formed byte */
+
+ movb %bh, %al
+ outb %al, %dx
+ movl %esi, %ecx
+0: loop 0b
+ incw %dx /* inb from STAT == port + 1 */
+ inb %dx, %al /* second byte high nibble goes into */
+ decw %dx
+ andb $0xf0, %al /* ... high nibble formed byte */
+ orb %ah, %al
+ stosb
+ popl %ecx
+ loop 1b
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+
+2: xorw %ax, %ax /* leave delay/latency */
+ movb %bl, %al
+ outb %al, %dx
+ incw %dx /* inb from STAT == port + 1 */
+ inb %dx, %al /* high nibble goes into low nibble */
+ decw %dx
+ salw $4, %ax
+
+ movb %bh, %al
+ outb %al, %dx
+ incw %dx /* inb from STAT == port + 1 */
+ inb %dx, %al
+ decw %dx
+ andb $0xf0, %al
+ orb %ah, %al
+ stosb
+ loop 2b
+
+ popl %ebx
+ popl %esi
+ popl %edi
+ leave
+ ret
+
+
+#ifdef unroll_wins
+ unrolled loop for write iff no delay
+2: lodsl
+ movl %eax, %ebx /* byte one; low nibble */
+ salb $4, %al
+ outb %al, %dx
+
+ movb %bl, %al /* byte one; high nibble */
+ andb $0xf0, %al
+ orb $8, %al
+ outb %al, %dx
+loop 3f
+jmp 6f
+3: sarl $8, %ebx
+ movb %bl, %al /* byte two; low nibble */
+ salb $4, %al
+ outb %al, %dx
+
+ movb %bl, %al /* byte two; high nibble */
+ andb $0xf0, %al
+ orb $8, %al
+ outb %al, %dx
+loop 4f
+jmp 6f
+4: sarl $8, %ebx
+ movb %bl, %al /* byte three; low nibble */
+ salb $4, %al
+ outb %al, %dx
+
+ movb %bl, %al /* byte three; high nibble */
+ andb $0xf0, %al
+ orb $8, %al
+ outb %al, %dx
+loop 5f
+jmp 6f
+5: sarl $8, %ebx
+ movb %bl, %al /* byte three; low nibble */
+ salb $4, %al
+ outb %al, %dx
+
+ movb %bl, %al /* byte four; high nibble */
+ andb $0xf0, %al
+ orb $8, %al
+ outb %al, %dx
+ loop 2b
+#endif /* unroll_wins */
+
diff --git a/i386/i386at/if_ne.c b/i386/i386at/if_ne.c
new file mode 100644
index 00000000..9a950d69
--- /dev/null
+++ b/i386/i386at/if_ne.c
@@ -0,0 +1,1081 @@
+/*-
+ * Copyright (c) 1990, 1991 William F. Jolitz.
+ * Copyright (c) 1990 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_ne.c 7.4 (Berkeley) 5/21/91
+ */
+
+/*
+ * NE2000 Ethernet driver
+ *
+ * Parts inspired from Tim Tucker's if_wd driver for the wd8003,
+ * insight on the ne2000 gained from Robert Clements PC/FTP driver.
+ */
+
+#include <ne.h>
+
+
+#if NNE > 0
+#ifdef MACH_KERNEL
+
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include <i386at/ds8390.h>
+#include <i386at/if_nereg.h>
+#include <i386/ipl.h>
+#include <chips/busses.h>
+#ifdef FIPC
+#include <ipc/fipc.h>
+#endif /* FIPC */
+
+#else MACH_KERNEL
+
+#include <sys/param.h>
+#include <mach/vm_param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/table.h>
+#include <sys/buf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/vmmac.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+#include <vm/vm_kern.h>
+
+#include <net/if.h>
+#include <net/if_types.h>
+#include <net/route.h>
+
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+
+
+#include <i386/ipl.h>
+#include <i386at/atbus.h>
+#include <i386at/ds8390.h>
+#include <i386at/if_nereg.h>
+#include <i386/handler.h>
+#include <i386/dispatcher.h>
+int ether_output();
+int neioctl();
+
+#endif
+
+
+int neprobe();
+void neattach();
+int neintr();
+int nestart();
+int neinit();
+
+static vm_offset_t ne_std[NNE] = {0};
+static struct bus_device *ne_info[NNE];
+struct bus_driver nedriver =
+ { neprobe, 0, neattach, 0, ne_std, "ne", ne_info, 0, 0, 0 };
+
+#define ETHER_MIN_LEN 64
+#define ETHER_MAX_LEN 1536
+#define SPLNET spl6
+/*
+ * Ethernet software status per interface.
+ *
+ * Each interface is referenced by a network interface structure,
+ * ns_if, which the routing code uses to locate the interface.
+ * This structure contains the output queue for the interface, its address, ...
+ */
+typedef struct {
+
+#ifdef MACH_KERNEL
+ struct ifnet ds_if;
+ u_char ds_addr[6];
+#else MACH_KERNEL
+ struct arpcom ns_ac; /* Ethernet common part */
+#define ds_if ns_ac.ac_if /* network-visible interface */
+#define ds_addr ns_ac.ac_enaddr /* hardware Ethernet address */
+#endif MACH_KERNEL
+
+ int ns_flags;
+#define DSF_LOCK 1 /* block re-entering enstart */
+#define DSF_RUNNING 2
+ int ns_oactive ;
+ int ns_mask ;
+ int ns_ba; /* byte addr in buffer ram of inc pkt */
+ int ns_cur; /* current page being filled */
+ struct prhdr ns_ph; /* hardware header of incoming packet*/
+ struct ether_header ns_eh; /* header of incoming packet */
+ u_char ns_pb[2048 /*ETHERMTU+sizeof(long)*/];
+ short ns_txstart; /* transmitter buffer start */
+ short ns_rxend; /* recevier buffer end */
+ short ns_rxbndry; /* recevier buffer boundary */
+ caddr_t ns_port; /* i/o port base */
+ short ns_mode; /* word/byte mode */
+ int mode;
+ short card_present;
+#ifndef MACH_KERNEL
+ ihandler_t handler;
+ ihandler_id_t *handler_id;
+#endif MACH_KERNEL
+
+} ne_softc_t;
+ne_softc_t ne_softc[NNE];
+
+#define PAT(n) (0xa55a + 37*(n))
+
+u_short boarddata[16];
+
+/*
+ * Fetch from onboard ROM/RAM
+ */
+nefetch (ns, up, ad, len) ne_softc_t *ns; caddr_t up; {
+ u_char cmd;
+ caddr_t nec = ns->ns_port;
+ int counter = 10000;
+ int t_len;
+ unsigned char last_word[2];
+ char odd;
+
+ cmd = inb (nec + ds_cmd);
+ outb (nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+
+ /* Setup remote dma */
+ outb (nec + ds0_isr, DSIS_RDC);
+
+ t_len = len;
+ if ((ns->ns_mode & DSDC_WTS) && len&1) {
+ odd=1;
+ t_len++; /* roundup to words */
+ } else odd=0;
+
+ outb (nec+ds0_rbcr0, t_len);
+ outb (nec+ds0_rbcr1, t_len>>8);
+ outb (nec+ds0_rsar0, ad);
+ outb (nec+ds0_rsar1, ad>>8);
+
+ /* Execute & extract from card */
+ outb (nec+ds_cmd, DSCM_RREAD|DSCM_PG0|DSCM_START);
+
+ if (ns->ns_mode & DSDC_WTS)
+ if (odd) {
+ linw (nec+ne_data, up, len/2);
+ *(last_word) = inw(nec+ne_data); /* get last word */
+ *(up+len-1) = last_word[0]; /* last byte */
+ } else {
+ linw (nec+ne_data, up, len/2);
+ }
+ else
+ linb (nec+ne_data, up, len);
+
+
+ /* Wait till done, then shutdown feature */
+ while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
+ ;
+
+ outb (nec+ds0_isr, DSIS_RDC);
+ outb (nec+ds_cmd, cmd);
+}
+
+/*
+ * Put to onboard RAM
+ */
+neput (ns, up, ad, len) ne_softc_t *ns; caddr_t up; {
+ u_char cmd;
+ caddr_t nec = ns->ns_port;
+ int counter = 10000;
+ int t_len;
+ int odd;
+ unsigned char last_word[2];
+
+ cmd = inb(nec+ds_cmd);
+ outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+
+ /* Setup for remote dma */
+ outb (nec+ds0_isr, DSIS_RDC);
+
+ t_len = len;
+ if ((ns->ns_mode & DSDC_WTS) && len&1) {
+ odd = 1;
+ t_len++; /* roundup to words */
+ } else odd = 0;
+
+ outb (nec+ds0_rbcr0, t_len);
+ outb (nec+ds0_rbcr1, t_len>>8);
+ outb (nec+ds0_rsar0, ad);
+ outb (nec+ds0_rsar1, ad>>8);
+
+ /* Execute & stuff to card */
+ outb (nec+ds_cmd, DSCM_RWRITE|DSCM_PG0|DSCM_START);
+ if (ns->ns_mode & DSDC_WTS) {
+ if (odd) {
+ loutw (nec+ne_data, up, len/2);
+ last_word[0] = *(up+len-1);
+ outw (nec+ne_data, (unsigned short) *(last_word));
+ }
+ else {
+ loutw (nec+ne_data, up, len/2);
+ }
+ }
+ else
+ loutb (nec+ne_data, up, len);
+
+
+ /* Wait till done, then shutdown feature */
+ while ((inb (nec+ds0_isr) & DSIS_RDC) == 0 && counter-- > 0)
+ ;
+
+ outb (nec+ds0_isr, DSIS_RDC);
+ outb (nec+ds_cmd, cmd);
+}
+
+int
+neprobe(port, dev)
+struct bus_device *dev;
+{
+ int val, i, sum, bytemode = 1, pat;
+ int unit = dev->unit;
+ ne_softc_t *ns = &ne_softc[unit];
+ caddr_t nec;
+
+ if ((unsigned) unit >= NNE)
+ return(0);
+
+ nec = (caddr_t) ns->ns_port = dev->address;
+
+ if (ns->card_present) {
+ printf("ne%s : card already present in port %x\n",
+ unit, nec);
+ return(0);
+ }
+
+ if (bytemode) {
+ /* Byte Transfers, Burst Mode Select, Fifo at 8 bytes */
+ ns->ns_mode = DSDC_BMS|DSDC_FT1;
+ ns->ns_txstart = TBUF8;
+ ns->ns_rxend = RBUFEND8;
+ } else {
+word:
+ /* Word Transfers, Burst Mode Select, Fifo at 8 bytes */
+ ns->ns_mode = DSDC_WTS|DSDC_BMS|DSDC_FT1;
+ ns->ns_txstart = TBUF16;
+ ns->ns_rxend = RBUFEND16;
+ bytemode = 0;
+ }
+
+ /* Reset the bastard */
+ val = inb(nec + ne_reset);
+ delay(200);
+ outb(nec + ne_reset, val);
+ delay(200);
+
+ outb(nec + ds_cmd, DSCM_STOP|DSCM_NODMA);
+
+ i = 10000;
+ while ((inb(nec + ds0_isr) & DSIS_RESET) == 0 && i-- > 0);
+ if (i < 0) return (0);
+
+ outb(nec + ds0_isr, 0xff);
+ outb(nec + ds0_dcr, ns->ns_mode);
+ outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+ delay(1000);
+
+ /* Check cmd reg and fail if not right */
+ if ((i = inb(nec + ds_cmd)) != (DSCM_NODMA|DSCM_PG0|DSCM_STOP))
+ return(0);
+
+ outb(nec + ds0_tcr, DSTC_LB0);
+ outb(nec + ds0_rcr, DSRC_MON);
+ outb(nec + ds0_pstart, ns->ns_txstart+PKTSZ);
+ outb(nec + ds0_pstop, ns->ns_rxend);
+ outb(nec + ds0_bnry, ns->ns_rxend);
+ outb(nec + ds0_imr, 0);
+ outb(nec + ds0_isr, 0);
+ outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
+ outb(nec + ds1_curr, ns->ns_txstart+PKTSZ);
+ outb(nec + ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+
+#ifdef NEDEBUG
+#define RCON 37
+ { int i, rom;
+
+ rom=1;
+ printf("ne ram ");
+
+ for (i = 0; i < 0xfff0; i+=4) {
+ pat = PAT(i);
+ neput(ns, &pat,i,4);
+ nefetch(ns, &pat,i,4);
+ if (pat == PAT(i)) {
+ if (rom) {
+ rom=0;
+ printf(" %x", i);
+ }
+ } else {
+ if (!rom) {
+ rom=1;
+ printf("..%x ", i);
+ }
+ }
+ pat=0;
+ neput(ns, &pat,i,4);
+ }
+ printf("\n");
+ }
+#endif
+
+ /*
+ * <groan> detect difference between units
+ * solely by where the RAM is decoded.
+ */
+ pat = PAT(0);
+ neput (ns, &pat, ns->ns_txstart*DS_PGSIZE, 4);
+ nefetch(ns, &pat, ns->ns_txstart*DS_PGSIZE, 4);
+ if (pat != PAT(0)) {
+ if (bytemode)
+ goto word;
+ else return (0);
+ }
+
+
+ /* Extract board address */
+ nefetch (ns, (caddr_t)boarddata, 0, sizeof(boarddata));
+
+ for(i=0; i < 6; i++)
+ ns->ds_addr[i] = boarddata[i];
+ ns->card_present = 1;
+ return (1);
+}
+
+/*
+ * Interface exists: make available by filling in network interface
+ * record. System will initialize the interface when it is ready
+ * to accept packets. We get the ethernet address here.
+ */
+void
+neattach(dev)
+struct bus_device *dev;
+{
+ short unit = dev->unit;
+ ne_softc_t *ns = &ne_softc[unit];
+ register struct ifnet *ifp = &(ns->ds_if);
+
+ if ((unsigned) unit >= NNE)
+ return;
+
+#ifdef MACH_KERNEL
+ take_dev_irq(dev);
+#else MACH_KERNEL
+ /* setup intr handler */
+ ns->handler.ih_level = dev->dev_pic;
+ ns->handler.ih_handler = dev->dev_intr[0];
+ ns->handler.ih_resolver = i386_resolver;
+ ns->handler.ih_rdev = dev;
+ ns->handler.ih_stats.intr_type = INTR_DEVICE;
+ ns->handler.ih_stats.intr_cnt = 0;
+ ns->handler.ih_hparam[0].intparam = unit;
+ if ((ns->handler_id = handler_add(&ns->handler)) != NULL)
+ handler_enable(ns->handler_id);
+ else
+ panic("Unable to add NEx000 interrupt handler");
+#endif MACH_KERNEL
+ printf (", port = %x, spl = %d, pic = %d, [%s].",
+ dev->address, dev->sysdep, dev->sysdep1,
+ ether_sprintf(ns->ds_addr));
+#ifndef MACH_KERNEL
+ ns->ns_ac.ac_bcastaddr = (u_char *)etherbroadcastaddr;
+ ns->ns_ac.ac_arphrd = ARPHRD_ETHER;
+#endif MACH_KERNEL
+ ns->ns_flags = 0;
+ ns->mode = 0;
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+#ifdef MACH_KERNEL
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&ns->ds_addr[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = nedriver.driver_dname;
+ ifp->if_init = neinit;
+ ifp->if_output = ether_output;
+ ifp->if_start = nestart;
+ ifp->if_ioctl = neioctl;
+ ifp->if_reset = nereset;
+ ifp->if_watchdog= 0;
+ if_attach(ifp);
+#endif MACH_KERNEL
+}
+
+/*
+ * Initialization of interface; set up initialization block
+ * and transmit/receive descriptor rings.
+ */
+neinit(unit)
+ int unit;
+{
+ ne_softc_t *ns = &ne_softc[unit];
+ struct ifnet *ifp = &ns->ds_if;
+ int i; char *cp;
+ int oldpri;
+ caddr_t nec = ns->ns_port;
+
+#ifndef MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) return;
+#endif MACH_KERNEL
+
+ oldpri = SPLNET();
+
+ outb(nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+
+ /* Word Transfer select, Burst Mode Select, Fifo at 8 bytes */
+ outb(nec+ds0_dcr, ns->ns_mode);
+
+ /* clear remote byte count resigters */
+ outb (nec+ds0_rbcr0, 0);
+ outb (nec+ds0_rbcr1, 0);
+
+ /* don't store incoming packets into memory for now */
+ outb (nec+ds0_rcr, DSRC_MON);
+
+ /* place NIC in internal loopback mode */
+ outb(nec+ds0_tcr, DSTC_LB0);
+
+ /* initialize transmit/recieve (ring-buffer) Page Start */
+ outb (nec+ds0_tpsr, 0);
+ outb (nec+ds0_pstart, ns->ns_txstart+PKTSZ);
+
+ /* initialize reciever (ring-buffer) Page Stop and Boundary */
+ outb (nec+ds0_pstop, ns->ns_rxend);
+ outb (nec+ds0_bnry, ns->ns_txstart+PKTSZ);
+
+ /* clear all interrupts */
+ outb (nec+ds0_isr, 0xff);
+
+ /* enable the interrupts that we care about */
+ outb (nec+ds0_imr, IMR_ENABLE);
+
+ /* set physical address on ethernet */
+ outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG1|DSCM_STOP);
+ for (i=0 ; i < 6 ; i++) outb(nec+ds1_par0+i,ns->ds_addr[i]);
+
+ ns->ns_cur = ns->ns_txstart+PKTSZ + 1;
+ outb (nec+ds1_curr, ns->ns_cur);
+
+ /* XXX deal with Reciever Configuration Register */
+ /* clr logical address hash filter for now */
+ for (i=0 ; i < 8 ; i++) outb(nec+ds1_mar0+i,0xff);
+
+ /* set page 0 registers */
+ outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_STOP);
+ outb (nec+ds0_rcr, DSRC_AB);
+
+ /* take unit out of loopback mode */
+ outb (nec+ds0_tcr, 0);
+
+ ns->ds_if.if_flags |= IFF_RUNNING;
+ ns->ns_flags &= ~(DSF_LOCK|DSF_RUNNING);
+ ns->ns_oactive = 0; ns->ns_mask = ~0;
+ splx(oldpri);
+ nestart(unit);
+ return(1);
+}
+
+/*
+ * Setup output on interface.
+ * Get another datagram to send off of the interface queue,
+ * and map it to the interface before starting the output.
+ * called only at splimp or interrupt level.
+ */
+nestart(unit)
+int unit;
+{
+ ne_softc_t *ns = &ne_softc[unit];
+ struct ifnet *ifp = &ns->ds_if;
+ int buffer;
+ int len, i, total,t;
+ caddr_t nec = ns->ns_port;
+#ifdef MACH_KERNEL
+ io_req_t m;
+
+#else MACH_KERNEL
+ struct mbuf *m0, *m;
+#endif MACH_KERNEL
+
+ /*
+ * The DS8390 has only one transmit buffer, if it is busy we
+ * must wait until the transmit interrupt completes.
+ */
+ outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
+
+ if (ns->ns_flags & DSF_LOCK)
+ goto done;
+
+ if (inb(nec+ds_cmd) & DSCM_TRANS)
+ goto done;
+
+ if ((ns->ds_if.if_flags & IFF_RUNNING) == 0)
+ goto done;
+
+ IF_DEQUEUE(&ns->ds_if.if_snd, m);
+ if (m == 0)
+ goto done;
+
+ /*
+ * Copy the mbuf chain into the transmit buffer
+ */
+
+ ns->ns_flags |= DSF_LOCK; /* prevent entering nestart */
+ buffer = ns->ns_txstart*DS_PGSIZE;
+#ifdef MACH_KERNEL
+ total = m->io_count;
+ neput(ns, m->io_data, buffer, total);
+#else MACH_KERNEL
+ t = 0; len = i = 0;
+ for (m0 = m; m != 0; m = m->m_next)
+ t += m->m_len;
+
+ m = m0;
+ total = t;
+ for (m0 = m; m != 0; ) {
+
+ if (m->m_len&1 && t > m->m_len) {
+ neput(ns, mtod(m, caddr_t), buffer, m->m_len - 1);
+ t -= m->m_len - 1;
+ buffer += m->m_len - 1;
+ m->m_data += m->m_len - 1;
+ m->m_len = 1;
+ m = m_pullup(m, 2);
+ } else {
+ neput(ns, mtod(m, caddr_t), buffer, m->m_len);
+ buffer += m->m_len;
+ t -= m->m_len;
+ MFREE(m, m0);
+ m = m0;
+ }
+ }
+#endif MACH_KERNEL
+ /*
+ * Init transmit length registers, and set transmit start flag.
+ */
+
+ len = total;
+ if (len < ETHER_MIN_LEN) len = ETHER_MIN_LEN;
+ outb(nec+ds0_tbcr0,len&0xff);
+ outb(nec+ds0_tbcr1,(len>>8)&0xff);
+ outb(nec+ds0_tpsr, ns->ns_txstart);
+ outb(nec+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
+
+#ifdef MACH_KERNEL
+ iodone(m);
+ m = 0;
+done:
+#endif MACH_KERNEL
+}
+
+/* buffer successor/predecessor in ring? */
+#define succ(n) (((n)+1 >= ns->ns_rxend) ? (ns->ns_txstart+PKTSZ) : (n)+1)
+#define pred(n) (((n)-1 < (ns->ns_txstart+PKTSZ)) ? ns->ns_rxend-1 : (n)-1)
+
+/*
+ * Controller interrupt.
+ */
+neintr(unit)
+{
+ ne_softc_t *ns = &ne_softc[unit];
+ u_char cmd,isr;
+ caddr_t nec = ns->ns_port;
+
+ /* Save cmd, clear interrupt */
+ cmd = inb (nec+ds_cmd);
+ isr = inb (nec+ds0_isr);
+loop:
+ outb(nec+ds_cmd,DSCM_NODMA|DSCM_START);
+ outb(nec+ds0_isr, isr);
+
+ /* Receiver error */
+ if (isr & DSIS_RXE) {
+ (void) inb(nec+ds0_rsr);
+ /* need to read these registers to clear status */
+ ns->ds_if.if_ierrors++;
+ }
+
+ /* Counters overflowed, reading the registers resets them */
+ if (isr & DSIS_CTRS) {
+ (void) inb(nec+ds0_cntr0);
+ (void) inb(nec+ds0_cntr1);
+ (void) inb(nec+ds0_cntr2);
+ }
+
+
+ /* We received something; rummage thru tiny ring buffer */
+ if (isr & (DSIS_RX|DSIS_RXE|DSIS_ROVRN)) {
+ u_char pend,lastfree;
+
+ outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
+ pend = inb(nec+ds1_curr);
+ outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
+
+ /* Something in the buffer? */
+ while (pend != ns->ns_cur) {
+ /* Extract header from microcephalic board */
+ nefetch(ns, &ns->ns_ph,ns->ns_cur*DS_PGSIZE,
+ sizeof(ns->ns_ph));
+ ns->ns_ba = ns->ns_cur*DS_PGSIZE+sizeof(ns->ns_ph);
+
+ /* Incipient paranoia */
+ if (ns->ns_ph.pr_status == DSRS_RPC ||
+ /* for dequna's */
+ ns->ns_ph.pr_status == 0x21) {
+ if (nerecv(ns))
+ ns->ns_cur = ns->ns_ph.pr_nxtpg ;
+ else {
+ outb(nec+ds0_bnry, pred(ns->ns_cur));
+ goto short_load;
+ }
+ }
+#ifdef NEDEBUG
+ else {
+ printf("cur %x pnd %x lfr %x ",
+ ns->ns_cur, pend, lastfree);
+ printf("nxt %x len %x ", ns->ns_ph.pr_nxtpg,
+ (ns->ns_ph.pr_sz1<<8)+ ns->ns_ph.pr_sz0);
+ printf("Bogus Sts %x\n", ns->ns_ph.pr_status);
+ ns->ns_cur = pend;
+ }
+#endif
+ outb(nec+ds0_bnry, pred(ns->ns_cur));
+ outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG1);
+ pend = inb(nec+ds1_curr);
+ outb(nec+ds_cmd, DSCM_START|DSCM_NODMA|DSCM_PG0);
+ }
+short_load:
+ outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
+ }
+
+ /* Transmit error */
+ if (isr & DSIS_TXE) {
+ ns->ns_flags &= ~DSF_LOCK;
+ /* Need to read these registers to clear status */
+ ns->ds_if.if_collisions += inb(nec+ds0_tbcr0);
+ ns->ds_if.if_oerrors++;
+ }
+
+ /* Packet Transmitted */
+ if (isr & DSIS_TX) {
+ ns->ns_flags &= ~DSF_LOCK;
+ ++ns->ds_if.if_opackets;
+ ns->ds_if.if_collisions += inb(nec+ds0_tbcr0);
+ }
+
+ /* Receiver ovverun? */
+ if (isr & DSIS_ROVRN) {
+ outb(nec+ds0_rbcr0, 0);
+ outb(nec+ds0_rbcr1, 0);
+ outb(nec+ds0_tcr, DSTC_LB0);
+ outb(nec+ds0_rcr, DSRC_MON);
+ outb(nec+ds_cmd, DSCM_START|DSCM_NODMA);
+ outb(nec+ds0_rcr, DSRC_AB);
+ outb(nec+ds0_tcr, 0);
+ }
+
+ /* Any more to send? */
+ outb (nec+ds_cmd, DSCM_NODMA|DSCM_PG0|DSCM_START);
+ nestart(unit);
+ outb (nec+ds_cmd, cmd);
+ outb (nec+ds0_imr, IMR_ENABLE);
+
+ /* Still more to do? */
+ isr = inb (nec+ds0_isr);
+ if(isr) goto loop;
+
+ return 0;
+}
+
+/*
+ * Ethernet interface receiver interface.
+ * If input error just drop packet.
+ * Otherwise examine packet to determine type. If can't determine length
+ * from type, then have to drop packet. Othewise decapsulate
+ * packet based on type and pass to type specific higher-level
+ * input routine.
+ */
+nerecv(ns)
+ ne_softc_t *ns;
+{
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+ register struct ifnet *ifp = &ns->ds_if;
+#ifdef FIPC
+ char *fipc_buf;
+#endif
+#else MACH_KERNEL
+ struct mbuf *top, **mp, *m, *p;
+#endif MACH_KERNEL
+ int len, l;
+ int epkt;
+
+ ns->ds_if.if_ipackets++;
+ len = ns->ns_ph.pr_sz0 + (ns->ns_ph.pr_sz1<<8);
+ if(len < ETHER_MIN_LEN || len > ETHER_MAX_LEN)
+ return 0;
+
+ nefetch(ns, &ns->ns_eh, ns->ns_ba, sizeof(struct ether_header));
+
+#ifndef MACH_KERNEL
+ ns->ns_eh.ether_type = ntohs((u_short)ns->ns_eh.ether_type);
+#endif MACH_KERNEL
+ ns->ns_ba += sizeof(struct ether_header);
+
+ /* don't forget checksum! */
+ len -= (sizeof(struct ether_header) + sizeof(long));
+#ifdef MACH_KERNEL
+#ifdef FIPC
+ if (ns->ns_eh.ether_type == FIPC_MSG_TYPE) /* fipc packet */
+ {
+ /* We need to hand the whole packet to the handler. */
+
+ fipc_recvs++;
+
+ fipc_buf = get_fipc_buffer (len, TRUE, TRUE);
+
+ if (fipc_buf == NULL)
+ {
+ ns->ds_if.if_rcvdrops++;
+ return(0);
+ }
+ nefetch (ns, fipc_buf, ns->ns_ba, len);
+
+ fipc_packet (fipc_buf, ns->ns_eh);
+ }
+ else /* net_kmsg */
+ {
+#endif /* FIPC */
+ new_kmsg = net_kmsg_get();
+
+ if (new_kmsg == IKM_NULL) {
+ ns->ds_if.if_rcvdrops++;
+ return(0);
+ }
+
+ ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *)(&net_kmsg(new_kmsg)->packet[0]);
+ *ehp = ns->ns_eh;
+
+ nefetch(ns, (char *) (pkt + 1), ns->ns_ba, len);
+
+ pkt->type = ehp->ether_type;
+
+ pkt->length = len + sizeof(struct packet_header);
+ net_packet(ifp, new_kmsg, pkt->length, ethernet_priority(new_kmsg));
+#ifdef FIPC
+ }
+#endif
+
+#else MACH_KERNEL
+/**/
+ epkt = ns->ns_ba + len;
+
+ MGETHDR(m, M_DONTWAIT, MT_DATA);
+ if (m == 0)
+ return (0);
+
+ m->m_pkthdr.rcvif = &ns->ds_if;
+ m->m_pkthdr.len = len;
+ m->m_len = MHLEN;
+
+ top = 0;
+ mp = &top;
+ while (len > 0) {
+ if (top) {
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == 0) {
+ m_freem(top);
+ return (0);
+ }
+ m->m_len = MLEN;
+ }
+ l = min(len, epkt - ns->ns_ba);
+ if (l >= MINCLSIZE) {
+ MCLGET(m, M_DONTWAIT);
+ if (m->m_flags & M_EXT)
+ m->m_len = l = min(len, MCLBYTES);
+ else
+ l = m->m_len;
+ } else {
+ /*
+ * Place initial small packet/header at end of mbuf.
+ */
+ if (l < m->m_len) {
+ if (top == 0 && len + max_linkhdr <= m->m_len)
+ m->m_data += max_linkhdr;
+ m->m_len = l;
+ } else
+ l = m->m_len;
+ }
+ nefetch(ns, mtod(m, caddr_t), ns->ns_ba, l);
+ ns->ns_ba += l;
+ *mp = m;
+ mp = &m->m_next;
+ len -= l;
+ }
+/**/
+ if (top == 0) return 0; /* NEED MODIFY HERE !!! */
+
+ ether_input(&ns->ds_if, &ns->ns_eh, top);
+#endif MACH_KERNEL
+ return 1;
+}
+
+#ifdef MACH_KERNEL
+neopen(dev, flag)
+dev_t dev;
+int flag;
+{
+ register int unit = minor(dev);
+
+ if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE)
+ return (ENXIO);
+
+ ne_softc[unit].ds_if.if_flags |= IFF_UP;
+ neinit(unit);
+ return(0);
+}
+
+#ifdef FIPC
+nefoutput(dev, ior)
+dev_t dev;
+io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE)
+ return (ENXIO);
+
+ return (net_fwrite(&ne_softc[unit].ds_if, nestart, ior));
+}
+#endif
+
+neoutput(dev, ior)
+dev_t dev;
+io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE)
+ return (ENXIO);
+
+ return (net_write(&ne_softc[unit].ds_if, nestart, ior));
+}
+
+nesetinput(dev, receive_port, priority, filter, filter_count)
+dev_t dev;
+mach_port_t receive_port;
+int priority;
+filter_t filter[];
+unsigned int filter_count;
+{
+ register int unit = minor(dev);
+
+ if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE)
+ return (ENXIO);
+
+ return (net_set_filter(&ne_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+
+negetstat(dev, flavor, status, count)
+dev_t dev;
+int flavor;
+dev_status_t status;
+unsigned int *count;
+{
+ register int unit = minor(dev);
+
+ if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE)
+ return (ENXIO);
+
+ return (net_getstat(&ne_softc[unit].ds_if,
+ flavor,
+ status,
+ count));
+}
+
+nesetstat(dev, flavor, status, count)
+dev_t dev;
+int flavor;
+dev_status_t status;
+unsigned int count;
+{
+ register int unit = minor(dev);
+ register ne_softc_t *ns;
+
+ if (!ne_softc[unit].card_present || unit < 0 || unit >= NNE)
+ return (ENXIO);
+
+ ns = &ne_softc[unit];
+
+ switch(flavor) {
+ case NET_STATUS: {
+ register struct net_status *s = (struct net_status *)status;
+ int mode = 0;
+ if (count < NET_STATUS_COUNT)
+ return(D_INVALID_SIZE);
+#define MOD_ENAL 1
+#define MOD_PROM 2
+ if (s->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (s->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ if (ns->mode != mode) {
+ ns->mode = mode;
+ if (ns->ns_flags & DSF_RUNNING) {
+ ns->ns_flags &= ~(DSF_LOCK | DSF_RUNNING);
+ neinit(unit);
+ }
+ }
+ break;
+ }
+ default :
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+#else MACH_KERNEL
+
+/*
+ * Process an ioctl request.
+ */
+neioctl(ifp, cmd, data)
+ register struct ifnet *ifp;
+ int cmd;
+ caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ ne_softc_t *ns = &ne_softc[ifp->if_unit];
+ struct ifreq *ifr = (struct ifreq *)data;
+ int s = splimp(), error = 0;
+
+
+ switch (cmd) {
+
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+
+ switch (ifa->ifa_addr->sa_family) {
+ case AF_INET:
+ neinit(ifp->if_unit); /* before arpwhohas */
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+ case AF_NS:
+ {
+ register struct ns_addr *ina = &(IA_SNS(ifa)->sns_addr);
+
+ if (ns_nullhost(*ina))
+ ina->x_host = *(union ns_host *)(ns->ds_addr);
+ else {
+ /*
+ * The manual says we can't change the address
+ * while the receiver is armed,
+ * so reset everything
+ */
+ ifp->if_flags &= ~IFF_RUNNING;
+ bcopy((caddr_t)ina->x_host.c_host,
+ (caddr_t)ns->ds_addr, sizeof(ns->ds_addr));
+ }
+ neinit(ifp->if_unit); /* does ne_setaddr() */
+ break;
+ }
+ default:
+ neinit(ifp->if_unit);
+ break;
+ }
+ break;
+
+ case SIOCSIFFLAGS:
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ ifp->if_flags & IFF_RUNNING) {
+ ifp->if_flags &= ~IFF_RUNNING;
+ outb(ns->ns_port + ds_cmd, DSCM_STOP|DSCM_NODMA);
+ } else if (ifp->if_flags & IFF_UP &&
+ (ifp->if_flags & IFF_RUNNING) == 0)
+ neinit(ifp->if_unit);
+ break;
+
+#ifdef notdef
+ case SIOCGHWADDR:
+ bcopy((caddr_t)ns->ds_addr, (caddr_t) &ifr->ifr_data,
+ sizeof(ns->ds_addr));
+ break;
+#endif
+
+ default:
+ error = EINVAL;
+ }
+ splx(s);
+ return (error);
+}
+
+/*
+ * Reset of interface.
+ */
+nereset(unit, uban)
+ int unit, uban;
+{
+ if (unit >= NNE)
+ return;
+ printf("ne%d: reset\n", unit);
+ ne_softc[unit].ns_flags &= ~DSF_LOCK;
+ neinit(unit);
+}
+#endif MACH_KERNEL
+#endif
diff --git a/i386/i386at/if_nereg.h b/i386/i386at/if_nereg.h
new file mode 100644
index 00000000..a6b5a82c
--- /dev/null
+++ b/i386/i386at/if_nereg.h
@@ -0,0 +1,66 @@
+/*-
+ * Copyright (c) 1991 The Regents of the University of California.
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ * must display the following acknowledgement:
+ * This product includes software developed by the University of
+ * California, Berkeley and its contributors.
+ * 4. Neither the name of the University nor the names of its contributors
+ * may be used to endorse or promote products derived from this software
+ * without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * @(#)if_nereg.h 7.1 (Berkeley) 5/9/91
+ */
+
+/*
+ * NE1000/2000 Ethernet Card registers
+ */
+
+/* This card uses a DS8390 Ethernet controller in at the beginning of
+ its i/o space */
+
+#define ne_data 0x10 /* Data Transfer port */
+#define ne_reset 0x1F /* Card Reset port */
+
+#define PKTSZ 6 /* Size of transmit buffer */
+
+/* Span of memory on an NE2000 */
+#define TBUF16 0x40 /* Starting location of Transmit Buffer */
+#define RBUFEND16 0x80 /* Ending location of Receive Buffer */
+
+/* Span of memory on an NE1000 */
+#define TBUF8 0x20 /* Starting location of Transmit Buffer */
+#define RBUFEND8 0x40 /* Ending location of Receive Buffer */
+
+#if 0
+#define PKTSZ 3*512 /* Size of transmit buffer */
+
+/* Span of memory on an NE2000 */
+#define TBUF16 (16*1024) /* Starting location of Transmit Buffer */
+#define RBUFEND16 (32*1024) /* Ending location of Receive Buffer */
+
+/* Span of memory on an NE1000 */
+#define TBUF8 (8*1024) /* Starting location of Transmit Buffer */
+#define RBUFEND8 (16*1024) /* Ending location of Receive Buffer */
+#endif
diff --git a/i386/i386at/if_ns8390.c b/i386/i386at/if_ns8390.c
new file mode 100644
index 00000000..15c94400
--- /dev/null
+++ b/i386/i386at/if_ns8390.c
@@ -0,0 +1,2578 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* NOTE:
+ * There are three outstanding bug/features in this implementation.
+ * They may even be hardware misfeatures. The conditions are registered
+ * by counters maintained by the software.
+ * 1: over_write is a condition that means that the board wants to store
+ * packets, but there is no room. So new packets are lost. What seems to
+ * be happening is that we get an over_write condition, but there are no
+ * or just a few packets in the board's ram. Also it seems that we get
+ * several over_writes in a row.
+ * 2: Since there is only one transmit buffer, we need a lock to indicate
+ * whether it is in use. We clear this lock when we get a transmit interrupt.
+ * Sometimes we go to transmit and although there is no transmit in progress,
+ * the lock is set. (In this case, we just ignore the lock.) It would look
+ * like we can miss transmit interrupts?
+ * 3: We tried to clean up the unnecessary switches to bank 0.
+ * Unfortunately, when you do an ifconfig "down", the system tend to lock up
+ * a few seconds later (this was when DSF_RUNNING) was not being set before.
+ * But even with DSF_RUNNING, on an EISA bus machine we ALWAYS lock up after
+ * a few seconds.
+ */
+
+/*
+ * Western Digital 8003E Mach Ethernet driver (for intel 80386)
+ * Copyright (c) 1990 by Open Software Foundation (OSF).
+ */
+
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+<INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define IF_CNTRS MACH
+
+#include <ns8390.h>
+#if NNS8390 > 0
+
+#include <mach_ttd.h>
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#include "vm_param.h"
+#include <i386/ipl.h>
+#include <chips/busses.h>
+#include <i386at/ds8390.h>
+#include <i386at/if_wd8003.h>
+#include <i386at/if_3c503.h>
+
+#if MACH_TTD
+#include <ttd/ttd_stub.h>
+#endif /* MACH_TTD */
+
+
+#define SPLNET spl6
+
+int wd_debug = 0;
+
+int ns8390probe();
+void ns8390attach();
+int ns8390intr();
+int ns8390init();
+int ns8390output();
+int ns8390ioctl();
+int ns8390reset();
+int ns8390rcv();
+int ns8390watch();
+int ns8390get_CURR();
+int ns8390over_write();
+
+struct bus_device *ns8390info[NNS8390]; /* ???? */
+
+static vm_offset_t ns8390_std[NNS8390] = { 0 };
+static struct bus_device *ns8390_info[NNS8390];
+struct bus_driver ns8390driver =
+ {ns8390probe, 0, ns8390attach, 0, ns8390_std, "ns8390", ns8390_info, 0, 0, 0};
+
+int watchdog_id;
+
+char *wd8003_card = "wd";
+char *elii_card = "el";
+/* 2e0, 2a0, 280, 250, 350, 330, 310, 300*/
+int elii_irq[8] = {5, 2, 2, 5, 5, 0x711, 0x711, 5};
+int elii_bnc[8] = {1, 0, 1, 1, 0, 0x711, 0x711, 0};
+/*int elii_bnc[8] = {0, 1, 1, 1, 1, 1, 0, 1}; */
+
+typedef struct {
+#ifdef MACH_KERNEL
+ struct ifnet ds_if; /* generic interface header */
+ u_char ds_addr[6]; /* Ethernet hardware address */
+#else MACH_KERNEL
+ struct arpcom ns8390_ac;
+#define ds_if ns8390_ac.ac_if
+#define ds_addr ns8390_ac.ac_enaddr
+#endif MACH_KERNEL
+ int flags;
+ int timer;
+ int interrupt;
+ char *nic;
+ u_char address[ETHER_ADDR_SIZE];
+ short mode;
+ int tbusy;
+ char *sram; /* beginning of the shared memory RAM buffer */
+ int read_nxtpkt_ptr;/* pointer to next packet available */
+ int pstart; /* page start hold */
+ int pstop; /* page stop hold */
+ int tpsr; /* transmit page start hold */
+ int fifo_depth; /* NIC fifo threshold */
+ char *card;
+ int board_id;
+}
+ns8390_softc_t;
+
+ns8390_softc_t ns8390_softc[NNS8390];
+
+struct ns8390_cntrs {
+u_int ovw,
+ jabber,
+ crc,
+ frame,
+ miss,
+ fifo,
+ rcv;
+u_int xmt,
+ xmti,
+ busy,
+ heart;
+} ns8390_cntrs[NNS8390];
+
+#if MACH_TTD
+boolean_t ttd_poll_loop;
+
+int ns8390poll_receive();
+int ns8390transmit_ttd();
+#endif /* MACH_TTD */
+
+#ifdef IF_CNTRS
+int ns_narp = 1, ns_arp = 0;
+int ns_ein[32], ns_eout[32];
+int ns_lin[128/8], ns_lout[128/8];
+static
+log_2(no)
+unsigned long no;
+{
+ return ({ unsigned long _temp__;
+ asm("bsr %1, %0; jne 0f; xorl %0, %0; 0:" :
+ "=r" (_temp__) : "a" (no));
+ _temp__;});
+}
+#endif IF_CNTRS
+
+/* Interrupts mask bits */
+int imr_hold = DSIM_PRXE|DSIM_PTXE|DSIM_RXEE|DSIM_TXEE|DSIM_OVWE|DSIM_CNTE;
+
+/*
+ * ns8390probe:
+ *
+ * This function "probes" or checks for the wd8003 board on the bus to see
+ * if it is there. As far as I can tell, the best break between this
+ * routine and the attach code is to simply determine whether the board
+ * is configured in properly. Currently my approach to this is to test the
+ * base I/O special offset for the Western Digital unique byte sequence
+ * identifier. If the bytes match we assume board is there.
+ * The config code expects to see a successful return from the probe
+ * routine before attach will be called.
+ *
+ * input : address device is mapped to, and unit # being checked
+ * output : a '1' is returned if the board exists, and a 0 otherwise
+ *
+ */
+
+ns8390probe(port, dev)
+struct bus_device *dev;
+{
+ caddr_t hdwbase = (caddr_t)dev->address;
+ int unit = dev->unit;
+ ns8390_softc_t *sp = &ns8390_softc[unit];
+ int tmp;
+ int vendor_id;
+
+ if ((unit < 0) || (unit > NNS8390)) {
+ printf("ns8390 ethernet unit %d out of range\n", unit);
+ return(0);
+ }
+ if (((u_char) inb(hdwbase+IFWD_LAR_0) == (u_char) WD_NODE_ADDR_0) &&
+ ((u_char) inb(hdwbase+IFWD_LAR_1) == (u_char) WD_NODE_ADDR_1) &&
+ ((u_char) inb(hdwbase+IFWD_LAR_2) == (u_char) WD_NODE_ADDR_2)) {
+ ns8390info[unit] = dev;
+ sp->card = wd8003_card;
+ dev->name = wd8003_card;
+ sp->nic = hdwbase + OFF_8390;
+ /* enable mem access to board */
+ sp->board_id = wd80xxget_board_id(dev);
+
+ *(sp->address) = inb(hdwbase+IFWD_LAR_0);
+ *(sp->address + 1) = inb(hdwbase+IFWD_LAR_1);
+ *(sp->address + 2) = inb(hdwbase+IFWD_LAR_2);
+ *(sp->address + 3) = inb(hdwbase+IFWD_LAR_3);
+ *(sp->address + 4) = inb(hdwbase+IFWD_LAR_4);
+ *(sp->address + 5) = inb(hdwbase+IFWD_LAR_5);
+ return (1);
+ } /* checks the address of the board to verify that it is a WD */
+
+ /* try to avoid any NE2000 pretending to be an el II */
+ if (inb(hdwbase + 0x408) == 0xff)
+ return 0;
+
+ /* check vendor id */
+ tmp = inb(hdwbase + CTLR);
+
+ outb(hdwbase + CTLR, CTLR_RST|CTLR_THIN); /* Reset it... */
+ outb(hdwbase + CTLR, CTLR_THIN);
+ /*
+ * Map the station addr PROM into the lower I/O ports. We now
+ * check for both the old and new 3Com prefix
+ */
+ outb(hdwbase + CTLR, CTLR_STA_ADDR|CTLR_THIN);
+ vendor_id = inb(hdwbase)*0x10000 + inb(hdwbase + 1)*0x100 +
+ inb(hdwbase + 2);
+ /* Restore the register we frobbed. */
+ outb(hdwbase + CTLR, tmp);
+ if ((vendor_id != OLD_3COM_ID) && (vendor_id != NEW_3COM_ID))
+ return 0;
+
+ if ((tmp = inb(hdwbase+BCFR))) {
+ switch(tmp) {
+ case (1<<7): sp->board_id = 7; break; /*irq5 xvcr*/
+#ifdef not_currently_possible
+ case (1<<6): sp->board_id = 6; break;
+ case (1<<5): sp->board_id = 5; break;
+#endif not_currently_possible
+ case (1<<4): sp->board_id = 4; break;
+ case (1<<3): sp->board_id = 3; break;
+ case (1<<2): sp->board_id = 2; break; /*irq2 bnc*/
+ case (1<<1): sp->board_id = 1; break; /*irq2 xvcr*/
+ case (1<<0): sp->board_id = 0; break; /*irq5 bnc*/
+ default: return 0;
+ }
+ switch (inb(hdwbase+PCFR)) {
+ case (1<<7): dev->phys_address = 0xDC000; break;
+ case (1<<6): dev->phys_address = 0xD8000; break;
+#ifdef not_currently_possible
+ case (1<<5): dev->phys_address = 0xCC000; break;
+ case (1<<4): dev->phys_address = 0xC8000; break;
+#endif not_currently_possible
+ default:
+ printf("EtherLink II with NO memory configured\n");
+ return 0;
+ }
+ ns8390info[unit] = dev;
+ dev->sysdep1 = elii_irq[sp->board_id];
+ if (dev->sysdep1 == 2)
+ dev->sysdep1 = 9;
+ sp->card = elii_card;
+ dev->name = elii_card;
+ sp->nic = hdwbase;
+ return 1;
+ }
+
+ return(0);
+}
+
+/*
+ * ns8390attach:
+ *
+ * This function attaches a ns8390 board to the "system". The rest of
+ * runtime structures are initialized here (this routine is called after
+ * a successful probe of the board). Once the ethernet address is read
+ * and stored, the board's ifnet structure is attached and readied.
+ *
+ * input : bus_device structure setup in autoconfig
+ * output : board structs and ifnet is setup
+ *
+ */
+
+void ns8390attach(dev)
+struct bus_device *dev;
+{
+ ns8390_softc_t *sp;
+ struct ifnet *ifp;
+ u_char unit;
+ int temp;
+
+ take_dev_irq(dev);
+ unit = (u_char)dev->unit;
+ sp = &ns8390_softc[unit];
+ printf(", port = %x, spl = %d, pic = %d. ",
+ dev->address, dev->sysdep, dev->sysdep1);
+
+ if (sp->card == elii_card) {
+ if (elii_bnc[sp->board_id])
+ printf("cheapernet ");
+ else
+ printf("ethernet ");
+ } else
+ printf("ethernet ");
+
+ (volatile char *)sp->sram =
+ (volatile char *) phystokv(dev->phys_address);
+ dev->address = (vm_offset_t) phystokv(dev->address);
+ sp->timer = -1;
+ sp->flags = 0;
+ sp->mode = 0;
+
+ if (!ns8390hwrst(unit)) {
+ printf("%s%d: attach(): reset failed.\n",
+ sp->card, unit);
+ return;
+ }
+ /* N.B. sp->address is not determined till
+ * hwrst time. */
+ *(sp->ds_addr) = *(sp->address);
+ *(sp->ds_addr + 1) = *(sp->address + 1);
+ *(sp->ds_addr + 2) = *(sp->address + 2);
+ *(sp->ds_addr + 3) = *(sp->address + 3);
+ *(sp->ds_addr + 4) = *(sp->address + 4);
+ *(sp->ds_addr + 5) = *(sp->address + 5);
+
+ printf("id [%x:%x:%x:%x:%x:%x]",
+ sp->address[0],sp->address[1],sp->address[2],
+ sp->address[3],sp->address[4],sp->address[5]);
+ ifp = &(sp->ds_if);
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+#ifdef MACH_KERNEL
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&sp->address[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = sp->card;
+ ifp->if_init = ns8390init;
+ ifp->if_output = ns8390output;
+ ifp->if_ioctl = ns8390ioctl;
+ ifp->if_reset = ns8390reset;
+ ifp->if_next = NULL;
+ if_attach(ifp);
+#ifdef notdef
+ watchdog_id = timeout(ns8390watch, &(ifp->if_unit), 20*HZ);
+#endif
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+#if MACH_TTD
+ if (!ttd_get_packet) {
+ ttd_device_unit = unit;
+ ttd_get_packet = ns8390poll_receive;
+ ttd_send_packet = ns8390transmit_ttd;
+ ttd_host_ether_id.array[0] = *(sp->address);
+ ttd_host_ether_id.array[1] = *(sp->address + 1);
+ ttd_host_ether_id.array[2] = *(sp->address + 2);
+ ttd_host_ether_id.array[3] = *(sp->address + 3);
+ ttd_host_ether_id.array[4] = *(sp->address + 4);
+ ttd_host_ether_id.array[5] = *(sp->address + 5);
+ }
+#endif /* MACH_TTD */
+#endif /* MACH_KERNEL */
+}
+
+/*
+ * ns8390watch():
+ *
+ */
+
+int
+ns8390watch(b_ptr)
+caddr_t b_ptr;
+{
+ int x,
+ y,
+ opri,
+ unit;
+ int temp_cr;
+ caddr_t nic;
+
+ unit = *b_ptr;
+#ifdef MACH_KERNEL
+ timeout(ns8390watch,b_ptr,20*HZ);
+#else MACH_KERNEL
+ watchdog_id = timeout(ns8390watch,b_ptr,20*HZ);
+#endif MACH_KERNEL
+ nic = ns8390_softc[unit].nic;
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic + ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ printf("<<< ISR=%x CURR=%x rdnxt=%x BNDY=%x>>> ",
+ inb(nic + ds0_isr),
+ ns8390get_CURR(unit), ns8390_softc[unit].read_nxtpkt_ptr,
+ inb(nic+ds0_bndy));
+ outb(nic+ds_cmd,temp_cr);
+}
+
+#ifdef MACH_KERNEL
+int ns8390start(); /* forward */
+
+/*ARGSUSED*/
+wd8003open(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (ns8390_softc[unit].card != wd8003_card)
+ return (ENXIO);
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ ns8390_softc[unit].ds_if.if_flags |= IFF_UP;
+ ns8390init(unit);
+ return(0);
+}
+
+eliiopen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (ns8390_softc[unit].card != elii_card)
+ return (ENXIO);
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ ns8390_softc[unit].ds_if.if_flags |= IFF_UP;
+ ns8390init(unit);
+ return(0);
+}
+
+ns8390output(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+ return (net_write(&ns8390_softc[unit].ds_if, ns8390start, ior));
+}
+
+ns8390setinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t filter[];
+ unsigned int filter_count;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ return (net_set_filter(&ns8390_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+
+#else MACH_KERNEL
+/*
+ * ns8390output:
+ *
+ * This routine is called by the "if" layer to output a packet to
+ * the network. This code resolves the local ethernet address, and
+ * puts it into the mbuf if there is room. If not, then a new mbuf
+ * is allocated with the header information and precedes the data
+ * to be transmitted. The routine ns8390xmt() which actually
+ * transmits the data expects the ethernet header to precede the
+ * data in the mbuf.
+ *
+ * input: ifnet structure pointer, an mbuf with data, and address
+ * to be resolved
+ * output: mbuf is updated to hold enet address, or a new mbuf
+ * with the address is added
+ *
+ */
+
+ns8390output(ifp, m0, dst)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+{
+ register ns8390_softc_t *is = &ns8390_softc[ifp->if_unit];
+ u_char edst[6];
+ struct in_addr idst;
+ register struct mbuf *m = m0;
+ register struct ether_header *eh;
+ register int off;
+ int usetrailers;
+ int type, error;
+ spl_t opri;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("%s%d output(): Turning off board %d\n",
+ is->card, ifp->if_unit);
+ ns8390intoff(ifp->if_unit);
+ error = ENETDOWN;
+ goto bad;
+ }
+ switch (dst->sa_family) {
+#ifdef INET
+ case AF_INET:
+ idst = ((struct sockaddr_in *)dst)->sin_addr;
+ if (!arpresolve(&is->ns8390_ac, m, &idst, edst, &usetrailers)){
+ return (0); /* if not yet resolved */
+ }
+ off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+ if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
+ m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+ type = ETHERTYPE_TRAIL + (off>>9);
+ m->m_off -= 2 * sizeof (u_short);
+ m->m_len += 2 * sizeof (u_short);
+ *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
+ *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
+ goto gottrailertype;
+ }
+ type = ETHERTYPE_IP;
+ off = 0;
+ goto gottype;
+#endif
+#ifdef NS
+ case AF_NS:
+ type = ETHERTYPE_NS;
+ bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
+ (caddr_t)edst,
+ sizeof (edst));
+ off = 0;
+ goto gottype;
+#endif
+ case AF_UNSPEC:
+ eh = (struct ether_header *)dst->sa_data;
+ bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
+ type = eh->ether_type;
+ goto gottype;
+ default:
+ printf("%s%d output(): can't handle af%d\n",
+ is->card, ifp->if_unit,
+ dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+gottrailertype:
+ /*
+ * Packet to be sent as trailer: move first packet
+ * (control information) to end of chain.
+ */
+ while (m->m_next)
+ m = m->m_next;
+ m->m_next = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ m0 = m;
+gottype:
+ /*
+ * Add local net header. If no space in first mbuf,
+ * allocate another.
+ */
+ if (m->m_off > MMAXOFF ||
+ MMINOFF + sizeof (struct ether_header) > m->m_off) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ m->m_next = m0;
+ m->m_off = MMINOFF;
+ m->m_len = sizeof (struct ether_header);
+ } else {
+ m->m_off -= sizeof (struct ether_header);
+ m->m_len += sizeof (struct ether_header);
+ }
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = htons((u_short)type);
+ bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)is->address,
+ (caddr_t)eh->ether_shost,
+ sizeof(edst));
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ opri = SPLNET();
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(opri);
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m);
+ /*
+ * Some action needs to be added here for checking whether the
+ * board is already transmitting. If it is, we don't want to
+ * start it up (ie call ns8390start()). We will attempt to send
+ * packets that are queued up after an interrupt occurs. Some
+ * flag checking action has to happen here and/or in the start
+ * routine. This note is here to remind me that some thought
+ * is needed and there is a potential problem here.
+ *
+ */
+ ns8390start(ifp->if_unit);
+ splx(opri);
+ return (0);
+bad:
+ m_freem(m0);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * ns8390reset:
+ *
+ * This routine is in part an entry point for the "if" code. Since most
+ * of the actual initialization has already (we hope already) been done
+ * by calling ns8390attach().
+ *
+ * input : unit number or board number to reset
+ * output : board is reset
+ *
+ */
+
+int
+ns8390reset(unit)
+int unit;
+{
+
+ ns8390_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ return(ns8390init(unit));
+}
+
+/*
+ * ns8390init:
+ *
+ * Another routine that interfaces the "if" layer to this driver.
+ * Simply resets the structures that are used by "upper layers".
+ * As well as calling ns8390hwrst that does reset the ns8390 board.
+ *
+ * input : board number
+ * output : structures (if structs) and board are reset
+ *
+ */
+
+int
+ns8390init(unit)
+int unit;
+{
+ struct ifnet *ifp;
+ int stat;
+ spl_t oldpri;
+
+ ifp = &(ns8390_softc[unit].ds_if);
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) {
+ return;
+ }
+#endif MACH_KERNEL
+ oldpri = SPLNET();
+ if ((stat = ns8390hwrst(unit)) == TRUE) {
+ ns8390_softc[unit].ds_if.if_flags |= IFF_RUNNING;
+ ns8390_softc[unit].flags |= DSF_RUNNING;
+ ns8390_softc[unit].tbusy = 0;
+ ns8390start(unit);
+ } else
+ printf("%s%d init(): trouble resetting board %d\n",
+ ns8390_softc[unit].card, unit);
+ ns8390_softc[unit].timer = 5;
+ splx(oldpri);
+ return(stat);
+}
+
+/*
+ * ns8390start:
+ *
+ * This is yet another interface routine that simply tries to output a
+ * in an mbuf after a reset.
+ *
+ * input : board number
+ * output : stuff sent to board if any there
+ *
+ */
+
+ns8390start(unit)
+int unit;
+{
+ register ns8390_softc_t *is = &ns8390_softc[unit];
+ struct ifnet *ifp;
+#ifdef MACH_KERNEL
+ io_req_t m;
+#else MACH_KERNEL
+ struct mbuf *m;
+#endif MACH_KERNEL
+
+ if (is->tbusy) {
+ caddr_t nic = ns8390_softc[unit].nic;
+ if (!(inb(nic+ds_cmd) & DSCM_TRANS)) {
+ is->tbusy = 0;
+ ns8390_cntrs[unit].busy++;
+ } else
+ return;
+ }
+
+ ifp = &(ns8390_softc[unit].ds_if);
+
+ IF_DEQUEUE(&ifp->if_snd, m);
+#ifdef MACH_KERNEL
+ if (m != 0)
+#else MACH_KERNEL
+ if (m != (struct mbuf *)0)
+#endif MACH_KERNEL
+ {
+ is->tbusy++;
+ ns8390_cntrs[unit].xmt++;
+ ns8390xmt(unit, m);
+ }
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+ns8390getstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status; /* pointer to OUT array */
+ unsigned int *count; /* out */
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ return (net_getstat(&ns8390_softc[unit].ds_if,
+ flavor,
+ status,
+ count));
+}
+ns8390setstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned int count;
+{
+ register int unit = minor(dev);
+ register ns8390_softc_t *sp;
+
+ if (unit < 0 || unit >= NNS8390 ||
+ ns8390_softc[unit].nic == 0)
+ return (ENXIO);
+
+ sp = &ns8390_softc[unit];
+
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ /*
+ * All we can change are flags, and not many of those.
+ */
+ register struct net_status *ns = (struct net_status *)status;
+ int mode = 0;
+
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ /*
+ * Force a complete reset if the receive mode changes
+ * so that these take effect immediately.
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ ns8390init(unit);
+ }
+ }
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+#else MACH_KERNEL
+/*
+ * ns8390ioctl:
+ *
+ * This routine processes an ioctl request from the "if" layer
+ * above.
+ *
+ * input : pointer the appropriate "if" struct, command, and data
+ * output : based on command appropriate action is taken on the
+ * ns8390 board(s) or related structures
+ * return : error is returned containing exit conditions
+ *
+ */
+
+int
+ns8390ioctl(ifp, cmd, data)
+struct ifnet *ifp;
+int cmd;
+caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ register ns8390_softc_t *is;
+ int error;
+ spl_t opri;
+ short mode = 0;
+
+ is = &ns8390_softc[ifp->if_unit];
+ opri = SPLNET();
+ error = 0;
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ ns8390init(ifp->if_unit);
+ switch (ifa->ifa_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ((struct arpcom *)ifp)->ac_ipaddr =
+ IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina =
+ &(IA_SNS(ifa)->sns_addr);
+ if (ns_nullhost(*ina))
+ ina->x_host =
+ *(union ns_host *)(ds->ds_addr);
+ else
+????
+ ns8390seteh(ina->x_host.c_host,
+ ns8390_softc[ifp->if_unit].base);
+ break;
+ }
+#endif
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ifp->if_flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+ /*
+ * force a complete reset if the receive multicast/
+ * promiscuous mode changes so that these take
+ * effect immediately.
+ *
+ */
+ if (is->mode != mode) {
+ is->mode = mode;
+ if (is->flags & DSF_RUNNING) {
+ is->flags &=
+ ~(DSF_LOCK|DSF_RUNNING);
+ ns8390init(ifp->if_unit);
+ }
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 &&
+ is->flags & DSF_RUNNING) {
+ printf("%s%d ioctl(): turning off board %d\n",
+ is->card, ifp->if_unit);
+ is->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ is->timer = -1;
+ ns8390intoff(ifp->if_unit);
+ ns8390over_write(ifp->if_unit);
+ } else
+ if (ifp->if_flags & IFF_UP &&
+ (is->flags & DSF_RUNNING) == 0)
+ ns8390init(ifp->if_unit);
+ break;
+#ifdef IF_CNTRS
+ case SIOCCIFCNTRS:
+ if (!suser()) {
+ error = EPERM;
+ break;
+ }
+ bzero((caddr_t)ns_ein, sizeof (ns_ein));
+ bzero((caddr_t)ns_eout, sizeof (ns_eout));
+ bzero((caddr_t)ns_lin, sizeof (ns_lin));
+ bzero((caddr_t)ns_lout, sizeof (ns_lout));
+ bzero((caddr_t)&ns_arp, sizeof (int));
+ bzero((caddr_t)&ns8390_cntrs, sizeof (ns8390_cntrs));
+ break;
+#endif IF_CNTRS
+ default:
+ error = EINVAL;
+ }
+ splx(opri);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * ns8390hwrst:
+ *
+ * This routine resets the ns8390 board that corresponds to the
+ * board number passed in.
+ *
+ * input : board number to do a hardware reset
+ * output : board is reset
+ *
+ */
+
+int
+ns8390hwrst(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int count;
+ u_char stat;
+ spl_t spl = SPLNET();
+
+ if (ns8390_softc[unit].card == wd8003_card &&
+ config_wd8003(unit) == FALSE) {
+ printf("%s%d hwrst(): config_wd8003 failed.\n",
+ ns8390_softc[unit].card, unit);
+ splx(spl);
+ return(FALSE);
+ }
+ if (ns8390_softc[unit].card == elii_card &&
+ config_3c503(unit) == FALSE) {
+ printf("%s%d hwrst(): config_3c503 failed.\n",
+ ns8390_softc[unit].card, unit);
+ splx(spl);
+ return(FALSE);
+ }
+ if (config_nic(unit) == FALSE) {
+ printf("%s%d hwrst(): config_nic failed.\n",
+ ns8390_softc[unit].card, unit);
+ splx(spl);
+ return(FALSE);
+ }
+ splx(spl);
+ return(TRUE);
+}
+
+/*
+ * ns8390intr:
+ *
+ * This function is the interrupt handler for the ns8390 ethernet
+ * board. This routine will be called whenever either a packet
+ * is received, or a packet has successfully been transfered and
+ * the unit is ready to transmit another packet.
+ *
+ * input : board number that interrupted
+ * output : either a packet is received, or a packet is transfered
+ *
+ */
+int
+ns8390intr(unit)
+{
+ int opri, i;
+ int isr_status;
+ int temp_cr;
+ caddr_t nic = ns8390_softc[unit].nic;
+
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ outb(nic+ds0_imr, 0); /* stop board interrupts */
+ outb(nic+ds_cmd, temp_cr);
+ while (isr_status = inb(nic+ds0_isr)) {
+ outb(nic+ds0_isr, isr_status); /* clear interrupt status */
+
+ if ((isr_status & (DSIS_ROVRN|DSIS_RXE)) == DSIS_RXE) {
+ int rsr = inb(nic+ds0_rsr);
+ if (rsr & DSRS_DFR) ns8390_cntrs[unit].jabber++;
+ if (rsr & ~(DSRS_DFR|DSRS_PHY|DSRS_FAE|DSRS_CRC|DSIS_RX))
+ printf("%s%d intr(): isr = %x, RSR = %x\n",
+ ns8390_softc[unit].card, unit,
+ isr_status, rsr);
+ } else if (isr_status & DSIS_ROVRN) {
+ ns8390_cntrs[unit].ovw++;
+ ns8390over_write(unit);
+ }
+ if (isr_status & DSIS_RX) { /* DFR & PRX is possible */
+ ns8390rcv(unit);
+
+#if MACH_TTD
+ if (kttd_active)
+ ttd_poll_loop = FALSE;
+#endif /* MACH_TTD */
+ }
+
+ if (isr_status & DSIS_TXE) {
+ int tsr = inb(nic+ds0_tsr);
+ tsr &= ~0x2; /* unadvertised special */
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ if (tsr == (DSTS_CDH|DSTS_ABT))
+ ns8390_cntrs[unit].heart++;
+ else
+ printf("%s%d intr(): isr = %x, TSR = %x\n",
+ ns8390_softc[unit].card, unit,
+ isr_status, tsr);
+ ns8390_softc[unit].tbusy = 0;
+ ns8390start(unit);
+ }
+ } else if (isr_status & DSIS_TX) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ ns8390_cntrs[unit].xmti++;
+ ns8390_softc[unit].tbusy = 0;
+ ns8390start(unit);
+ }
+ }
+
+ if (isr_status & DSIS_CTRS) {
+ int c0 = inb(nic+ds0_cntr0);
+ int c1 = inb(nic+ds0_cntr1);
+ int c2 = inb(nic+ds0_cntr2);
+ ns8390_cntrs[unit].frame += c0;
+ ns8390_cntrs[unit].crc += c1;
+ ns8390_cntrs[unit].miss += c2;
+#ifdef COUNTERS
+ printf("%s%d intr(): isr = %x, FRAME %x, CRC %x, MISS %x\n",
+ ns8390_softc[unit].card, unit,
+ isr_status, c0, c1, c2);
+ printf("%s%d intr(): TOTAL , FRAME %x, CRC %x, MISS %x\n",
+ ns8390_softc[unit].card, unit,
+ ns8390_cntrs[unit].frame,
+ ns8390_cntrs[unit].crc,
+ ns8390_cntrs[unit].miss);
+#endif COUNTERS
+ outb(nic+ds0_isr, isr_status); /* clear interrupt status again */
+ }
+ }
+ temp_cr=inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ outb(nic+ds0_imr, imr_hold);
+ outb(nic+ds_cmd, temp_cr);
+ return(0);
+}
+
+/*
+ * Called if on board buffer has been completely filled by ns8390intr. It stops
+ * the board, reads in all the buffers that are currently in the buffer, and
+ * then restart board.
+ */
+ns8390over_write(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int no;
+ int count = 0;
+
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_STOP|DSCM_PG0); /* clear the receive buffer */
+ outb(nic+ds0_rbcr0, 0);
+ outb(nic+ds0_rbcr1, 0);
+ while ((!(inb (nic + ds0_isr) & DSIS_RESET)) && (count < 10000))
+ count++;
+ if (count == 10000) {
+ printf("%s%d: over_write(): would not reset.\n",
+ ns8390_softc[unit].card, unit);
+ }
+ no = ns8390rcv(unit);
+#ifdef OVWBUG
+ printf("%s%d over_write(): ns8390 OVW ... %d.\n",
+ ns8390_softc[unit].card, unit, no);
+#endif OVWBUG
+ outb(nic+ds0_tcr, DSTC_LB0); /* External loopback mode */
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_START|DSCM_PG0);
+ outb(nic+ds0_tcr, 0);
+ return;
+}
+
+/*
+ * ns8390rcv:
+ *
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the board to the "if" layer above this
+ * driver. This routine checks if a buffer has been successfully
+ * received by the ns8390. If so, it does the actual transfer of the
+ * board data (including the ethernet header) into a packet (consisting
+ * of an mbuf chain) and enqueues it to a higher level.
+ * Then check again whether there are any packets in the receive ring,
+ * if so, read the next packet, until there are no more.
+ *
+ * input : number of the board to check
+ * output : if a packet is available, it is "sent up"
+ */
+ns8390rcv(unit)
+int unit;
+{
+ register ns8390_softc_t *is = &ns8390_softc[unit];
+ register struct ifnet *ifp = &is->ds_if;
+ caddr_t nic = is->nic;
+ int packets = 0;
+ struct ether_header eh;
+ u_short mlen, len, bytes_in_mbuf, bytes;
+ u_short remaining;
+ int temp_cr;
+ u_char *mb_p;
+ int board_id = is->board_id;
+ vm_offset_t hdwbase = ns8390info[unit]->address;
+ spl_t s;
+
+ /* calculation of pkt size */
+ int nic_overcount; /* NIC says 1 or 2 more than we need */
+ int pkt_size; /* calculated size of received data */
+ int wrap_size; /* size of data before wrapping it */
+ int header_nxtpkt_ptr; /* NIC's next pkt ptr in rcv header */
+ int low_byte_count; /* low byte count of read from rcv header */
+ int high_byte_count; /* calculated high byte count */
+
+
+ volatile char *sram_nxtpkt_ptr; /* mem location of next packet */
+ volatile char *sram_getdata_ptr; /* next location to be read */
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+#else MACH_KERNEL
+ struct mbuf *m, *tm; /* initial allocation of mem; temp */
+#endif MACH_KERNEL
+
+
+#if MACH_TTD
+ if (((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) &&
+ !kttd_active) {
+#else
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+#endif /* MACH_TTD */
+ temp_cr = inb(nic+ds_cmd); /* get current CR value */
+ outb(nic+ds_cmd,((temp_cr & 0x3F)|DSCM_PG0|DSCM_STOP));
+ outb(nic+ds0_imr, 0); /* Interrupt Mask Register */
+ outb(nic+ds_cmd, temp_cr);
+ return -1;
+ }
+
+ while(is->read_nxtpkt_ptr != ns8390get_CURR(unit)) {
+
+ /* while there is a packet to read from the buffer */
+
+ if ((is->read_nxtpkt_ptr < is->pstart) ||
+ (is->read_nxtpkt_ptr >= is->pstop)) {
+ ns8390hwrst(unit);
+ return -1;
+ } /* if next packet pointer is out of receive ring bounds */
+
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ packets++;
+ ns8390_cntrs[unit].rcv++;
+ }
+
+ sram_nxtpkt_ptr = (char *) (is->sram + (is->read_nxtpkt_ptr << 8));
+
+ /* get packet size and location of next packet */
+ header_nxtpkt_ptr = *(sram_nxtpkt_ptr + 1);
+ header_nxtpkt_ptr &= 0xFF;
+ low_byte_count = *(sram_nxtpkt_ptr + 2);
+ low_byte_count &= 0xFF;
+
+ if ((low_byte_count + NIC_HEADER_SIZE) > NIC_PAGE_SIZE)
+ nic_overcount = 2;
+ else
+ nic_overcount = 1;
+ if (header_nxtpkt_ptr > is->read_nxtpkt_ptr) {
+ wrap_size = 0;
+ high_byte_count = header_nxtpkt_ptr - is->read_nxtpkt_ptr -
+ nic_overcount;
+ } else {
+ wrap_size = (int) (is->pstop - is->read_nxtpkt_ptr - nic_overcount);
+ high_byte_count = is->pstop - is->read_nxtpkt_ptr +
+ header_nxtpkt_ptr - is->pstart - nic_overcount;
+ }
+ pkt_size = (high_byte_count << 8) | (low_byte_count & 0xFF);
+ /* does not seem to include NIC_HEADER_SIZE */
+ if (!pkt_size) {
+ printf("%s%d rcv(): zero length.\n",
+ ns8390_softc[unit].card, unit);
+ goto next_pkt;
+ }
+ len = pkt_size;
+
+ sram_getdata_ptr = sram_nxtpkt_ptr + NIC_HEADER_SIZE;
+ if (board_id & IFWD_SLOT_16BIT) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { s = splhi(); }
+
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ &eh,
+ sizeof(struct ether_header));
+ dis_16bit_access (hdwbase, board_id);
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { splx(s); }
+
+ } else {
+ bcopy16 (sram_getdata_ptr,
+ &eh,
+ sizeof(struct ether_header));
+ }
+ sram_getdata_ptr += sizeof(struct ether_header);
+ len -= (sizeof(struct ether_header) + 4); /* crc size */
+#ifdef MACH_KERNEL
+#if MACH_TTD
+ if (kttd_active) {
+ new_kmsg = (ipc_kmsg_t)ttd_request_msg;
+ }else
+#endif /* MACH_TTD */
+ {
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the packet.
+ */
+ is->ds_if.if_rcvdrops++;
+ /*
+ * not only do we want to return, we need to drop
+ * the packet on the floor to clear the interrupt.
+ */
+ ns8390lost_frame(unit);
+ return;/* packets;*/
+ }
+ }
+
+#if DEBUG_TTD
+ dump_ether_header("ns8390wire",&eh);
+#endif /* DEBUG_TTD */
+
+ ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *) (&net_kmsg(new_kmsg)->packet[0]);
+
+#if DEBUG_TTD
+ printf("!ehp = 0x%x, pkt = 0x%x!",ehp, pkt);
+#endif /* DEBUG_TTD */
+
+ *ehp = eh;
+ if (len >
+ (wrap_size = (is->sram + (is->pstop << 8) - sram_getdata_ptr))) {
+ /* if needs to wrap */
+ if (board_id & IFWD_SLOT_16BIT) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { s = splhi(); }
+
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr, (char *) (pkt + 1),
+ wrap_size);
+ dis_16bit_access (hdwbase, board_id);
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { splx(s); }
+ } else {
+ bcopy (sram_getdata_ptr, (char *) (pkt + 1),
+ wrap_size);
+ }
+ sram_getdata_ptr = (volatile char *)
+ (is->sram + (is->pstart << 8));
+ } else { /* normal getting data from buffer */
+ wrap_size = 0;
+ }
+ if (board_id & IFWD_SLOT_16BIT) {
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { s = splhi(); }
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ (char *) (pkt + 1) + wrap_size,
+ len - wrap_size);
+ dis_16bit_access (hdwbase, board_id);
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { splx(s); }
+ } else {
+ bcopy (sram_getdata_ptr,
+ (char *) (pkt + 1) + wrap_size,
+ len - wrap_size);
+ }
+
+ pkt->type = ehp->ether_type;
+ pkt->length = len + sizeof(struct packet_header);
+
+#if MACH_TTD
+ /*
+ * Don't want to call net_packet if we are polling
+ * for a packet.
+ */
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ /*
+ * Hand the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length,
+ ethernet_priority(new_kmsg));
+ }
+
+#else MACH_KERNEL
+#define NEW
+#ifdef NEW
+ m = (struct mbuf *) 0;
+ eh.ether_type = ntohs(eh.ether_type);
+ MGET(m, M_DONTWAIT, MT_DATA);
+ if (m == (struct mbuf *) 0) {
+ printf("%s%d rcv(): Lost frame\n",
+ ns8390_softc[unit].card, unit);
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return packets;
+ }
+ m->m_next = (struct mbuf *) 0;
+ tm = m;
+ m->m_len = MLEN;
+ if (len > 2 * MLEN - sizeof (struct ifnet **)) {
+ MCLGET(m);
+ }
+ *(mtod(tm, struct ifnet **)) = ifp;
+ mlen = sizeof (struct ifnet **);
+ bytes_in_mbuf = m->m_len - sizeof(struct ifnet **);
+ mb_p = mtod(tm, u_char *) + sizeof (struct ifnet **);
+ bytes = min(bytes_in_mbuf, len);
+ remaining = (int) (is->sram + (is->pstop << 8) -
+ sram_getdata_ptr);
+ bytes = min(bytes, remaining);
+ do {
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr, mb_p, bytes);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (sram_getdata_ptr, mb_p, bytes);
+ }
+
+ mlen += bytes;
+
+ if (!(bytes_in_mbuf -= bytes)) {
+ MGET(tm->m_next, M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ if (tm == (struct mbuf *)0) {
+ printf("%s%d rcv(): No mbufs, lost frame\n",
+ ns8390_softc[unit].card, unit);
+ m_freem(m); /* free the mbuf chain */
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return;
+ }
+ mlen = 0;
+ tm->m_len = MLEN;
+ bytes_in_mbuf = MLEN;
+ mb_p = mtod(tm, u_char *);
+ } else
+ mb_p += bytes;
+
+ if (!(len -= bytes)) {
+ tm->m_len = mlen;
+ break;
+ } else if (bytes == remaining) {
+ sram_getdata_ptr = (volatile char *) (is->sram +
+ (is->pstart << 8));
+ bytes = len;
+ remaining = ETHERMTU;
+ } else {
+ sram_getdata_ptr += bytes;
+ remaining -= bytes;
+ }
+
+ bytes = min(bytes_in_mbuf, len);
+ bytes = min(bytes, remaining);
+ } while(1);
+#else NEW
+ m = (struct mbuf *) 0;
+ eh.ether_type = ntohs(eh.ether_type);
+
+ while ( len ) {
+ if (m == (struct mbuf *) 0) {
+ m = m_get(M_DONTWAIT, MT_DATA);
+ if (m == (struct mbuf *) 0) {
+ printf("%s%d rcv(): Lost frame\n",
+ ns8390_softc[unit].card, unit);
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return packets;
+ }
+ tm = m;
+ tm->m_off = MMINOFF;
+
+
+ /*
+ * first mbuf in the packet must contain a pointer to the
+ * ifnet structure. other mbufs that follow and make up
+ * the packet do not need this pointer in the mbuf.
+ *
+ */
+
+ *(mtod(tm, struct ifnet **)) = ifp;
+ tm->m_len = sizeof(struct ifnet **);
+
+ /* end of first buffer of packet */
+ } else {
+ tm->m_next = m_get(M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ if (tm == (struct mbuf *) 0) {
+ printf("%s%d rcv(): No mbufs, lost frame\n",
+ ns8390_softc[unit].card, unit);
+ m_freem(m); /* free the mbuf chain */
+ ns8390lost_frame(unit); /* update NIC pointers and registers */
+ return packets;
+ }
+ tm->m_off = MMINOFF;
+ tm->m_len = 0;
+ }
+
+ tlen = MIN( MLEN - tm->m_len, len);
+ /* size of mbuf so you know how much you can copy from board */
+ tm->m_next = (struct mbuf *) 0;
+ if (sram_getdata_ptr + tlen >=
+ (volatile char *) (is->sram + (is->pstop << 8))) {
+ /* if needs to wrap */
+ wrap_size = (int) (is->sram + (is->pstop << 8) -
+ sram_getdata_ptr);
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ wrap_size);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ wrap_size);
+ }
+ tm->m_len += wrap_size;
+ len -= wrap_size;
+
+ sram_getdata_ptr = (volatile char *) (is->sram +
+ (is->pstart << 8));
+ } else { /* normal getting data from buffer */
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ tlen);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (sram_getdata_ptr,
+ mtod(tm, char*) + tm->m_len,
+ tlen);
+ }
+ sram_getdata_ptr += tlen;
+ tm->m_len += tlen;
+ len -= tlen;
+
+ }
+ }
+
+#endif NEW
+ if (!ns8390send_packet_up(m, &eh, is))
+ m_freem(m);
+#ifdef IF_CNTRS
+ ns_ein[log_2(pkt_size)]++;
+ if (pkt_size < 128) ns_lin[(pkt_size)>>3]++;
+
+ if (eh.ether_type == ETHERTYPE_ARP) {
+ ns_arp++;
+ if (ns_narp) {
+ ns_ein[log_2(pkt_size)]--;
+ if (pkt_size < 128) ns_lin[(pkt_size)>>3]--;
+ }
+ }
+#endif IF_CNTRS
+#endif MACH_KERNEL
+
+next_pkt:
+ is->read_nxtpkt_ptr = *(sram_nxtpkt_ptr + 1);
+ is->read_nxtpkt_ptr &= 0xFF;
+
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ {
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+ }
+
+ if (is->read_nxtpkt_ptr == ns8390get_CURR(unit))
+ if (is->read_nxtpkt_ptr == is->pstart)
+ outb(nic+ds0_bndy, is->pstop - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr);
+
+#if MACH_TTD
+ if (!kttd_active)
+#endif /* MACH_TTD */
+ { outb(nic+ds_cmd, temp_cr); }
+
+#if MACH_TTD
+ /*
+ * Hand the packet back to the TTD server, if active.
+ */
+ if (kttd_active && pkt_size)
+ return 1;
+#endif /* MACH_TTD */
+
+
+ }
+ return packets;
+
+}
+
+#ifdef MACH_KERNEL
+#if MACH_TTD
+/*
+ * Polling routines for the TTD debugger.
+ */
+int ns8390poll_receive(unit)
+ int unit;
+{
+ int s;
+ int orig_cr;
+ int orig_imr;
+ int isr_status;
+ int pkts;
+
+ ttd_poll_loop = TRUE;
+
+
+ /*
+ * Should already in at splhigh. Is this necessary? XXX
+ */
+ s = splhigh();
+
+#if 0
+ if (kttd_debug)
+ printf("ns8390poll_receive: beginning polling loop\n");
+#endif /* DEBUG_TTD */
+
+ /*
+ * Loop until packet arrives.
+ */
+ while(ttd_poll_loop) {
+
+ /*
+ * Call intr routine
+ */
+
+ ns8390intr(unit);
+ }
+
+#if 0
+ if (kttd_debug)
+ printf("ns8390poll_receive: got packet exiting loop\n");
+#endif /* DEBUG_TTD */
+
+ splx(s);
+}
+
+int ns8390transmit_ttd(unit, packet, len)
+ int unit;
+ char * packet;
+ int len;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ u_short count = 0; /* amount of data already copied */
+ volatile char *sram_write_pkt;
+ int board_id = is->board_id;
+ caddr_t hdwbase = ns8390info[unit]->address;
+ int s;
+ int orig_cr;
+ int orig_imr;
+ int isr_status;
+ boolean_t loop = TRUE;
+
+#if 0
+ dump_ipudpbootp("Beg of xmit",packet);
+#endif
+
+ s = splhigh();
+
+ /* begining of physical address of transmition buffer */
+
+ sram_write_pkt = is->sram + is->tpsr * 0x100;
+
+ count = len;
+ if (board_id & IFWD_SLOT_16BIT) {
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (packet, sram_write_pkt, count);
+ dis_16bit_access (hdwbase, board_id);
+ } else {
+ bcopy (packet, sram_write_pkt, count);
+ }
+
+ while (count < ETHERMIN+sizeof(struct ether_header)) {
+ *(sram_write_pkt + count) = 0;
+ count++;
+ }
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_START|DSCM_PG0); /* select page 0 */
+ outb(nic+ds0_tpsr, is->tpsr); /* xmt page start at 0 of RAM */
+ outb(nic+ds0_tbcr1, count >> 8); /* upper byte of count */
+ outb(nic+ds0_tbcr0, count & 0xFF); /* lower byte of count */
+ outb(nic+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START); /* start transmission */
+
+ ns8390intr(unit);
+
+ splx(s);
+}
+#endif /* MACH_TTD */
+#endif /* MACH_KERNEL */
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+/*
+ * Send a packet composed of an mbuf chain to the higher levels
+ *
+ */
+ns8390send_packet_up(m, eh, is)
+struct mbuf *m;
+struct ether_header *eh;
+ns8390_softc_t *is;
+{
+ register struct ifqueue *inq;
+ spl_t opri;
+
+ switch (eh->ether_type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+ case ETHERTYPE_ARP:
+ arpinput(&is->ns8390_ac, m);
+ return(TRUE);
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+#endif
+ default:
+ return(FALSE);
+ }
+ opri = SPLNET();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(opri);
+ return(FALSE);
+ }
+ IF_ENQUEUE(inq, m);
+ splx(opri);
+ return(TRUE);
+}
+#endif MACH_KERNEL
+
+/*
+ * ns8390lost_frame:
+ * this routine called by ns8390read after memory for mbufs could not be
+ * allocated. It sets the boundary pointers and registers to the next
+ * packet location.
+ */
+
+ns8390lost_frame(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ volatile char *sram_nxtpkt_ptr;
+ int temp_cr;
+
+
+
+ sram_nxtpkt_ptr = (volatile char *) (is->sram +
+ (is->read_nxtpkt_ptr << 8));
+
+ is->read_nxtpkt_ptr = *(sram_nxtpkt_ptr + 1);
+ is->read_nxtpkt_ptr &= 0xFF;
+
+ temp_cr = inb(nic+ds_cmd);
+ outb(nic+ds_cmd, (temp_cr & 0x3f) | DSCM_PG0);
+
+ /* update boundary register */
+ if (is->read_nxtpkt_ptr == ns8390get_CURR(unit))
+ if (is->read_nxtpkt_ptr == is->pstart)
+ outb(nic+ds0_bndy, is->pstop - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr - 1);
+ else
+ outb(nic+ds0_bndy, is->read_nxtpkt_ptr);
+
+ outb(nic+ds_cmd, temp_cr);
+
+ return;
+}
+
+/*
+ * ns8390get_CURR():
+ *
+ * Returns the value of the register CURR, which points to the next
+ * available space for NIC to receive from network unto receive ring.
+ *
+ */
+
+int
+ns8390get_CURR(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int temp_cr;
+ int ret_val;
+ spl_t s;
+
+ s = SPLNET();
+
+ temp_cr = inb(nic+ds_cmd); /* get current CR value */
+ outb(nic+ds_cmd, ((temp_cr & 0x3F) | DSCM_PG1)); /* select page 1 registers */
+ ret_val = inb(nic+ds1_curr); /* read CURR value */
+ outb(nic+ds_cmd, temp_cr);
+ splx(s);
+ return (ret_val & 0xFF);
+}
+
+/*
+ * ns8390xmt:
+ *
+ * This routine fills in the appropriate registers and memory
+ * locations on the ns8390 board and starts the board off on
+ * the transmit.
+ *
+ * input : board number of interest, and a pointer to the mbuf
+ * output : board memory and registers are set for xfer and attention
+ *
+ */
+
+ns8390xmt(unit, m)
+int unit;
+#ifdef MACH_KERNEL
+io_req_t m;
+#else MACH_KERNEL
+struct mbuf *m;
+#endif MACH_KERNEL
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ struct ether_header *eh;
+ int i;
+ int opri;
+ u_short count = 0; /* amount of data already copied */
+ volatile char *sram_write_pkt;
+ int board_id = is->board_id;
+ vm_offset_t hdwbase = ns8390info[unit]->address;
+ spl_t s;
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ register struct mbuf *tm_p;
+#endif MACH_KERNEL
+ /* begining of physical address of transmition buffer */
+
+ sram_write_pkt = is->sram + is->tpsr * 0x100;
+
+#ifdef MACH_KERNEL
+ count = m->io_count;
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (m->io_data, sram_write_pkt, count);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy (m->io_data, sram_write_pkt, count);
+ }
+#else MACH_KERNEL
+ for(tm_p = m; tm_p != (struct mbuf *)0; tm_p = tm_p->m_next) {
+ if (count + tm_p->m_len > ETHERMTU + sizeof (struct ether_header))
+ break;
+ if (tm_p->m_len == 0)
+ continue;
+ if (board_id & IFWD_SLOT_16BIT) {
+ s = splhi();
+ en_16bit_access(hdwbase, board_id);
+ bcopy16 (mtod(tm_p, caddr_t),
+ sram_write_pkt + count,
+ tm_p->m_len);
+ dis_16bit_access (hdwbase, board_id);
+ splx(s);
+ } else {
+ bcopy16 (mtod(tm_p, caddr_t),
+ sram_write_pkt + count,
+ tm_p->m_len);
+ }
+ count += tm_p->m_len;
+ }
+#ifdef IF_CNTRS
+ ns_eout[log_2(count+4/*crc*/)]++;
+ if (count < 128) ns_lout[(count+4/*crc*/)>>3]++;
+#endif IF_CNTRS
+#endif MACH_KERNEL
+ while (count < ETHERMIN+sizeof(struct ether_header)) {
+ *(sram_write_pkt + count) = 0;
+ count++;
+ }
+
+ /* select page 0 */
+ outb(nic+ds_cmd, DSCM_NODMA|DSCM_START|DSCM_PG0);
+ outb(nic+ds0_tpsr, is->tpsr); /* xmt page start at 0 of RAM */
+ outb(nic+ds0_tbcr1, count >> 8); /* upper byte of count */
+ outb(nic+ds0_tbcr0, count & 0xFF); /* lower byte of count */
+ /* start transmission */
+ outb(nic+ds_cmd, DSCM_TRANS|DSCM_NODMA|DSCM_START);
+
+#ifdef MACH_KERNEL
+ iodone(m);
+ m=0;
+#else MACH_KERNEL
+ /* If this is a broadcast packet, loop it back to rcv. */
+ eh = mtod( m, struct ether_header *);
+ for (i=0; ((i < 6) && (eh->ether_dhost[i] == 0xff)); i++) ;
+ if (i == 6) {
+ if (!ns8390send_packet_up(m, eh, is))
+ m_freem(m);
+ } else
+ m_freem(m);
+#endif MACH_KERNEL
+ return;
+}
+
+config_nic(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ caddr_t nic = is->nic;
+ int i;
+ int temp;
+ int count = 0;
+ spl_t s;
+
+ /* soft reset and page 0 */
+ outb (nic+ds_cmd, DSCM_PG0|DSCM_NODMA|DSCM_STOP);
+
+ while ((!(inb (nic + ds0_isr) & DSIS_RESET)) && (count < 10000))
+ count++;
+ if (count == 10000) {
+ printf("%s%d: config_nic(): would not reset.\n",
+ ns8390_softc[unit].card, unit);
+ }
+
+ /* fifo depth | not loopback */
+ temp = ((is->fifo_depth & 0x0c) << 3) | DSDC_BMS;
+
+ /* word xfer select (16 bit cards ) */
+ if (is->board_id & IFWD_SLOT_16BIT)
+ temp |= DSDC_WTS;
+
+ outb (nic+ds0_dcr, temp);
+ outb (nic+ds0_tcr, 0);
+ outb (nic+ds0_rcr, DSRC_MON); /* receive configuration register */
+ /* recieve ring starts 2k into RAM */
+ outb (nic+ds0_pstart, is->pstart);
+ /* stop at last RAM buffer rcv location */
+ outb (nic+ds0_pstop, is->pstop);
+
+ /* boundary pointer for page 0 */
+ outb (nic+ds0_bndy, is->pstart);
+ s = SPLNET();
+
+ /* maintain rst | sel page 1 */
+ outb (nic+ds_cmd, DSCM_PG1|DSCM_NODMA|DSCM_STOP);
+
+ /* internal next packet pointer */
+ is->read_nxtpkt_ptr = is->pstart + 1;
+
+ outb (nic+ds1_curr, is->read_nxtpkt_ptr); /* Current page register */
+ for(i=0; i<ETHER_ADDR_SIZE; i++)
+ outb (nic+ds1_par0+i, is->address[i]);
+ for(i=0; i<8; i++)
+ outb (nic+ds1_mar0+i, 0);
+
+ outb (nic+ds_cmd, DSCM_PG0|DSCM_STOP|DSCM_NODMA);
+ splx(s);
+ outb (nic+ds0_isr, 0xff); /* clear all interrupt status bits */
+ outb (nic+ds0_imr, imr_hold); /* Enable interrupts */
+ outb (nic+ds0_rbcr0, 0); /* clear remote byte count */
+ outb (nic+ds0_rbcr1, 0);
+
+ /* start NIC | select page 0 */
+ outb (nic+ds_cmd, DSCM_PG0|DSCM_START|DSCM_NODMA);
+
+ outb (nic+ds0_rcr, DSRC_AB); /* receive configuration register */
+
+ return TRUE;
+}
+
+/*
+ * config_ns8390:
+ *
+ * This routine does a standard config of a wd8003 family board, with
+ * the proper modifications to different boards within this family.
+ *
+ */
+config_wd8003(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ vm_offset_t hdwbase = ns8390info[unit]->address;
+ int i;
+ int RAMsize;
+ volatile char *RAMbase;
+ int addr_temp;
+
+ is->tpsr = 0; /* transmit page start hold */
+ is->pstart = 0x06; /* receive page start hold */
+ is->read_nxtpkt_ptr = is->pstart + 1; /* internal next packet pointer */
+ is->fifo_depth = 0x08; /* NIC fifo threshold */
+ switch (is->board_id & IFWD_RAM_SIZE_MASK) {
+ case IFWD_RAM_SIZE_8K:
+ RAMsize = 0x2000; break;
+ case IFWD_RAM_SIZE_16K:
+ RAMsize = 0x4000; break;
+ case IFWD_RAM_SIZE_32K:
+ RAMsize = 0x8000; break;
+ case IFWD_RAM_SIZE_64K:
+ RAMsize = 0x10000; break;
+ default:
+ RAMsize = 0x2000; break;
+ }
+ is->pstop = (((int)RAMsize >> 8) & 0x0ff); /* rcv page stop hold */
+ RAMbase = (volatile char *)ns8390info[unit]->phys_address;
+ addr_temp = ((int)(RAMbase) >> 13) & 0x3f; /* convert to be written to MSR */
+ outb(hdwbase+IFWD_MSR, addr_temp | IFWD_MENB); /* initialize MSR */
+ /* enable 16 bit access from lan controller */
+ if (is->board_id & IFWD_SLOT_16BIT) {
+ if (is->board_id & IFWD_INTERFACE_CHIP) {
+ outb(hdwbase+IFWD_REG_5,
+ (inb(hdwbase + IFWD_REG_5) & IFWD_REG5_MEM_MASK) |
+ IFWD_LAN16ENB);
+ } else {
+ outb(hdwbase+IFWD_REG_5, (IFWD_LAN16ENB | IFWD_LA19));
+ }
+ }
+ /*
+ outb(hdwbase+LAAR, LAN16ENB | LA19| MEM16ENB | SOFTINT);
+ */
+
+ return TRUE;
+}
+
+/*
+ * config_ns8390:
+ *
+ * This routine does a standard config of a 3 com etherlink II board.
+ *
+ */
+int
+config_3c503(unit)
+int unit;
+{
+ ns8390_softc_t *is = &ns8390_softc[unit];
+ struct bus_device *dev = ns8390info[unit];
+ vm_offset_t hdwbase = dev->address;
+ int RAMsize = dev->am;
+ int i;
+
+ is->tpsr = 0x20; /* transmit page start hold */
+ is->sram = (char *)phystokv(dev->phys_address) - is->tpsr * 0x100;
+ /* When NIC says page 20, this means go to
+ the beginning of the sram range */
+ is->pstart = 0x26; /* receive page start hold */
+ is->read_nxtpkt_ptr = is->pstart + 1; /* internal next packet pointer */
+ is->fifo_depth = 0x08; /* NIC fifo threshold */
+ is->pstop = is->tpsr + ((RAMsize >> 8) & 0x0ff); /* rcv page stop hold */
+
+ outb(hdwbase+CTLR, CTLR_RST|CTLR_THIN);
+ outb(hdwbase+CTLR, CTLR_THIN);
+ outb(hdwbase+CTLR, CTLR_STA_ADDR|CTLR_THIN);
+ for (i = 0; i < 6; i++)
+ is->address[i] = inb(hdwbase+i);
+ outb(hdwbase+CTLR, elii_bnc[is->board_id]?CTLR_THIN:CTLR_THICK);
+ outb(hdwbase+PSTR, is->pstart);
+ outb(hdwbase+PSPR, is->pstop);
+ outb(hdwbase+IDCFR, IDCFR_IRQ2 << (elii_irq[is->board_id] - 2));
+ outb(hdwbase+GACFR, GACFR_TCM|GACFR_8K);
+ /* BCFR & PCRFR ro */
+ /* STREG ro & dma */
+ outb(hdwbase+DQTR, 0);
+ outb(hdwbase+DAMSB, 0);
+ outb(hdwbase+DALSB, 0);
+ outb(hdwbase+VPTR2, 0);
+ outb(hdwbase+VPTR1, 0);
+ outb(hdwbase+VPTR0, 0);
+ outb(hdwbase+RFMSB, 0);
+ outb(hdwbase+RFLSB, 0);
+ return TRUE;
+}
+
+/*
+ * ns8390intoff:
+ *
+ * This function turns interrupts off for the ns8390 board indicated.
+ *
+ */
+void
+ns8390intoff(unit)
+int unit;
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ int temp_cr = inb(nic+ds_cmd); /* get current CR value */
+
+ outb(nic+ds_cmd,((temp_cr & 0x3F)|DSCM_PG0|DSCM_STOP));
+ outb(nic+ds0_imr, 0); /* Interrupt Mask Register */
+ outb(nic+ds_cmd, temp_cr|DSCM_STOP);
+
+}
+
+
+/*
+ * wd80xxget_board_id:
+ *
+ * determine which board is being used.
+ * Currently supports:
+ * wd8003E (tested)
+ * wd8003EBT
+ * wd8003EP (tested)
+ * wd8013EP (tested)
+ *
+ */
+wd80xxget_board_id(dev)
+struct bus_device *dev;
+{
+ vm_offset_t hdwbase = dev->address;
+ long unit = dev->unit;
+ long board_id = 0;
+ int reg_temp;
+ int rev_num; /* revision number */
+ int ram_flag;
+ int intr_temp;
+ int i;
+ boolean_t register_aliasing;
+
+ rev_num = (inb(hdwbase + IFWD_BOARD_ID) & IFWD_BOARD_REV_MASK) >> 1;
+ printf("%s%d: ", ns8390_softc[unit].card, unit);
+
+ if (rev_num == 0) {
+ printf("rev 0x00\n");
+ /* It must be 8000 board */
+ return 0;
+ }
+
+ /* Check if register aliasing is true, that is reading from register
+ offsets 0-7 will return the contents of register offsets 8-f */
+
+ register_aliasing = TRUE;
+ for (i = 1; i < 5; i++) {
+ if (inb(hdwbase + IFWD_REG_0 + i) !=
+ inb(hdwbase + IFWD_LAR_0 + i))
+ register_aliasing = FALSE;
+ }
+ if (inb(hdwbase + IFWD_REG_7) != inb(hdwbase + IFWD_CHKSUM))
+ register_aliasing = FALSE;
+
+
+ if (register_aliasing == FALSE) {
+ /* Check if board has interface chip */
+
+ reg_temp = inb(hdwbase + IFWD_REG_7); /* save old */
+ outb(hdwbase + IFWD_REG_7, 0x35); /* write value */
+ inb(hdwbase + IFWD_REG_0); /* dummy read */
+ if ((inb(hdwbase + IFWD_REG_7) & 0xff) == 0x35) {
+ outb(hdwbase + IFWD_REG_7, 0x3a);/* Try another value*/
+ inb(hdwbase + IFWD_REG_0); /* dummy read */
+ if ((inb(hdwbase + IFWD_REG_7) & 0xff) == 0x3a) {
+ board_id |= IFWD_INTERFACE_CHIP;
+ outb(hdwbase + IFWD_REG_7, reg_temp);
+ /* restore old value */
+ }
+ }
+
+ /* Check if board is 16 bit by testing if bit zero in
+ register 1 is unchangeable by software. If so then
+ card has 16 bit capability */
+ reg_temp = inb(hdwbase + IFWD_REG_1);
+ outb(hdwbase + IFWD_REG_1, reg_temp ^ IFWD_16BIT);
+ inb(hdwbase + IFWD_REG_0); /* dummy read */
+ if ((inb(hdwbase + IFWD_REG_1) & IFWD_16BIT) ==
+ (reg_temp & IFWD_16BIT)) { /* Is bit unchanged */
+ board_id |= IFWD_BOARD_16BIT; /* Yes == 16 bit */
+ reg_temp &= 0xfe; /* For 16 bit board
+ always reset bit 0 */
+ }
+ outb(hdwbase + IFWD_REG_1, reg_temp); /* write value back */
+
+ /* Test if 16 bit card is in 16 bit slot by reading bit zero in
+ register 1. */
+ if (board_id & IFWD_BOARD_16BIT) {
+ if (inb(hdwbase + IFWD_REG_1) & IFWD_16BIT) {
+ board_id |= IFWD_SLOT_16BIT;
+ }
+ }
+ }
+
+ /* Get media type */
+
+ if (inb(hdwbase + IFWD_BOARD_ID) & IFWD_MEDIA_TYPE) {
+ board_id |= IFWD_ETHERNET_MEDIA;
+ } else if (rev_num == 1) {
+ board_id |= IFWD_STARLAN_MEDIA;
+ } else {
+ board_id |= IFWD_TWISTED_PAIR_MEDIA;
+ }
+
+ if (rev_num == 2) {
+ if (inb(hdwbase + IFWD_BOARD_ID) & IFWD_SOFT_CONFIG) {
+ if ((board_id & IFWD_STATIC_ID_MASK) == WD8003EB ||
+ (board_id & IFWD_STATIC_ID_MASK) == WD8003W) {
+ board_id |= IFWD_ALTERNATE_IRQ_BIT;
+ }
+ }
+ /* Check for memory size */
+
+ ram_flag = inb(hdwbase + IFWD_BOARD_ID) & IFWD_MEMSIZE;
+
+ switch (board_id & IFWD_STATIC_ID_MASK) {
+ case WD8003E: /* same as WD8003EBT */
+ case WD8003S: /* same as WD8003SH */
+ case WD8003WT:
+ case WD8003W:
+ case WD8003EB: /* same as WD8003EP */
+ if (ram_flag)
+ board_id |= IFWD_RAM_SIZE_32K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ break;
+ case WD8003ETA:
+ case WD8003STA:
+ case WD8003EA:
+ case WD8003SHA:
+ case WD8003WA:
+ board_id |= IFWD_RAM_SIZE_16K;
+ break;
+ case WD8013EBT:
+ if (board_id & IFWD_SLOT_16BIT) {
+ if (ram_flag)
+ board_id |= IFWD_RAM_SIZE_64K;
+ else
+ board_id |= IFWD_RAM_SIZE_16K;
+ } else {
+ if (ram_flag)
+ board_id |= IFWD_RAM_SIZE_32K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ }
+ break;
+ default:
+ board_id |= IFWD_RAM_SIZE_UNKNOWN;
+ break;
+ }
+ } else if (rev_num >= 3) {
+ board_id &= (long) ~IFWD_MEDIA_MASK; /* remove media info */
+ board_id |= IFWD_INTERFACE_584_CHIP;
+ board_id |= wd80xxget_eeprom_info(hdwbase, board_id);
+ } else {
+ /* Check for memory size */
+ if (board_id & IFWD_BOARD_16BIT) {
+ if (board_id & IFWD_SLOT_16BIT)
+ board_id |= IFWD_RAM_SIZE_16K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ } else if (board_id & IFWD_MICROCHANNEL)
+ board_id |= IFWD_RAM_SIZE_16K;
+ else if (board_id & IFWD_INTERFACE_CHIP) {
+ if (inb(hdwbase + IFWD_REG_1) & IFWD_MEMSIZE)
+ board_id |= IFWD_RAM_SIZE_32K;
+ else
+ board_id |= IFWD_RAM_SIZE_8K;
+ } else
+ board_id |= IFWD_RAM_SIZE_UNKNOWN;
+
+ /* No support for 690 chip yet. It should be checked here */
+ }
+
+ switch (board_id & IFWD_STATIC_ID_MASK) {
+ case WD8003E: printf("WD8003E or WD8003EBT"); break;
+ case WD8003S: printf("WD8003S or WD8003SH"); break;
+ case WD8003WT: printf("WD8003WT"); break;
+ case WD8003W: printf("WD8003W"); break;
+ case WD8003EB:
+ if (board_id & IFWD_INTERFACE_584_CHIP)
+ printf("WD8003EP");
+ else
+ printf("WD8003EB");
+ break;
+ case WD8003EW: printf("WD8003EW"); break;
+ case WD8003ETA: printf("WD8003ETA"); break;
+ case WD8003STA: printf("WD8003STA"); break;
+ case WD8003EA: printf("WD8003EA"); break;
+ case WD8003SHA: printf("WD8003SHA"); break;
+ case WD8003WA: printf("WD8003WA"); break;
+ case WD8013EBT: printf("WD8013EBT"); break;
+ case WD8013EB:
+ if (board_id & IFWD_INTERFACE_584_CHIP)
+ printf("WD8013EP");
+ else
+ printf("WD8013EB");
+ break;
+ case WD8013W: printf("WD8013W"); break;
+ case WD8013EW: printf("WD8013EW"); break;
+ default: printf("unknown"); break;
+ }
+ printf(" rev 0x%02x", rev_num);
+ switch(board_id & IFWD_RAM_SIZE_RES_7) {
+ case IFWD_RAM_SIZE_UNKNOWN:
+ break;
+ case IFWD_RAM_SIZE_8K:
+ printf(" 8 kB ram");
+ break;
+ case IFWD_RAM_SIZE_16K:
+ printf(" 16 kB ram");
+ break;
+ case IFWD_RAM_SIZE_32K:
+ printf(" 32 kB ram");
+ break;
+ case IFWD_RAM_SIZE_64K:
+ printf(" 64 kB ram");
+ break;
+ default:
+ printf("wd: Internal error ram size value invalid %d\n",
+ (board_id & IFWD_RAM_SIZE_RES_7)>>16);
+ }
+
+ if (board_id & IFWD_BOARD_16BIT) {
+ if (board_id & IFWD_SLOT_16BIT) {
+ printf(", in 16 bit slot");
+ } else {
+ printf(", 16 bit board in 8 bit slot");
+ }
+ }
+ if (board_id & IFWD_INTERFACE_CHIP) {
+ if (board_id & IFWD_INTERFACE_584_CHIP) {
+ printf(", 584 chip");
+ } else {
+ printf(", 583 chip");
+ }
+ }
+ if ((board_id & IFWD_INTERFACE_CHIP) == IFWD_INTERFACE_CHIP) {
+ /* program the WD83C583 EEPROM registers */
+ int irr_temp, icr_temp;
+
+ icr_temp = inb(hdwbase + IFWD_ICR);
+ irr_temp = inb(hdwbase + IFWD_IRR);
+
+ irr_temp &= ~(IFWD_IR0 | IFWD_IR1);
+ irr_temp |= IFWD_IEN;
+
+ icr_temp &= IFWD_WTS;
+
+ if (!(board_id & IFWD_INTERFACE_584_CHIP)) {
+ icr_temp |= IFWD_DMAE | IFWD_IOPE;
+ if (ram_flag)
+ icr_temp |= IFWD_MSZ;
+ }
+
+ if (board_id & IFWD_INTERFACE_584_CHIP) {
+ switch(ns8390info[unit]->sysdep1) {
+ case 10:
+ icr_temp |= IFWD_DMAE;
+ break;
+ case 2:
+ case 9: /* Same as 2 */
+ break;
+ case 11:
+ icr_temp |= IFWD_DMAE;
+ /*FALLTHROUGH*/
+ case 3:
+ irr_temp |= IFWD_IR0;
+ break;
+ case 15:
+ icr_temp |= IFWD_DMAE;
+ /*FALLTHROUGH*/
+ case 5:
+ irr_temp |= IFWD_IR1;
+ break;
+ case 4:
+ icr_temp |= IFWD_DMAE;
+ /*FALLTHROUGH*/
+ case 7:
+ irr_temp |= IFWD_IR0 | IFWD_IR1;
+ break;
+ default:
+ printf("%s%d: wd80xx_get_board_id(): Could not set Interrupt Request Register according to pic(%d).\n",
+ ns8390_softc[unit].card, unit,
+ ns8390info[unit]->sysdep1);
+ break;
+ }
+ } else {
+ switch(ns8390info[unit]->sysdep1) {
+ /* attempt to set interrupt according to assigned pic */
+ case 2:
+ case 9: /* Same as 2 */
+ break;
+ case 3:
+ irr_temp |= IFWD_IR0;
+ break;
+ case 4:
+ irr_temp |= IFWD_IR1;
+ break;
+ case 5:
+ irr_temp |= IFWD_IR1 | IFWD_AINT;
+ break;
+ case 7:
+ irr_temp |= IFWD_IR0 | IFWD_IR1;
+ break;
+ default:
+ printf("%s%d: wd80xx_get_board_id(): Could not set Interrupt Request Register according to pic(%d).\n",
+ ns8390_softc[unit].card, unit,
+ ns8390info[unit]->sysdep1);
+ }
+ }
+ outb(hdwbase + IFWD_IRR, irr_temp);
+ outb(hdwbase + IFWD_ICR, icr_temp);
+ }
+ printf("\n");
+ return (board_id);
+}
+
+wd80xxget_eeprom_info(hdwbase, board_id)
+ caddr_t hdwbase;
+ long board_id;
+{
+ unsigned long new_bits = 0;
+ int reg_temp;
+
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) | IFWD_OTHER_BIT));
+ outb(hdwbase + IFWD_REG_3,
+ ((inb(hdwbase + IFWD_REG_3) & IFWD_EAR_MASK) | IFWD_ENGR_PAGE));
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) |
+ (IFWD_RLA | IFWD_OTHER_BIT)));
+ while (inb(hdwbase + IFWD_REG_1) & IFWD_RECALL_DONE_MASK)
+ ;
+
+ reg_temp = inb(hdwbase + IFWD_EEPROM_1);
+ switch (reg_temp & IFWD_EEPROM_BUS_TYPE_MASK) {
+ case IFWD_EEPROM_BUS_TYPE_AT:
+ if (wd_debug & 1) printf("wd: AT bus, ");
+ break;
+ case IFWD_EEPROM_BUS_TYPE_MCA:
+ if (wd_debug & 1) printf("wd: MICROCHANNEL, ");
+ new_bits |= IFWD_MICROCHANNEL;
+ break;
+ default:
+ break;
+ }
+ switch (reg_temp & IFWD_EEPROM_BUS_SIZE_MASK) {
+ case IFWD_EEPROM_BUS_SIZE_8BIT:
+ if (wd_debug & 1) printf("8 bit bus size, ");
+ break;
+ case IFWD_EEPROM_BUS_SIZE_16BIT:
+ if (wd_debug & 1) printf("16 bit bus size ");
+ new_bits |= IFWD_BOARD_16BIT;
+ if (inb(hdwbase + IFWD_REG_1) & IFWD_16BIT) {
+ new_bits |= IFWD_SLOT_16BIT;
+ if (wd_debug & 1)
+ printf("in 16 bit slot, ");
+ } else {
+ if (wd_debug & 1)
+ printf("in 8 bit slot (why?), ");
+ }
+ break;
+ default:
+ if (wd_debug & 1) printf("bus size other than 8 or 16 bit, ");
+ break;
+ }
+ reg_temp = inb(hdwbase + IFWD_EEPROM_0);
+ switch (reg_temp & IFWD_EEPROM_MEDIA_MASK) {
+ case IFWD_STARLAN_TYPE:
+ if (wd_debug & 1) printf("Starlan media, ");
+ new_bits |= IFWD_STARLAN_MEDIA;
+ break;
+ case IFWD_TP_TYPE:
+ if (wd_debug & 1) printf("Twisted pair media, ");
+ new_bits |= IFWD_TWISTED_PAIR_MEDIA;
+ break;
+ case IFWD_EW_TYPE:
+ if (wd_debug & 1) printf("Ethernet and twisted pair media, ");
+ new_bits |= IFWD_EW_MEDIA;
+ break;
+ case IFWD_ETHERNET_TYPE: /*FALLTHROUGH*/
+ default:
+ if (wd_debug & 1) printf("ethernet media, ");
+ new_bits |= IFWD_ETHERNET_MEDIA;
+ break;
+ }
+ switch (reg_temp & IFWD_EEPROM_IRQ_MASK) {
+ case IFWD_ALTERNATE_IRQ_1:
+ if (wd_debug & 1) printf("Alternate irq 1\n");
+ new_bits |= IFWD_ALTERNATE_IRQ_BIT;
+ break;
+ default:
+ if (wd_debug & 1) printf("\n");
+ break;
+ }
+ switch (reg_temp & IFWD_EEPROM_RAM_SIZE_MASK) {
+ case IFWD_EEPROM_RAM_SIZE_8K:
+ new_bits |= IFWD_RAM_SIZE_8K;
+ break;
+ case IFWD_EEPROM_RAM_SIZE_16K:
+ if ((new_bits & IFWD_BOARD_16BIT) && (new_bits & IFWD_SLOT_16BIT))
+ new_bits |= IFWD_RAM_SIZE_16K;
+ else
+ new_bits |= IFWD_RAM_SIZE_8K;
+ break;
+ case IFWD_EEPROM_RAM_SIZE_32K:
+ new_bits |= IFWD_RAM_SIZE_32K;
+ break;
+ case IFWD_EEPROM_RAM_SIZE_64K:
+ if ((new_bits & IFWD_BOARD_16BIT) && (new_bits & IFWD_SLOT_16BIT))
+ new_bits |= IFWD_RAM_SIZE_64K;
+ else
+ new_bits |= IFWD_RAM_SIZE_32K;
+ break;
+ default:
+ new_bits |= IFWD_RAM_SIZE_UNKNOWN;
+ break;
+ }
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) | IFWD_OTHER_BIT));
+ outb(hdwbase + IFWD_REG_3,
+ ((inb(hdwbase + IFWD_REG_3) & IFWD_EAR_MASK) | IFWD_EA6));
+ outb(hdwbase + IFWD_REG_1,
+ ((inb(hdwbase + IFWD_REG_1) & IFWD_ICR_MASK) | IFWD_RLA));
+ return (new_bits);
+}
+
+wdpr(unit)
+{
+ caddr_t nic = ns8390_softc[unit].nic;
+ spl_t s;
+ int temp_cr;
+
+ s = SPLNET();
+ temp_cr = inb(nic+ds_cmd); /* get current CR value */
+
+ printf("CR %x, BNDRY %x, TSR %x, NCR %x, FIFO %x, ISR %x, RSR %x\n",
+ inb(nic+0x0), inb(nic+0x3), inb(nic+0x4), inb(nic+0x5),
+ inb(nic+0x6), inb(nic+0x7), inb(nic+0xc));
+ printf("CLD %x:%x, CRD %x:%x, FR %x, CRC %x, Miss %x\n",
+ inb(nic+0x1), inb(nic+0x2),
+ inb(nic+0x8), inb(nic+0x9),
+ inb(nic+0xd), inb(nic+0xe), inb(nic+0xf));
+
+
+ outb(nic, (temp_cr&0x3f)|DSCM_PG1); /* page 1 CR value */
+ printf("PHYS %x:%x:%x:%x:%x CUR %x\n",
+ inb(nic+0x1), inb(nic+0x2), inb(nic+0x3),
+ inb(nic+0x4), inb(nic+0x5), inb(nic+0x6),
+ inb(nic+0x7));
+ printf("MAR %x:%x:%x:%x:%x:%x:%x:%x\n",
+ inb(nic+0x8), inb(nic+0x9), inb(nic+0xa), inb(nic+0xb),
+ inb(nic+0xc), inb(nic+0xd), inb(nic+0xe), inb(nic+0xf));
+ outb(nic, temp_cr); /* restore current CR value */
+ splx(s);
+}
+
+
+/*
+ This sets bit 7 (0 justified) of register offset 0x05. It will enable
+ the host to access shared RAM 16 bits at a time. It will also maintain
+ the LAN16BIT bit high in addition, this routine maintains address bit 19
+ (previous cards assumed this bit high...we must do it manually)
+
+ note 1: this is a write only register
+ note 2: this routine should be called only after interrupts are disabled
+ and they should remain disabled until after the routine 'dis_16bit_access'
+ is called
+*/
+
+en_16bit_access (hdwbase, board_id)
+ caddr_t hdwbase;
+ long board_id;
+{
+ if (board_id & IFWD_INTERFACE_CHIP)
+ outb(hdwbase+IFWD_REG_5,
+ (inb(hdwbase+IFWD_REG_5) & IFWD_REG5_MEM_MASK)
+ | IFWD_MEM16ENB | IFWD_LAN16ENB);
+ else
+ outb(hdwbase+IFWD_REG_5, (IFWD_MEM16ENB | IFWD_LAN16ENB |
+ IFWD_LA19));
+}
+
+/*
+ This resets bit 7 (0 justified) of register offset 0x05. It will disable
+ the host from accessing shared RAM 16 bits at a time. It will maintain the
+ LAN16BIT bit high in addition, this routine maintains address bit 19
+ (previous cards assumed this bit high...we must do it manually)
+
+ note: this is a write only register
+*/
+
+dis_16bit_access (hdwbase, board_id)
+ caddr_t hdwbase;
+ long board_id;
+{
+ if (board_id & IFWD_INTERFACE_CHIP)
+ outb(hdwbase+IFWD_REG_5,
+ ((inb(hdwbase+IFWD_REG_5) & IFWD_REG5_MEM_MASK) |
+ IFWD_LAN16ENB));
+ else
+ outb(hdwbase+IFWD_REG_5, (IFWD_LAN16ENB | IFWD_LA19));
+}
+
+#endif
diff --git a/i386/i386at/if_ns8390.h b/i386/i386at/if_ns8390.h
new file mode 100644
index 00000000..9466c364
--- /dev/null
+++ b/i386/i386at/if_ns8390.h
@@ -0,0 +1,203 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Western Digital Mach Ethernet driver
+ * Copyright (c) 1990 OSF Research Institute
+ */
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/********************************************/
+/* Defines for the NIC 8390 Lan Controller */
+/********************************************/
+
+
+/*-- page 0, rd --*/
+#define CR 0x00 /* Command Register */
+#define CLDA0 0x01 /* Current Local DMA Address 0 */
+#define CLDA1 0x02 /* Current Local DMA Address 1 */
+#define BNRY 0x03 /* Boundary Pointer */
+#define TSR 0x04 /* Transmit Status Register */
+#define NCR 0x05 /* Number of Collisions Register */
+#define FIFO 0x06 /* FIFO */
+#define ISR 0x07 /* Interrupt Status Register */
+#define CRDA0 0x08 /* Current Remote DMA Address 0 */
+#define CRDA1 0x09 /* Current Remote DMA Address 1 */
+/* 0x0A is reserved */
+/* 0x0B is reserved */
+#define RSR 0x0C /* Receive Status Register */
+#define CNTR0 0x0D /* Frame Alignment Errors */
+#define CNTR1 0x0E /* CRC Errors */
+#define CNTR2 0x0F /* Missed Packet Errors */
+
+/*-- page 0, wr --*/
+/* CR 0x00 Command Register */
+#define PSTART 0x01 /* Page Start Register */
+#define PSTOP 0x02 /* Page Stop Register */
+#define BNDY 0x03 /* Boundary Pointer */
+#define TPSR 0x04 /* Transmit Page Start Register */
+#define TBCR0 0x05 /* Transmit Byte Count Register 0*/
+#define TBCR1 0x06 /* Transmit Byte Count Register 1*/
+/* ISR 0x07 Interrupt Status Register */
+#define RSAR0 0x08 /* Remote Start Address Register 0 */
+#define RSAR1 0x09 /* Remote Start Address Register 1 */
+#define RBCR0 0x0A /* Remote Byte Count Register 0 */
+#define RBCR1 0x0B /* Remote Byte Count Register 1 */
+#define RCR 0x0C /* Receive Configuration Register */
+#define TCR 0x0D /* Transmit Configuration Register */
+#define DCR 0x0E /* Data Configuration Register */
+#define IMR 0x0F /* Interrupt Mask Register */
+
+/*-- page 1, rd and wr */
+/* CR 0x00 Control Register */
+#define PAR0 0x01 /* Physical Address Register 0 */
+#define PAR1 0x02 /* 1 */
+#define PAR2 0x03 /* 2 */
+#define PAR3 0x04 /* 3 */
+#define PAR4 0x05 /* 4 */
+#define PAR5 0x06 /* 5 */
+#define CURR 0x07 /* Current Page Register */
+#define MAR0 0x08 /* Multicast Address Register 0 */
+#define MAR1 0x09 /* 1 */
+#define MAR2 0x0A /* 2 */
+#define MAR3 0x0B /* 3 */
+#define MAR4 0x0C /* 4 */
+#define MAR5 0x0D /* 5 */
+#define MAR6 0x0E /* 6 */
+#define MAR7 0x0F /* 7 */
+
+/*-- page 2, rd --*/
+
+/*-- page 2, wr --*/
+
+/*-- Command Register CR description */
+#define STP 0x01 /* stop; software reset */
+#define STA 0x02 /* start */
+#define TXP 0x04 /* transmit packet */
+#define RD0 0x08
+#define RD1 0x10
+#define RD2 0x20
+#define RRD 0x08 /* remote DMA command - remote read */
+
+#define RWR 0x10 /* remote DMA command - remote write */
+#define SPK 0x18 /* remote DMA command - send packet */
+#define ABR 0x20 /* remote DMA command - abrt/cmplt remote DMA */
+
+#define PS0 0x00 /* register page select - 0 */
+#define PS1 0x40 /* register page select - 1 */
+#define PS2 0x80 /* register page select - 2 */
+
+#define PS0_STA 0x22 /* page select 0 with start bit maintained */
+#define PS1_STA 0x62 /* page select 1 with start bit maintained */
+#define PS2_STA 0x0A2 /* page select 2 with start bit maintained */
+
+/*-- Interrupt Status Register ISR description */
+#define PRX 0x01 /* packet received no error */
+#define PTX 0x02 /* packet transmitted no error */
+#define RXE 0x04 /* receive error */
+#define TXE 0x08 /* transmit error */
+#define OVW 0x10 /* overwrite warning */
+#define CNT 0x20 /* counter overflow */
+#define RDC 0x40 /* remote DMA complete */
+#define RST 0x80 /* reset status */
+
+/*-- Interrupt Mask Register IMR description */
+#define PRXE 0x01 /* packet received interrupt enable */
+#define PTXE 0x02 /* packet transmitted interrupt enable */
+#define RXEE 0x04 /* receive error interrupt enable */
+#define TXEE 0x08 /* transmit error interrupt enable */
+#define OVWE 0x10 /* overwrite warning interrupt enable */
+#define CNTE 0x20 /* counter overflow interrupt enable */
+#define RDCE 0x40 /* DMA complete interrupt enable */
+
+/*-- Data Configuration Register DCR description */
+#define WTS 0x01 /* word transfer select */
+#define BOS 0x02 /* byte order select */
+#define LAS 0x04 /* long address select */
+#define BMS 0x08 /* burst DMA select */
+#define AINIT 0x10 /* autoinitialize remote */
+
+#define FTB2 0x00 /* receive FIFO threshold select - 2 bytes */
+#define FTB4 0x20 /* receive FIFO threshold select - 4 bytes */
+#define FTB8 0x40 /* receive FIFO threshold select - 8 bytes */
+#define FTB12 0x60 /* receive FIFO threshold select - 12 bytes */
+
+/*-- Transmit Configuration Register TCR description */
+#define MCRC 0x01 /* manual crc generation */
+#define LB1 0x02 /* mode 1; internal loopback LPBK=0 */
+#define LB2 0x04 /* mode 2; internal loopback LPBK=1 */
+#define LB3 0x06 /* mode 3; internal loopback LPBK=0 */
+
+#define ATD 0x08 /* auto transmit disable */
+#define OFST 0x10 /* collision offset enable */
+
+/*-- Transmit Status Register TSR description --*/
+#define XMT 0x01 /* packet transmitted without error */
+#define COL 0x04 /* transmit collided */
+#define ABT 0x08 /* transmit aborted */
+#define CRS 0x10 /* carrier sense lost - xmit not aborted */
+#define FU 0x20 /* FIFO underrun */
+#define CDH 0x40 /* CD heartbeat */
+#define OWC 0x80 /* out of window collision - xmit not aborted */
+
+/*-- Receive Configuration Register RCR description --*/
+#define SEP 0x01 /* save error packets */
+#define AR 0x02 /* accept runt packet */
+#define AB 0x04 /* accept broadcast */
+#define AM 0x08 /* accept multicast */
+#define PRO 0x10 /* promiscuous physical */
+#define MON 0x20 /* monitor mode */
+
+/*--Receive Status Register RSR description --*/
+#define RCV 0x01 /* packet received intact */
+#define CRC 0x02 /* CRC error */
+#define FAE 0x04 /* frame alignment error */
+#define FO 0x08 /* FIFO overrun */
+#define MPA 0x10 /* missed packet */
+#define PHY 0x20 /* physical/multicast address */
+#define DIS 0x40 /* receiver disable */
+#define DFR 0x80 /* deferring */
diff --git a/i386/i386at/if_par.c b/i386/i386at/if_par.c
new file mode 100644
index 00000000..3995fadc
--- /dev/null
+++ b/i386/i386at/if_par.c
@@ -0,0 +1,456 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993,1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Parallel port network driver v1.1
+ * All rights reserved.
+ */
+
+/*
+ Subject: parallel network interface
+
+ The printer network driver has the following hardware requirements for the
+ interconnection cable:
+
+ Connections:
+ Side1 Side2 Function Side1 / Side2
+ Pin 5 Pin 10 Interrupt strobe: send status (w)/send status (r)
+ Pin 2 Pin 15 Data bits : write / read
+ Pin 3 Pin 13 Data bits : write / read
+ Pin 4 Pin 12 Data bits : write / read
+ Pin 6 Pin 11 Data bits : write / read
+ Pin 10 Pin 5
+ Pin 11 Pin 6
+ Pin 12 Pin 4
+ Pin 13 Pin 3
+ Pin 15 Pin 2
+ Pins 18-25 Pins 18-25 (ground interconnections)
+
+ The cable is "symmetric" in that either side can be plugged into either of the
+ computers.
+
+ The hardware requirements are as follows:
+ Port 0x378 must be writable with the following specifications:
+ Bit 4 -> pin 6
+ Bit 3 -> pin 5
+ Bit 2 -> pin 4
+ Bit 1 -> pin 3
+ Bit 0 -> pin 2
+ Port 0x379 must be readable with the following specifications:
+ Bit 7 <- pin 11
+ Bit 6 <- pin 10
+ Bit 5 <- pin 12
+ Bit 4 <- pin 13
+ Bit 3 <- pin 15
+ Port 0x37a must be readable and writable with the following specifications:
+ Bit 4 -> interrupt enable
+ So Port 0x378 connects to Port 0x379 as
+ Bit 3 -> pin 5 : pin 10 -> Bit 6 0x08 -> 0x40
+
+ Bit 4 -> pin 6 : pin 11 -> Bit 7 0x08<<1 -> ~ 0x80
+ Bit 2 -> pin 4 : pin 12 -> Bit 5 0x07 -> 0x38
+ Bit 1 -> pin 3 : pin 13 -> Bit 4 0x07 -> 0x38
+ Bit 0 -> pin 2 : pin 15 -> Bit 3 0x07 -> 0x38
+ [note: bit 0 is considered the least significant bit, pins on the connector
+ are numbered starting with 1, -> represents sending data out on the bus, <-
+ represents reading data from the bus]
+
+ Pins 1,7,8,9, and 16 are currently unused, and may be allowed to "float".
+
+ The data is sent in 4 bit "nybbles", with the highest 4 bits being sent first.
+
+ To bring up the interface, all that should be required is
+ ifconfig par0 <your ip address> <connected machine's ip address> up
+ and to bring down the interface
+ ifconfig par0 down
+ You may get a warning message (such as printer out of paper) once you down
+ the interface, as the port is monitored for both printer and network activity
+ depending on whether par0 is up or down, and when you down the interface the
+ printer driver will then read whatever is on the port (which will be the last
+ message from the other computer).
+ */
+
+#include <par.h>
+#if NPAR > 0
+
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <chips/busses.h>
+#include <i386at/if_par.h>
+
+
+int parintr();
+int parioctl();
+int parattach();
+int paroutput();
+
+int (*oldvect)();
+int oldunit;
+
+extern struct bus_device *lprinfo[];
+
+int par_watch = 0;
+
+struct par_softc {
+ struct ifnet ds_if;
+ u_char ds_addr[6]; /* Ethernet hardware address */
+ u_char address[6];
+ char sc_buf[PARMTU+sizeof(struct ifnet *)];
+} par_softc[NPAR];
+
+void parintoff(unit)
+int unit;
+{
+struct bus_device *lpdev = lprinfo[unit];
+
+ outb(INTR(lpdev->address), 0x07);
+ par_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ ivect[lpdev->sysdep1] = oldvect;
+ iunit[lpdev->sysdep1] = oldunit;
+}
+
+void parinit(unit)
+int unit;
+{
+struct bus_device *lpdev = lprinfo[unit];
+
+ if (ivect[lpdev->sysdep1] != parintr) {
+ oldvect = ivect[lpdev->sysdep1];
+ oldunit = iunit[lpdev->sysdep1];
+ ivect[lpdev->sysdep1] = parintr;
+ iunit[lpdev->sysdep1] = unit;
+ }
+ outb(INTR(lpdev->address),0x11);
+ par_softc[unit].ds_if.if_flags |= IFF_RUNNING;
+ *(struct ifnet **)par_softc[unit].sc_buf = &par_softc[unit].ds_if;
+}
+
+struct ether_header par_eh;
+
+int parattach(dev)
+struct bus_device *dev;
+{
+ u_char unit = (u_char)dev->unit;
+ struct ifnet *ifp;
+ struct par_softc*sp;
+
+ if ((unit < 0) || (unit >= NPAR))
+ return(0);
+ printf("\n par%d: at lpr%d, port = %x, spl = %d, pic = %d. ",
+ unit, unit, dev->address, dev->sysdep, dev->sysdep1);
+
+ sp = &par_softc[unit];
+ ifp = &(sp->ds_if);
+
+ *(sp->ds_addr) = *(sp->address) = 0x11;
+ *(sp->ds_addr + 1) = *(sp->address + 1) = 0x22;
+ *(sp->ds_addr + 2) = *(sp->address + 2) = 0x33;
+ *(sp->ds_addr + 3) = *(sp->address + 3) = 0x44;
+ *(sp->ds_addr + 4) = *(sp->address + 4) = 0x55;
+ *(sp->ds_addr + 5) = *(sp->address + 5) = 0x66;
+
+ par_eh.ether_dhost[5] = par_eh.ether_shost[0] = 0x11;
+ par_eh.ether_dhost[4] = par_eh.ether_shost[1] = 0x22;
+ par_eh.ether_dhost[3] = par_eh.ether_shost[2] = 0x33;
+ par_eh.ether_dhost[2] = par_eh.ether_shost[3] = 0x44;
+ par_eh.ether_dhost[1] = par_eh.ether_shost[4] = 0x55;
+ par_eh.ether_dhost[0] = par_eh.ether_shost[5] = 0x66;
+ par_eh.ether_type = htons(0x0800);
+
+ printf("ethernet id [%x:%x:%x:%x:%x:%x]",
+ sp->address[0],sp->address[1],sp->address[2],
+ sp->address[3],sp->address[4],sp->address[5]);
+
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_POINTOPOINT;
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&par_softc[unit].address[0];
+ if_init_queues(ifp);
+ return(0);
+}
+
+int parstart(); /* forward */
+
+/*ARGSUSED*/
+paropen(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NPAR)
+ return (ENXIO);
+
+ par_softc[unit].ds_if.if_flags |= IFF_UP;
+ parinit(unit);
+ return(0);
+}
+
+paroutput(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NPAR)
+ return (ENXIO);
+ return (net_write(&par_softc[unit].ds_if, parstart, ior));
+}
+
+parsetinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t filter[];
+ unsigned int filter_count;
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NPAR)
+ return (ENXIO);
+
+ return (net_set_filter(&par_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+
+int parstart(unit)
+{
+ struct ifnet *ifp = &(par_softc[unit].ds_if);
+ u_short addr = lprinfo[unit]->address;
+ struct sockaddr *dst;
+ int len, i;
+ spl_t s;
+ u_char *mcp, c;
+ io_req_t m;
+
+ if (!(ifp->if_flags & IFF_RUNNING)) {
+#ifdef WHY
+ m_free(m);
+ parintoff(unit);
+ return(ENETDOWN);
+#else WHY
+ parintoff(unit);
+ return(-1);
+#endif WHY
+ }
+ s = SPLNET();
+
+ IF_DEQUEUE(&ifp->if_snd, m);
+ if (m == 0) {
+ splx(s);
+ return 0;
+ }
+ len = m->io_count;
+ if (par_watch)
+ printf("O%d\n",len);
+ len -= 14 /* XXX */;
+ mcp = (u_char *)m->io_data + 14 /* XXX */;
+ while (len--) {
+ c=*mcp++;
+ outb(OUTPUT(addr),((c&0x80)>>3) | ((c&0x70)>>4) | 0x08);
+ i=MAXSPIN;
+ while (!(inb(INPUT(addr))&0x40) && --i);
+ outb(OUTPUT(addr),((c&0x08)<<1) | (c&0x07));
+ i=MAXSPIN;
+ while ((inb(INPUT(addr))&0x40) && --i);
+ }
+ outb(OUTPUT(addr),(((c&0x08)<<1) | (c&0x07))^0x17);
+ iodone(m);
+ splx(s);
+ return (0);
+}
+
+/*ARGSUSED*/
+pargetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status; /* pointer to OUT array */
+ unsigned int *count; /* out */
+{
+ register int unit = minor(dev);
+
+ if (unit < 0 || unit >= NPAR)
+ return (ENXIO);
+
+
+ switch (flavor) {
+ case NET_DSTADDR:
+ return (D_SUCCESS);
+ break;
+ }
+
+ return (net_getstat(&par_softc[unit].ds_if,
+ flavor,
+ status,
+ count));
+}
+
+parsetstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned int count;
+{
+ register int unit = minor(dev);
+ register struct par_softc *sp;
+
+ if (unit < 0 || unit >= NPAR)
+ return (ENXIO);
+
+ sp = &par_softc[unit];
+
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ /*
+ * All we can change are flags, and not many of those.
+ */
+ register struct net_status *ns = (struct net_status *)status;
+ int mode = 0;
+
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_SIZE);
+
+#if 0
+ /* ha ha ha */
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+ /*
+ * Force a complete reset if the receive mode changes
+ * so that these take effect immediately.
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ parinit(unit);
+ }
+ }
+#endif
+ break;
+ }
+ case NET_ADDRESS:
+ {
+ register union ether_cvt {
+ char addr[6];
+ int lwd[2];
+ } *ec = (union ether_cvt *)status;
+
+ if (count < sizeof(*ec)/sizeof(int))
+ return (D_INVALID_SIZE);
+
+ ec->lwd[0] = ntohl(ec->lwd[0]);
+ ec->lwd[1] = ntohl(ec->lwd[1]);
+/* at3c501seteh(sp->base, ec->addr);*/
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+int parintr(unit)
+int unit;
+{
+ register struct par_softc *sp = &par_softc[unit];
+ u_short addr = lprinfo[unit]->address;
+ char *trav = sp->sc_buf;
+ short len = 0;
+ u_char c, c2;
+ int i;
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+ struct ifnet *ifp = &(sp->ds_if);
+
+ do {
+ c2=inb(INPUT(addr));
+ outb(OUTPUT(addr),0x08);
+ i=MAXSPIN;
+ while(((c=inb(INPUT(addr)))&0x40) && --i);
+
+ c = inb(INPUT(addr));
+ outb(OUTPUT(addr),0x00);
+ if (!i)
+ break;
+
+ if (++len > ETHERMTU) {
+ trav = sp->sc_buf;
+ len = 0;
+ continue;
+ }
+ *trav++ = ((~c2)&0x80) | ((c2&0x38)<<1) | (((~c)&0x80)>>4) | ((c&0x38)>>3);
+ i=MAXSPIN;
+ while (!((c2=inb(INPUT(addr)))&0x40) && --i)
+ if (((c2^0xb8)&0xf8) == (c&0xf8))
+ goto end;
+ } while (i);
+end:
+ if (len < 20) /* line noise ? */
+ return;
+ if (par_watch)
+ printf("I%d\n",len);
+
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the packet.
+ */
+ sp->ds_if.if_rcvdrops++;
+ return;
+ }
+ ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *) (&net_kmsg(new_kmsg)->packet[0]);
+ *ehp = par_eh;
+
+ bcopy (sp->sc_buf, (char *) (pkt + 1), len);
+
+ pkt->type = ehp->ether_type;
+ pkt->length = len + sizeof(struct packet_header);
+ /*
+ * Hand the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length,
+ ethernet_priority(new_kmsg));
+ return(0);
+}
+#endif
diff --git a/i386/i386at/if_par.h b/i386/i386at/if_par.h
new file mode 100644
index 00000000..2cb7ed5d
--- /dev/null
+++ b/i386/i386at/if_par.h
@@ -0,0 +1,36 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Parallel port network driver v1.0
+ * All rights reserved.
+ */
+#define OUTPUT(addr) (addr + 0)
+#define INPUT(addr) (addr + 1)
+#define INTR(addr) (addr + 2)
+
+#define SPLNET spl6
+#define PARMTU 8192
+#define MAXSPIN 10000
diff --git a/i386/i386at/if_pc586.c b/i386/i386at/if_pc586.c
new file mode 100644
index 00000000..195ce7d6
--- /dev/null
+++ b/i386/i386at/if_pc586.c
@@ -0,0 +1,2076 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Olivetti PC586 Mach Ethernet driver v1.0
+ * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
+ * All rights reserved.
+ *
+ */
+
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * NOTE:
+ * by rvb:
+ * 1. The best book on the 82586 is:
+ * LAN Components User's Manual by Intel
+ * The copy I found was dated 1984. This really tells you
+ * what the state machines are doing
+ * 2. In the current design, we only do one write at a time,
+ * though the hardware is capable of chaining and possibly
+ * even batching. The problem is that we only make one
+ * transmit buffer available in sram space.
+ * 3.
+ * n. Board Memory Map
+ RFA/FD 0 - 227 0x228 bytes
+ 226 = 0x19 * 0x16 bytes
+ RBD 228 - 3813 0x35ec bytes
+ 35e8 = 0x19 * 0x228 bytes
+ == 0x0a bytes (bd) + 2 bytes + 21c bytes
+ CU 3814 - 3913 0x100 bytes
+ TBD 3914 - 39a3 0x90 bytes
+ 90 = No 18 * 0x08 bytes
+ TBUF 39a4 - 3fdd 0x63a bytes (= 1594(10))
+ SCB 3fde - 3fed 0x10 bytes
+ ISCP 3fee - 3ff5 0x08 bytes
+ SCP 3ff6 - 3fff 0x0a bytes
+ *
+ */
+
+/*
+ * NOTE:
+ *
+ * Currently this driver doesn't support trailer protocols for
+ * packets. Once that is added, please remove this comment.
+ *
+ * Also, some lacking material includes the DLI code. If you
+ * are compiling this driver with DLI set, lookout, that code
+ * has not been looked at.
+ *
+ */
+
+#define DEBUG
+#define IF_CNTRS MACH
+#define NDLI 0
+
+#include <pc586.h>
+
+#ifdef MACH_KERNEL
+#include <kern/time_out.h>
+#include <device/device_types.h>
+#include <device/errno.h>
+#include <device/io_req.h>
+#include <device/if_hdr.h>
+#include <device/if_ether.h>
+#include <device/net_status.h>
+#include <device/net_io.h>
+#else MACH_KERNEL
+#include <sys/param.h>
+#include <mach/machine/vm_param.h>
+#include <sys/systm.h>
+#include <sys/mbuf.h>
+#include <sys/buf.h>
+#include <sys/protosw.h>
+#include <sys/socket.h>
+#include <sys/vmmac.h>
+#include <sys/ioctl.h>
+#include <sys/errno.h>
+#include <sys/syslog.h>
+
+#include <net/if.h>
+#include <net/netisr.h>
+#include <net/route.h>
+
+#ifdef INET
+#include <netinet/in.h>
+#include <netinet/in_systm.h>
+#include <netinet/in_var.h>
+#include <netinet/ip.h>
+#include <netinet/if_ether.h>
+#endif
+
+#ifdef NS
+#include <netns/ns.h>
+#include <netns/ns_if.h>
+#endif
+
+#if DLI
+#include <net/dli_var.h>
+struct dli_var de_dlv[NDE];
+#endif DLI
+#endif MACH_KERNEL
+
+#include <i386/ipl.h>
+#include <mach/vm_param.h>
+#include <vm/vm_kern.h>
+#include <chips/busses.h>
+#include <i386at/if_pc586.h>
+
+#define SPLNET spl6
+#if __STDC__
+#define CMD(x, y, unit) *(u_short *)(pc_softc[unit].prom + OFFSET_ ## x) = (u_short) (y)
+#else __STDC__
+#define CMD(x, y, unit) *(u_short *)(pc_softc[unit].prom + OFFSET_/**/x) = (u_short) (y)
+#endif __STDC__
+
+#define pc586chatt(unit) CMD(CHANATT, 0x0001, unit)
+#define pc586inton(unit) CMD(INTENAB, CMD_1, unit)
+#define pc586intoff(unit) CMD(INTENAB, CMD_0, unit)
+
+int pc586probe();
+void pc586attach();
+int pc586intr(), pc586init(), pc586output(), pc586ioctl(), pc586reset();
+int pc586watch(), pc586rcv(), pc586xmt(), pc586bldcu();
+int pc586diag(), pc586config();
+char *pc586bldru();
+char *ram_to_ptr();
+u_short ptr_to_ram();
+
+static vm_offset_t pc586_std[NPC586] = { 0 };
+static struct bus_device *pc586_info[NPC586];
+struct bus_driver pcdriver =
+ {pc586probe, 0, pc586attach, 0, pc586_std, "pc", pc586_info, 0, 0, 0};
+
+char t_packet[ETHERMTU + sizeof(struct ether_header) + sizeof(long)];
+int xmt_watch = 0;
+
+typedef struct {
+#ifdef MACH_KERNEL
+ struct ifnet ds_if; /* generic interface header */
+ u_char ds_addr[6]; /* Ethernet hardware address */
+#else MACH_KERNEL
+ struct arpcom pc586_ac;
+#define ds_if pc586_ac.ac_if
+#define ds_addr pc586_ac.ac_enaddr
+#endif MACH_KERNEL
+ int flags;
+ int seated;
+ int timer;
+ int open;
+ fd_t *begin_fd;
+ fd_t *end_fd;
+ rbd_t *end_rbd;
+ char *prom;
+ char *sram;
+ int tbusy;
+ short mode;
+} pc_softc_t;
+pc_softc_t pc_softc[NPC586];
+
+struct pc586_cntrs {
+ struct {
+ u_int xmt, xmti;
+ u_int defer;
+ u_int busy;
+ u_int sleaze, intrinsic, intrinsic_count;
+ u_int chain;
+ } xmt;
+ struct {
+ u_int rcv;
+ u_int ovw;
+ u_int crc;
+ u_int frame;
+ u_int rscerrs, ovrnerrs;
+ u_int partial, bad_chain, fill;
+ } rcv;
+ u_int watch;
+} pc586_cntrs[NPC586];
+
+
+#ifdef IF_CNTRS
+int pc586_narp = 1, pc586_arp = 0;
+int pc586_ein[32], pc586_eout[32];
+int pc586_lin[128/8], pc586_lout[128/8];
+static
+log_2(no)
+unsigned long no;
+{
+ return ({ unsigned long _temp__;
+ asm("bsr %1, %0; jne 0f; xorl %0, %0; 0:" :
+ "=r" (_temp__) : "a" (no));
+ _temp__;});
+}
+#endif IF_CNTRS
+
+/*
+ * pc586probe:
+ *
+ * This function "probes" or checks for the pc586 board on the bus to see
+ * if it is there. As far as I can tell, the best break between this
+ * routine and the attach code is to simply determine whether the board
+ * is configured in properly. Currently my approach to this is to write
+ * and read a word from the SRAM on the board being probed. If the word
+ * comes back properly then we assume the board is there. The config
+ * code expects to see a successful return from the probe routine before
+ * attach will be called.
+ *
+ * input : address device is mapped to, and unit # being checked
+ * output : a '1' is returned if the board exists, and a 0 otherwise
+ *
+ */
+pc586probe(port, dev)
+struct bus_device *dev;
+{
+ caddr_t addr = (caddr_t)dev->address;
+ int unit = dev->unit;
+ int len = round_page(0x4000);
+ int sram_len = round_page(0x4000);
+ extern vm_offset_t phys_last_addr;
+ int i;
+ volatile char *b_prom;
+ volatile char *b_sram;
+ volatile u_short*t_ps;
+
+ if ((unit < 0) || (unit > NPC586)) {
+ printf("pc%d: board out of range [0..%d]\n",
+ unit, NPC586);
+ return(0);
+ }
+ if ((addr > (caddr_t)0x100000) && (addr < (caddr_t)phys_last_addr))
+ return 0;
+
+ if (kmem_alloc_pageable(kernel_map, (vm_offset_t *) &b_prom, len)
+ != KERN_SUCCESS) {
+ printf("pc%d: can not allocate memory for prom.\n", unit);
+ return 0;
+ }
+ if (kmem_alloc_pageable(kernel_map, (vm_offset_t *) &b_sram, sram_len)
+ != KERN_SUCCESS) {
+ printf("pc%d: can not allocate memory for sram.\n", unit);
+ return 0;
+ }
+ (void)pmap_map(b_prom, (vm_offset_t)addr,
+ (vm_offset_t)addr+len,
+ VM_PROT_READ | VM_PROT_WRITE);
+ if ((int)addr > 0x100000) /* stupid hardware */
+ addr += EXTENDED_ADDR;
+ addr += 0x4000; /* sram space */
+ (void)pmap_map(b_sram, (vm_offset_t)addr,
+ (vm_offset_t)addr+sram_len,
+ VM_PROT_READ | VM_PROT_WRITE);
+
+ *(b_prom + OFFSET_RESET) = 1;
+ { int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
+ *(b_prom + OFFSET_RESET) = 0;
+ t_ps = (u_short *)(b_sram + OFFSET_SCB);
+ *(t_ps) = (u_short)0x5a5a;
+ if (*(t_ps) != (u_short)0x5a5a) {
+ kmem_free(kernel_map, b_prom, len);
+ kmem_free(kernel_map, b_sram, sram_len);
+ return(0);
+ }
+ t_ps = (u_short *)(b_prom + + OFFSET_PROM);
+#define ETHER0 0x00
+#define ETHER1 0xaa
+#define ETHER2 0x00
+ if ((t_ps[0]&0xff) == ETHER0 &&
+ (t_ps[1]&0xff) == ETHER1 &&
+ (t_ps[2]&0xff) == ETHER2)
+ pc_softc[unit].seated = TRUE;
+#undef ETHER0
+#undef ETHER1
+#undef ETHER2
+#define ETHER0 0x00
+#define ETHER1 0x00
+#define ETHER2 0x1c
+ if ((t_ps[0]&0xff) == ETHER0 ||
+ (t_ps[1]&0xff) == ETHER1 ||
+ (t_ps[2]&0xff) == ETHER2)
+ pc_softc[unit].seated = TRUE;
+#undef ETHER0
+#undef ETHER1
+#undef ETHER2
+ if (pc_softc[unit].seated != TRUE) {
+ kmem_free(kernel_map, b_prom, len);
+ kmem_free(kernel_map, b_sram, sram_len);
+ return(0);
+ }
+ (volatile char *)pc_softc[unit].prom = (volatile char *)b_prom;
+ (volatile char *)pc_softc[unit].sram = (volatile char *)b_sram;
+ return(1);
+}
+
+/*
+ * pc586attach:
+ *
+ * This function attaches a PC586 board to the "system". The rest of
+ * runtime structures are initialized here (this routine is called after
+ * a successful probe of the board). Once the ethernet address is read
+ * and stored, the board's ifnet structure is attached and readied.
+ *
+ * input : bus_device structure setup in autoconfig
+ * output : board structs and ifnet is setup
+ *
+ */
+void pc586attach(dev)
+ struct bus_device *dev;
+{
+ struct ifnet *ifp;
+ u_char *addr_p;
+ u_short *b_addr;
+ u_char unit = (u_char)dev->unit;
+ pc_softc_t *sp = &pc_softc[unit];
+ volatile scb_t *scb_p;
+
+ take_dev_irq(dev);
+ printf(", port = %x, spl = %d, pic = %d. ",
+ dev->address, dev->sysdep, dev->sysdep1);
+
+ sp->timer = -1;
+ sp->flags = 0;
+ sp->mode = 0;
+ sp->open = 0;
+ CMD(RESET, CMD_1, unit);
+ { int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
+ CMD(RESET, CMD_0, unit);
+ b_addr = (u_short *)(sp->prom + OFFSET_PROM);
+ addr_p = (u_char *)sp->ds_addr;
+ addr_p[0] = b_addr[0];
+ addr_p[1] = b_addr[1];
+ addr_p[2] = b_addr[2];
+ addr_p[3] = b_addr[3];
+ addr_p[4] = b_addr[4];
+ addr_p[5] = b_addr[5];
+ printf("ethernet id [%x:%x:%x:%x:%x:%x]",
+ addr_p[0], addr_p[1], addr_p[2],
+ addr_p[3], addr_p[4], addr_p[5]);
+
+ scb_p = (volatile scb_t *)(sp->sram + OFFSET_SCB);
+ scb_p->scb_crcerrs = 0; /* initialize counters */
+ scb_p->scb_alnerrs = 0;
+ scb_p->scb_rscerrs = 0;
+ scb_p->scb_ovrnerrs = 0;
+
+ ifp = &(sp->ds_if);
+ ifp->if_unit = unit;
+ ifp->if_mtu = ETHERMTU;
+ ifp->if_flags = IFF_BROADCAST;
+#ifdef MACH_KERNEL
+ ifp->if_header_size = sizeof(struct ether_header);
+ ifp->if_header_format = HDR_ETHERNET;
+ ifp->if_address_size = 6;
+ ifp->if_address = (char *)&sp->ds_addr[0];
+ if_init_queues(ifp);
+#else MACH_KERNEL
+ ifp->if_name = "pc";
+ ifp->if_init = pc586init;
+ ifp->if_output = pc586output;
+ ifp->if_ioctl = pc586ioctl;
+ ifp->if_reset = pc586reset;
+ ifp->if_next = NULL;
+ if_attach(ifp);
+#endif MACH_KERNEL
+}
+
+/*
+ * pc586reset:
+ *
+ * This routine is in part an entry point for the "if" code. Since most
+ * of the actual initialization has already (we hope already) been done
+ * by calling pc586attach().
+ *
+ * input : unit number or board number to reset
+ * output : board is reset
+ *
+ */
+pc586reset(unit)
+int unit;
+{
+ pc_softc[unit].ds_if.if_flags &= ~IFF_RUNNING;
+ pc_softc[unit].flags &= ~(DSF_LOCK|DSF_RUNNING);
+ return(pc586init(unit));
+
+}
+
+/*
+ * pc586init:
+ *
+ * Another routine that interfaces the "if" layer to this driver.
+ * Simply resets the structures that are used by "upper layers".
+ * As well as calling pc586hwrst that does reset the pc586 board.
+ *
+ * input : board number
+ * output : structures (if structs) and board are reset
+ *
+ */
+pc586init(unit)
+int unit;
+{
+ struct ifnet *ifp;
+ int stat;
+ spl_t oldpri;
+
+ ifp = &(pc_softc[unit].ds_if);
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (ifp->if_addrlist == (struct ifaddr *)0) {
+ return;
+ }
+#endif MACH_KERNEL
+ oldpri = SPLNET();
+ if ((stat = pc586hwrst(unit)) == TRUE) {
+#ifdef MACH_KERNEL
+#undef HZ
+#define HZ hz
+#endif MACH_KERNEL
+ timeout(pc586watch, &(ifp->if_unit), 5*HZ);
+ pc_softc[unit].timer = 5;
+
+ pc_softc[unit].ds_if.if_flags |= IFF_RUNNING;
+ pc_softc[unit].flags |= DSF_RUNNING;
+ pc_softc[unit].tbusy = 0;
+ pc586start(unit);
+#if DLI
+ dli_init();
+#endif DLI
+ } else
+ printf("pc%d init(): trouble resetting board.\n", unit);
+ splx(oldpri);
+ return(stat);
+}
+
+#ifdef MACH_KERNEL
+/*ARGSUSED*/
+pc586open(dev, flag)
+ dev_t dev;
+ int flag;
+{
+ register int unit;
+ pc_softc_t *sp;
+
+ unit = minor(dev); /* XXX */
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ pc_softc[unit].ds_if.if_flags |= IFF_UP;
+ pc586init(unit);
+ return (0);
+}
+#endif MACH_KERNEL
+
+/*
+ * pc586start:
+ *
+ * This is yet another interface routine that simply tries to output a
+ * in an mbuf after a reset.
+ *
+ * input : board number
+ * output : stuff sent to board if any there
+ *
+ */
+pc586start(unit)
+int unit;
+{
+#ifdef MACH_KERNEL
+ io_req_t m;
+#else MACH_KERNEL
+ struct mbuf *m;
+#endif MACH_KERNEL
+ struct ifnet *ifp;
+ register pc_softc_t *is = &pc_softc[unit];
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+
+ if (is->tbusy) {
+ if (!(scb_p->scb_status & 0x0700)) { /* ! IDLE */
+ is->tbusy = 0;
+ pc586_cntrs[unit].xmt.busy++;
+ /*
+ * This is probably just a race. The xmt'r is just
+ * became idle but WE have masked interrupts so ...
+ */
+ if (xmt_watch) printf("!!");
+ } else
+ return;
+ }
+
+ ifp = &(pc_softc[unit].ds_if);
+ IF_DEQUEUE(&ifp->if_snd, m);
+#ifdef MACH_KERNEL
+ if (m != 0)
+#else MACH_KERNEL
+ if (m != (struct mbuf *)0)
+#endif MACH_KERNEL
+ {
+ is->tbusy++;
+ pc586_cntrs[unit].xmt.xmt++;
+ pc586xmt(unit, m);
+ }
+ return;
+}
+
+/*
+ * pc586read:
+ *
+ * This routine does the actual copy of data (including ethernet header
+ * structure) from the pc586 to an mbuf chain that will be passed up
+ * to the "if" (network interface) layer. NOTE: we currently
+ * don't handle trailer protocols, so if that is needed, it will
+ * (at least in part) be added here. For simplicities sake, this
+ * routine copies the receive buffers from the board into a local (stack)
+ * buffer until the frame has been copied from the board. Once in
+ * the local buffer, the contents are copied to an mbuf chain that
+ * is then enqueued onto the appropriate "if" queue.
+ *
+ * input : board number, and an frame descriptor pointer
+ * output : the packet is put into an mbuf chain, and passed up
+ * assumes : if any errors occur, packet is "dropped on the floor"
+ *
+ */
+pc586read(unit, fd_p)
+int unit;
+fd_t *fd_p;
+{
+ register pc_softc_t *is = &pc_softc[unit];
+ register struct ifnet *ifp = &is->ds_if;
+ struct ether_header eh;
+#ifdef MACH_KERNEL
+ ipc_kmsg_t new_kmsg;
+ struct ether_header *ehp;
+ struct packet_header *pkt;
+ char *dp;
+#else MACH_KERNEL
+ struct mbuf *m, *tm;
+#endif MACH_KERNEL
+ rbd_t *rbd_p;
+ u_char *buffer_p;
+ u_char *mb_p;
+ u_short mlen, len, clen;
+ u_short bytes_in_msg, bytes_in_mbuf, bytes;
+
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("pc%d read(): board is not running.\n", ifp->if_unit);
+ pc586intoff(ifp->if_unit);
+ }
+ pc586_cntrs[unit].rcv.rcv++;
+#ifdef MACH_KERNEL
+ new_kmsg = net_kmsg_get();
+ if (new_kmsg == IKM_NULL) {
+ /*
+ * Drop the received packet.
+ */
+ is->ds_if.if_rcvdrops++;
+
+ /*
+ * not only do we want to return, we need to drop the packet on
+ * the floor to clear the interrupt.
+ */
+ return 1;
+ }
+ ehp = (struct ether_header *) (&net_kmsg(new_kmsg)->header[0]);
+ pkt = (struct packet_header *)(&net_kmsg(new_kmsg)->packet[0]);
+
+ /*
+ * Get ether header.
+ */
+ ehp->ether_type = fd_p->length;
+ len = sizeof(struct ether_header);
+ bcopy16(fd_p->source, ehp->ether_shost, ETHER_ADD_SIZE);
+ bcopy16(fd_p->destination, ehp->ether_dhost, ETHER_ADD_SIZE);
+
+ /*
+ * Get packet body.
+ */
+ dp = (char *)(pkt + 1);
+
+ rbd_p = (rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit);
+ if (rbd_p == 0) {
+ printf("pc%d read(): Invalid buffer\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ }
+ net_kmsg_put(new_kmsg);
+ return 0;
+ }
+
+ do {
+ buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
+ bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
+ bcopy16((u_short *)buffer_p,
+ (u_short *)dp,
+ (bytes_in_msg + 1) & ~1); /* but we know it's even */
+ len += bytes_in_msg;
+ dp += bytes_in_msg;
+ if (rbd_p->status & RBD_SW_EOF)
+ break;
+ rbd_p = (rbd_t *)ram_to_ptr(rbd_p->next_rbd_offset, unit);
+ } while ((int) rbd_p);
+
+ pkt->type = ehp->ether_type;
+ pkt->length =
+ len - sizeof(struct ether_header)
+ + sizeof(struct packet_header);
+
+ /*
+ * Send the packet to the network module.
+ */
+ net_packet(ifp, new_kmsg, pkt->length, ethernet_priority(new_kmsg));
+ return 1;
+#else MACH_KERNEL
+ eh.ether_type = ntohs(fd_p->length);
+ bcopy16(fd_p->source, eh.ether_shost, ETHER_ADD_SIZE);
+ bcopy16(fd_p->destination, eh.ether_dhost, ETHER_ADD_SIZE);
+
+ if ((rbd_p =(rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit))== (rbd_t *)NULL) {
+ printf("pc%d read(): Invalid buffer\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ }
+ return 0;
+ }
+
+ bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
+ buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
+ MGET(m, M_DONTWAIT, MT_DATA);
+ tm = m;
+ if (m == (struct mbuf *)0) {
+ /*
+ * not only do we want to return, we need to drop the packet on
+ * the floor to clear the interrupt.
+ *
+ */
+ printf("pc%d read(): No mbuf 1st\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ pc586intoff(unit);
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ pc_softc[unit].timer = 0;
+ }
+ return 0;
+ }
+m->m_next = (struct mbuf *) 0;
+ m->m_len = MLEN;
+ if (bytes_in_msg > 2 * MLEN - sizeof (struct ifnet **)) {
+ MCLGET(m);
+ }
+ /*
+ * first mbuf in the packet must contain a pointer to the
+ * ifnet structure. other mbufs that follow and make up
+ * the packet do not need this pointer in the mbuf.
+ *
+ */
+ *(mtod(tm, struct ifnet **)) = ifp;
+ mlen = sizeof (struct ifnet **);
+ clen = mlen;
+ bytes_in_mbuf = m->m_len - sizeof(struct ifnet **);
+ mb_p = mtod(tm, u_char *) + sizeof (struct ifnet **);
+ bytes = min(bytes_in_mbuf, bytes_in_msg);
+ do {
+ if (bytes & 1)
+ len = bytes + 1;
+ else
+ len = bytes;
+ bcopy16(buffer_p, mb_p, len);
+ clen += bytes;
+ mlen += bytes;
+
+ if (!(bytes_in_mbuf -= bytes)) {
+ MGET(tm->m_next, M_DONTWAIT, MT_DATA);
+ tm = tm->m_next;
+ if (tm == (struct mbuf *)0) {
+ m_freem(m);
+ printf("pc%d read(): No mbuf nth\n", unit);
+ if (pc586hwrst(unit) != TRUE) {
+ pc586intoff(unit);
+ printf("pc%d read(): hwrst trouble.\n", unit);
+ pc_softc[unit].timer = 0;
+ }
+ return 0;
+ }
+ mlen = 0;
+ tm->m_len = MLEN;
+ bytes_in_mbuf = MLEN;
+ mb_p = mtod(tm, u_char *);
+ } else
+ mb_p += bytes;
+
+ if (!(bytes_in_msg -= bytes)) {
+ if (rbd_p->status & RBD_SW_EOF ||
+ (rbd_p = (rbd_t *)ram_to_ptr(rbd_p->next_rbd_offset, unit)) ==
+ NULL) {
+ tm->m_len = mlen;
+ break;
+ } else {
+ bytes_in_msg = rbd_p->status & RBD_SW_COUNT;
+ buffer_p = (u_char *)(pc_softc[unit].sram + rbd_p->buffer_addr);
+ }
+ } else
+ buffer_p += bytes;
+
+ bytes = min(bytes_in_mbuf, bytes_in_msg);
+ } while(1);
+#ifdef IF_CNTRS
+/* clen -= (sizeof (struct ifnet **)
+ clen += 4 /* crc */;
+ clen += sizeof (struct ether_header);
+ pc586_ein[log_2(clen)]++;
+ if (clen < 128) pc586_lin[clen>>3]++;
+
+ if (eh.ether_type == ETHERTYPE_ARP) {
+ pc586_arp++;
+ if (pc586_narp) {
+ pc586_ein[log_2(clen)]--;
+ if (clen < 128) pc586_lin[clen>>3]--;
+ }
+ }
+#endif IF_CNTRS
+ /*
+ * received packet is now in a chain of mbuf's. next step is
+ * to pass the packet upwards.
+ *
+ */
+ pc586send_packet_up(m, &eh, is);
+ return 1;
+#endif MACH_KERNEL
+}
+
+/*
+ * Send a packet composed of an mbuf chain to the higher levels
+ *
+ */
+#ifndef MACH_KERNEL
+pc586send_packet_up(m, eh, is)
+struct mbuf *m;
+struct ether_header *eh;
+pc_softc_t *is;
+{
+ register struct ifqueue *inq;
+ spl_t opri;
+
+ switch (eh->ether_type) {
+#ifdef INET
+ case ETHERTYPE_IP:
+ schednetisr(NETISR_IP);
+ inq = &ipintrq;
+ break;
+ case ETHERTYPE_ARP:
+ arpinput(&is->pc586_ac, m);
+ return;
+#endif
+#ifdef NS
+ case ETHERTYPE_NS:
+ schednetisr(NETISR_NS);
+ inq = &nsintrq;
+ break;
+#endif
+ default:
+#if DLI
+ {
+ eh.ether_type = htons(eh.ether_type);
+ dli_input(m,eh.ether_type,&eh.ether_shost[0],
+ &de_dlv[ds->ds_if.if_unit], &eh);
+ }
+#else DLI
+ m_freem(m);
+#endif DLI
+ return;
+ }
+ opri = SPLNET();
+ if (IF_QFULL(inq)) {
+ IF_DROP(inq);
+ splx(opri);
+ m_freem(m);
+ return;
+ }
+ IF_ENQUEUE(inq, m);
+ splx(opri);
+ return;
+}
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+pc586output(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ register int unit;
+
+ unit = minor(dev); /* XXX */
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ return (net_write(&pc_softc[unit].ds_if, pc586start, ior));
+}
+
+pc586setinput(dev, receive_port, priority, filter, filter_count)
+ dev_t dev;
+ mach_port_t receive_port;
+ int priority;
+ filter_t filter[];
+ unsigned int filter_count;
+{
+ register int unit = minor(dev);
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ return (net_set_filter(&pc_softc[unit].ds_if,
+ receive_port, priority,
+ filter, filter_count));
+}
+#else MACH_KERNEL
+/*
+ * pc586output:
+ *
+ * This routine is called by the "if" layer to output a packet to
+ * the network. This code resolves the local ethernet address, and
+ * puts it into the mbuf if there is room. If not, then a new mbuf
+ * is allocated with the header information and precedes the data
+ * to be transmitted. The routines that actually transmit the
+ * data (pc586xmt()) expect the ethernet structure to precede
+ * the data in the mbuf. This information is required by the
+ * 82586's transfer command segment, and thus mbuf's cannot
+ * be simply "slammed" out onto the network.
+ *
+ * input: ifnet structure pointer, an mbuf with data, and address
+ * to be resolved
+ * output: mbuf is updated to hold enet address, or a new mbuf
+ * with the address is added
+ *
+ */
+pc586output(ifp, m0, dst)
+struct ifnet *ifp;
+struct mbuf *m0;
+struct sockaddr *dst;
+{
+ register pc_softc_t *is = &pc_softc[ifp->if_unit];
+ register struct mbuf *m = m0;
+ int type, error;
+ spl_t opri;
+ u_char edst[6];
+ struct in_addr idst;
+ register struct ether_header *eh;
+ register int off;
+ int usetrailers;
+
+ if ((ifp->if_flags & (IFF_UP|IFF_RUNNING)) != (IFF_UP|IFF_RUNNING)) {
+ printf("pc%d output(): board is not running.\n", ifp->if_unit);
+ pc586intoff(ifp->if_unit);
+ error = ENETDOWN;
+ goto bad;
+ }
+ switch (dst->sa_family) {
+
+#ifdef INET
+ case AF_INET:
+ idst = ((struct sockaddr_in *)dst)->sin_addr;
+ if (!arpresolve(&is->pc586_ac, m, &idst, edst, &usetrailers)){
+ return (0); /* if not yet resolved */
+ }
+ off = ntohs((u_short)mtod(m, struct ip *)->ip_len) - m->m_len;
+
+ if (usetrailers && off > 0 && (off & 0x1ff) == 0 &&
+ m->m_off >= MMINOFF + 2 * sizeof (u_short)) {
+ type = ETHERTYPE_TRAIL + (off>>9);
+ m->m_off -= 2 * sizeof (u_short);
+ m->m_len += 2 * sizeof (u_short);
+ *mtod(m, u_short *) = htons((u_short)ETHERTYPE_IP);
+ *(mtod(m, u_short *) + 1) = htons((u_short)m->m_len);
+ goto gottrailertype;
+ }
+ type = ETHERTYPE_IP;
+ off = 0;
+ goto gottype;
+#endif
+#ifdef NS
+ case AF_NS:
+ type = ETHERTYPE_NS;
+ bcopy((caddr_t)&(((struct sockaddr_ns *)dst)->sns_addr.x_host),
+ (caddr_t)edst, sizeof (edst));
+ off = 0;
+ goto gottype;
+#endif
+
+#if DLI
+ case AF_DLI:
+ if (m->m_len < sizeof(struct ether_header))
+ {
+ error = EMSGSIZE;
+ goto bad;
+ }
+ eh = mtod(m, struct ether_header *);
+ bcopy(dst->sa_data, (caddr_t)eh->ether_dhost,
+ sizeof (eh->ether_dhost));
+ goto gotheader;
+#endif DLI
+
+ case AF_UNSPEC:
+ eh = (struct ether_header *)dst->sa_data;
+ bcopy((caddr_t)eh->ether_dhost, (caddr_t)edst, sizeof (edst));
+ type = eh->ether_type;
+ goto gottype;
+
+ default:
+ printf("pc%d output(): can't handle af%d\n",
+ ifp->if_unit, dst->sa_family);
+ error = EAFNOSUPPORT;
+ goto bad;
+ }
+
+gottrailertype:
+ /*
+ * Packet to be sent as trailer: move first packet
+ * (control information) to end of chain.
+ */
+ while (m->m_next)
+ m = m->m_next;
+ m->m_next = m0;
+ m = m0->m_next;
+ m0->m_next = 0;
+ m0 = m;
+
+gottype:
+ /*
+ * Add local net header. If no space in first mbuf,
+ * allocate another.
+ */
+ if (m->m_off > MMAXOFF ||
+ MMINOFF + sizeof (struct ether_header) > m->m_off) {
+ m = m_get(M_DONTWAIT, MT_HEADER);
+ if (m == 0) {
+ error = ENOBUFS;
+ goto bad;
+ }
+ m->m_next = m0;
+ m->m_off = MMINOFF;
+ m->m_len = sizeof (struct ether_header);
+ } else {
+ m->m_off -= sizeof (struct ether_header);
+ m->m_len += sizeof (struct ether_header);
+ }
+ eh = mtod(m, struct ether_header *);
+ eh->ether_type = htons((u_short)type);
+ bcopy((caddr_t)edst, (caddr_t)eh->ether_dhost, sizeof (edst));
+ bcopy((caddr_t)is->ds_addr,(caddr_t)eh->ether_shost, sizeof(edst));
+#if DLI
+gotheader:
+#endif DLI
+
+ /*
+ * Queue message on interface, and start output if interface
+ * not yet active.
+ */
+ opri = SPLNET();
+ if (IF_QFULL(&ifp->if_snd)) {
+ IF_DROP(&ifp->if_snd);
+ splx(opri);
+ m_freem(m);
+ return (ENOBUFS);
+ }
+ IF_ENQUEUE(&ifp->if_snd, m);
+ /*
+ * Some action needs to be added here for checking whether the
+ * board is already transmitting. If it is, we don't want to
+ * start it up (ie call pc586start()). We will attempt to send
+ * packets that are queued up after an interrupt occurs. Some
+ * flag checking action has to happen here and/or in the start
+ * routine. This note is here to remind me that some thought
+ * is needed and there is a potential problem here.
+ *
+ */
+ pc586start(ifp->if_unit);
+ splx(opri);
+ return (0);
+bad:
+ m_freem(m0);
+ return (error);
+}
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+pc586getstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status; /* pointer to OUT array */
+ unsigned int *count; /* out */
+{
+ register int unit = minor(dev);
+ register pc_softc_t *sp;
+
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ sp = &pc_softc[unit];
+ return (net_getstat(&sp->ds_if, flavor, status, count));
+}
+
+pc586setstat(dev, flavor, status, count)
+ dev_t dev;
+ int flavor;
+ dev_status_t status;
+ unsigned int count;
+{
+ register int unit = minor(dev);
+ register pc_softc_t *sp;
+
+ if (unit < 0 || unit >= NPC586 || !pc_softc[unit].seated)
+ return (ENXIO);
+
+ sp = &pc_softc[unit];
+
+ switch (flavor) {
+ case NET_STATUS:
+ {
+ /*
+ * All we can change are flags, and not many of those.
+ */
+ register struct net_status *ns = (struct net_status *)status;
+ int mode = 0;
+
+ if (count < NET_STATUS_COUNT)
+ return (D_INVALID_OPERATION);
+
+ if (ns->flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ns->flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+
+ /*
+ * Force a complete reset if the receive mode changes
+ * so that these take effect immediately.
+ */
+ if (sp->mode != mode) {
+ sp->mode = mode;
+ if (sp->flags & DSF_RUNNING) {
+ sp->flags &= ~(DSF_LOCK|DSF_RUNNING);
+ pc586init(unit);
+ }
+ }
+ break;
+ }
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+
+}
+#else MACH_KERNEL
+/*
+ * pc586ioctl:
+ *
+ * This routine processes an ioctl request from the "if" layer
+ * above.
+ *
+ * input : pointer the appropriate "if" struct, command, and data
+ * output : based on command appropriate action is taken on the
+ * pc586 board(s) or related structures
+ * return : error is returned containing exit conditions
+ *
+ */
+pc586ioctl(ifp, cmd, data)
+struct ifnet *ifp;
+int cmd;
+caddr_t data;
+{
+ register struct ifaddr *ifa = (struct ifaddr *)data;
+ int unit = ifp->if_unit;
+ register pc_softc_t *is = &pc_softc[unit];
+ short mode = 0;
+ int error = 0;
+ spl_t opri;
+
+ opri = SPLNET();
+ switch (cmd) {
+ case SIOCSIFADDR:
+ ifp->if_flags |= IFF_UP;
+ pc586init(unit);
+ switch (ifa->ifa_addr.sa_family) {
+#ifdef INET
+ case AF_INET:
+ ((struct arpcom *)ifp)->ac_ipaddr = IA_SIN(ifa)->sin_addr;
+ arpwhohas((struct arpcom *)ifp, &IA_SIN(ifa)->sin_addr);
+ break;
+#endif
+#ifdef NS
+ case AF_NS:
+ {
+ register struct ns_addr *ina =
+ &(IA_SNS(ifa)->sns_addr);
+ if (ns_nullhost(*ina))
+ ina->x_host = *(union ns_host *)(ds->ds_addr);
+ else
+ pc586setaddr(ina->x_host.c_host, unit);
+ break;
+ }
+#endif
+ }
+ break;
+ case SIOCSIFFLAGS:
+ if (ifp->if_flags & IFF_ALLMULTI)
+ mode |= MOD_ENAL;
+ if (ifp->if_flags & IFF_PROMISC)
+ mode |= MOD_PROM;
+ /*
+ * force a complete reset if the receive multicast/
+ * promiscuous mode changes so that these take
+ * effect immediately.
+ *
+ */
+ if (is->mode != mode) {
+ is->mode = mode;
+ if (is->flags & DSF_RUNNING) {
+ is->flags &= ~(DSF_LOCK|DSF_RUNNING);
+ pc586init(unit);
+ }
+ }
+ if ((ifp->if_flags & IFF_UP) == 0 && is->flags & DSF_RUNNING) {
+ printf("pc%d ioctl(): board is not running\n", unit);
+ is->flags &= ~(DSF_LOCK | DSF_RUNNING);
+ is->timer = -1;
+ pc586intoff(unit);
+ } else if (ifp->if_flags & IFF_UP && (is->flags & DSF_RUNNING) == 0) {
+ pc586init(unit);
+ }
+ break;
+#ifdef IF_CNTRS
+ case SIOCCIFCNTRS:
+ if (!suser()) {
+ error = EPERM;
+ break;
+ }
+ bzero((caddr_t)pc586_ein, sizeof (pc586_ein));
+ bzero((caddr_t)pc586_eout, sizeof (pc586_eout));
+ bzero((caddr_t)pc586_lin, sizeof (pc586_lin));
+ bzero((caddr_t)pc586_lout, sizeof (pc586_lout));
+ bzero((caddr_t)&pc586_arp, sizeof (int));
+ bzero((caddr_t)&pc586_cntrs, sizeof (pc586_cntrs));
+ break;
+#endif IF_CNTRS
+ default:
+ error = EINVAL;
+ }
+ splx(opri);
+ return (error);
+}
+#endif MACH_KERNEL
+
+/*
+ * pc586hwrst:
+ *
+ * This routine resets the pc586 board that corresponds to the
+ * board number passed in.
+ *
+ * input : board number to do a hardware reset
+ * output : board is reset
+ *
+ */
+pc586hwrst(unit)
+int unit;
+{
+ CMD(CHANATT, CMD_0, unit);
+ CMD(RESET, CMD_1, unit);
+ { int i; for (i = 0; i < 1000; i++); /* 4 clocks at 6Mhz */}
+ CMD(RESET,CMD_0, unit);
+
+/*
+ * for (i = 0; i < 1000000; i++);
+ * with this loop above and with the reset toggle also looping to
+ * 1000000. We don't see the reset behaving as advertised. DOES
+ * IT HAPPEN AT ALL. In particular, NORMODE, ENABLE, and XFER
+ * should all be zero and they have not changed at all.
+ */
+ CMD(INTENAB, CMD_0, unit);
+ CMD(NORMMODE, CMD_0, unit);
+ CMD(XFERMODE, CMD_1, unit);
+
+ pc586bldcu(unit);
+
+ if (pc586diag(unit) == FALSE)
+ return(FALSE);
+
+ if (pc586config(unit) == FALSE)
+ return(FALSE);
+ /*
+ * insert code for loopback test here
+ *
+ */
+ pc586rustrt(unit);
+
+ pc586inton(unit);
+ CMD(NORMMODE, CMD_1, unit);
+ return(TRUE);
+}
+
+/*
+ * pc586watch():
+ *
+ * This routine is the watchdog timer routine for the pc586 chip. If
+ * chip wedges, this routine will fire and cause a board reset and
+ * begin again.
+ *
+ * input : which board is timing out
+ * output : potential board reset if wedged
+ *
+ */
+int watch_dead = 0;
+pc586watch(b_ptr)
+caddr_t b_ptr;
+{
+ spl_t opri;
+ int unit = *b_ptr;
+
+ if ((pc_softc[unit].ds_if.if_flags & IFF_UP) == 0) {
+ return;
+ }
+ if (pc_softc[unit].timer == -1) {
+ timeout(pc586watch, b_ptr, 5*HZ);
+ return;
+ }
+ if (--pc_softc[unit].timer != -1) {
+ timeout(pc586watch, b_ptr, 1*HZ);
+ return;
+ }
+
+ opri = SPLNET();
+#ifdef notdef
+ printf("pc%d watch(): 6sec timeout no %d\n", unit, ++watch_dead);
+#endif notdef
+ pc586_cntrs[unit].watch++;
+ if (pc586hwrst(unit) != TRUE) {
+ printf("pc%d watch(): hwrst trouble.\n", unit);
+ pc_softc[unit].timer = 0;
+ } else {
+ timeout(pc586watch, b_ptr, 1*HZ);
+ pc_softc[unit].timer = 5;
+ }
+ splx(opri);
+}
+
+/*
+ * pc586intr:
+ *
+ * This function is the interrupt handler for the pc586 ethernet
+ * board. This routine will be called whenever either a packet
+ * is received, or a packet has successfully been transfered and
+ * the unit is ready to transmit another packet.
+ *
+ * input : board number that interrupted
+ * output : either a packet is received, or a packet is transfered
+ *
+ */
+pc586intr(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
+ int next, x;
+ int i;
+ u_short int_type;
+
+ if (pc_softc[unit].seated == FALSE) {
+ printf("pc%d intr(): board not seated\n", unit);
+ return(-1);
+ }
+
+ while ((int_type = (scb_p->scb_status & SCB_SW_INT)) != 0) {
+ pc586ack(unit);
+ if (int_type & SCB_SW_FR) {
+ pc586rcv(unit);
+ watch_dead=0;
+ }
+ if (int_type & SCB_SW_RNR) {
+ pc586_cntrs[unit].rcv.ovw++;
+#ifdef notdef
+ printf("pc%d intr(): receiver overrun! begin_fd = %x\n",
+ unit, pc_softc[unit].begin_fd);
+#endif notdef
+ pc586rustrt(unit);
+ }
+ if (int_type & SCB_SW_CNA) {
+ /*
+ * At present, we don't care about CNA's. We
+ * believe they are a side effect of XMT.
+ */
+ }
+ if (int_type & SCB_SW_CX) {
+ /*
+ * At present, we only request Interrupt for
+ * XMT.
+ */
+ if ((!(cb_p->ac_status & AC_SW_OK)) ||
+ (cb_p->ac_status & (0xfff^TC_SQE))) {
+ if (cb_p->ac_status & TC_DEFER) {
+ if (xmt_watch) printf("DF");
+ pc586_cntrs[unit].xmt.defer++;
+ } else if (cb_p->ac_status & (TC_COLLISION|0xf)) {
+ if (xmt_watch) printf("%x",cb_p->ac_status & 0xf);
+ } else if (xmt_watch)
+ printf("pc%d XMT: %x %x\n",
+ unit, cb_p->ac_status, cb_p->ac_command);
+ }
+ pc586_cntrs[unit].xmt.xmti++;
+ pc_softc[unit].tbusy = 0;
+ pc586start(unit);
+ }
+ pc_softc[unit].timer = 5;
+ }
+ return(0);
+}
+
+/*
+ * pc586rcv:
+ *
+ * This routine is called by the interrupt handler to initiate a
+ * packet transfer from the board to the "if" layer above this
+ * driver. This routine checks if a buffer has been successfully
+ * received by the pc586. If so, the routine pc586read is called
+ * to do the actual transfer of the board data (including the
+ * ethernet header) into a packet (consisting of an mbuf chain).
+ *
+ * input : number of the board to check
+ * output : if a packet is available, it is "sent up"
+ *
+ */
+pc586rcv(unit)
+int unit;
+{
+ fd_t *fd_p;
+
+ for (fd_p = pc_softc[unit].begin_fd; fd_p != (fd_t *)NULL;
+ fd_p = pc_softc[unit].begin_fd) {
+ if (fd_p->status == 0xffff || fd_p->rbd_offset == 0xffff) {
+ if (pc586hwrst(unit) != TRUE)
+ printf("pc%d rcv(): hwrst ffff trouble.\n",
+ unit);
+ return;
+ } else if (fd_p->status & AC_SW_C) {
+ fd_t *bfd = (fd_t *)ram_to_ptr(fd_p->link_offset, unit);
+
+ if (fd_p->status == (RFD_DONE|RFD_RSC)) {
+ /* lost one */;
+#ifdef notdef
+ printf("pc%d RCV: RSC %x\n",
+ unit, fd_p->status);
+#endif notdef
+ pc586_cntrs[unit].rcv.partial++;
+ } else if (!(fd_p->status & RFD_OK))
+ printf("pc%d RCV: !OK %x\n",
+ unit, fd_p->status);
+ else if (fd_p->status & 0xfff)
+ printf("pc%d RCV: ERRs %x\n",
+ unit, fd_p->status);
+ else
+ if (!pc586read(unit, fd_p))
+ return;
+ if (!pc586requeue(unit, fd_p)) { /* abort on chain error */
+ if (pc586hwrst(unit) != TRUE)
+ printf("pc%d rcv(): hwrst trouble.\n", unit);
+ return;
+ }
+ pc_softc[unit].begin_fd = bfd;
+ } else
+ break;
+ }
+ return;
+}
+
+/*
+ * pc586requeue:
+ *
+ * This routine puts rbd's used in the last receive back onto the
+ * free list for the next receive.
+ *
+ */
+pc586requeue(unit, fd_p)
+int unit;
+fd_t *fd_p;
+{
+ rbd_t *l_rbdp;
+ rbd_t *f_rbdp;
+
+#ifndef REQUEUE_DBG
+ if (bad_rbd_chain(fd_p->rbd_offset, unit))
+ return 0;
+#endif REQUEUE_DBG
+ f_rbdp = (rbd_t *)ram_to_ptr(fd_p->rbd_offset, unit);
+ if (f_rbdp != NULL) {
+ l_rbdp = f_rbdp;
+ while ( (!(l_rbdp->status & RBD_SW_EOF)) &&
+ (l_rbdp->next_rbd_offset != 0xffff))
+ {
+ l_rbdp->status = 0;
+ l_rbdp = (rbd_t *)ram_to_ptr(l_rbdp->next_rbd_offset,
+ unit);
+ }
+ l_rbdp->next_rbd_offset = PC586NULL;
+ l_rbdp->status = 0;
+ l_rbdp->size |= AC_CW_EL;
+ pc_softc[unit].end_rbd->next_rbd_offset =
+ ptr_to_ram((char *)f_rbdp, unit);
+ pc_softc[unit].end_rbd->size &= ~AC_CW_EL;
+ pc_softc[unit].end_rbd= l_rbdp;
+ }
+
+ fd_p->status = 0;
+ fd_p->command = AC_CW_EL;
+ fd_p->link_offset = PC586NULL;
+ fd_p->rbd_offset = PC586NULL;
+
+ pc_softc[unit].end_fd->link_offset = ptr_to_ram((char *)fd_p, unit);
+ pc_softc[unit].end_fd->command = 0;
+ pc_softc[unit].end_fd = fd_p;
+
+ return 1;
+}
+
+/*
+ * pc586xmt:
+ *
+ * This routine fills in the appropriate registers and memory
+ * locations on the PC586 board and starts the board off on
+ * the transmit.
+ *
+ * input : board number of interest, and a pointer to the mbuf
+ * output : board memory and registers are set for xfer and attention
+ *
+ */
+#ifdef DEBUG
+int xmt_debug = 0;
+#endif DEBUG
+pc586xmt(unit, m)
+int unit;
+#ifdef MACH_KERNEL
+io_req_t m;
+#else MACH_KERNEL
+struct mbuf *m;
+#endif MACH_KERNEL
+{
+ pc_softc_t *is = &pc_softc[unit];
+ register u_char *xmtdata_p = (u_char *)(is->sram + OFFSET_TBUF);
+ register u_short *xmtshort_p;
+#ifdef MACH_KERNEL
+ register struct ether_header *eh_p = (struct ether_header *)m->io_data;
+#else MACH_KERNEL
+ struct mbuf *tm_p = m;
+ register struct ether_header *eh_p = mtod(m, struct ether_header *);
+ u_char *mb_p = mtod(m, u_char *) + sizeof(struct ether_header);
+ u_short count = m->m_len - sizeof(struct ether_header);
+#endif MACH_KERNEL
+ volatile scb_t *scb_p = (volatile scb_t *)(is->sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(is->sram + OFFSET_CU);
+ tbd_t *tbd_p = (tbd_t *)(is->sram + OFFSET_TBD);
+ u_short tbd = OFFSET_TBD;
+ u_short len, clen = 0;
+
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_CW_EL|AC_TRANSMIT|AC_CW_I);
+ cb_p->ac_link_offset = PC586NULL;
+ cb_p->cmd.transmit.tbd_offset = OFFSET_TBD;
+
+ bcopy16(eh_p->ether_dhost, cb_p->cmd.transmit.dest_addr, ETHER_ADD_SIZE);
+ cb_p->cmd.transmit.length = (u_short)(eh_p->ether_type);
+
+#ifndef MACH_KERNEL
+#ifdef DEBUG
+ if (xmt_debug)
+ printf("XMT mbuf: L%d @%x ", count, mb_p);
+#endif DEBUG
+#endif MACH_KERNEL
+ tbd_p->act_count = 0;
+ tbd_p->buffer_base = 0;
+ tbd_p->buffer_addr = ptr_to_ram(xmtdata_p, unit);
+#ifdef MACH_KERNEL
+ { int Rlen, Llen;
+ clen = m->io_count - sizeof(struct ether_header);
+ Llen = clen & 1;
+ Rlen = ((int)(m->io_data + sizeof(struct ether_header))) & 1;
+
+ bcopy16(m->io_data + sizeof(struct ether_header) - Rlen,
+ xmtdata_p,
+ clen + (Rlen + Llen) );
+ xmtdata_p += clen + Llen;
+ tbd_p->act_count = clen;
+ tbd_p->buffer_addr += Rlen;
+ }
+#else MACH_KERNEL
+ do {
+ if (count) {
+ if (clen + count > ETHERMTU)
+ break;
+ if (count & 1)
+ len = count + 1;
+ else
+ len = count;
+ bcopy16(mb_p, xmtdata_p, len);
+ clen += count;
+ tbd_p->act_count += count;
+ xmtdata_p += len;
+ if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
+ break;
+ if (count & 1) {
+ /* go to the next descriptor */
+ tbd_p++->next_tbd_offset = (tbd += sizeof (tbd_t));
+ tbd_p->act_count = 0;
+ tbd_p->buffer_base = 0;
+ tbd_p->buffer_addr = ptr_to_ram(xmtdata_p, unit);
+ /* at the end -> coallesce remaining mbufs */
+ if (tbd == OFFSET_TBD + (N_TBD-1) * sizeof (tbd_t)) {
+ pc586sftwsleaze(&count, &mb_p, &tm_p, unit);
+ continue;
+ }
+ /* next mbuf short -> coallesce as needed */
+ if ( (tm_p->m_next == (struct mbuf *) 0) ||
+#define HDW_THRESHOLD 55
+ tm_p->m_len > HDW_THRESHOLD)
+ /* ok */;
+ else {
+ pc586hdwsleaze(&count, &mb_p, &tm_p, unit);
+ continue;
+ }
+ }
+ } else if ((tm_p = tm_p->m_next) == (struct mbuf *)0)
+ break;
+ count = tm_p->m_len;
+ mb_p = mtod(tm_p, u_char *);
+#ifdef DEBUG
+ if (xmt_debug)
+ printf("mbuf+ L%d @%x ", count, mb_p);
+#endif DEBUG
+ } while (1);
+#endif MACH_KERNEL
+#ifdef DEBUG
+ if (xmt_debug)
+ printf("CLEN = %d\n", clen);
+#endif DEBUG
+ if (clen < ETHERMIN) {
+ tbd_p->act_count += ETHERMIN - clen;
+ for (xmtshort_p = (u_short *)xmtdata_p;
+ clen < ETHERMIN;
+ clen += 2) *xmtshort_p++ = 0;
+ }
+ tbd_p->act_count |= TBD_SW_EOF;
+ tbd_p->next_tbd_offset = PC586NULL;
+#ifdef IF_CNTRS
+ clen += sizeof (struct ether_header) + 4 /* crc */;
+ pc586_eout[log_2(clen)]++;
+ if (clen < 128) pc586_lout[clen>>3]++;
+#endif IF_CNTRS
+#ifdef DEBUG
+ if (xmt_debug) {
+ pc586tbd(unit);
+ printf("\n");
+ }
+#endif DEBUG
+
+ while (scb_p->scb_command) ;
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+#ifdef MACH_KERNEL
+ iodone(m);
+#else MACH_KERNEL
+ for (count=0; ((count < 6) && (eh_p->ether_dhost[count] == 0xff)); count++) ;
+ if (count == 6) {
+ pc586send_packet_up(m, eh_p, is);
+ } else
+ m_freem(m);
+#endif MACH_KERNEL
+ return;
+}
+
+/*
+ * pc586bldcu:
+ *
+ * This function builds up the command unit structures. It inits
+ * the scp, iscp, scb, cb, tbd, and tbuf.
+ *
+ */
+pc586bldcu(unit)
+{
+ char *sram = pc_softc[unit].sram;
+ scp_t *scp_p = (scp_t *)(sram + OFFSET_SCP);
+ iscp_t *iscp_p = (iscp_t *)(sram + OFFSET_ISCP);
+ volatile scb_t *scb_p = (volatile scb_t *)(sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(sram + OFFSET_CU);
+ tbd_t *tbd_p = (tbd_t *)(sram + OFFSET_TBD);
+ int i;
+
+ scp_p->scp_sysbus = 0;
+ scp_p->scp_iscp = OFFSET_ISCP;
+ scp_p->scp_iscp_base = 0;
+
+ iscp_p->iscp_busy = 1;
+ iscp_p->iscp_scb_offset = OFFSET_SCB;
+ iscp_p->iscp_scb = 0;
+ iscp_p->iscp_scb_base = 0;
+
+ pc586_cntrs[unit].rcv.crc += scb_p->scb_crcerrs;
+ pc586_cntrs[unit].rcv.frame += scb_p->scb_alnerrs;
+ pc586_cntrs[unit].rcv.rscerrs += scb_p->scb_rscerrs;
+ pc586_cntrs[unit].rcv.ovrnerrs += scb_p->scb_ovrnerrs;
+ scb_p->scb_status = 0;
+ scb_p->scb_command = 0;
+ scb_p->scb_cbl_offset = OFFSET_CU;
+ scb_p->scb_rfa_offset = OFFSET_RU;
+ scb_p->scb_crcerrs = 0;
+ scb_p->scb_alnerrs = 0;
+ scb_p->scb_rscerrs = 0;
+ scb_p->scb_ovrnerrs = 0;
+
+ scb_p->scb_command = SCB_RESET;
+ pc586chatt(unit);
+ for (i = 1000000; iscp_p->iscp_busy && (i-- > 0); );
+ if (!i) printf("pc%d bldcu(): iscp_busy timeout.\n", unit);
+ for (i = STATUS_TRIES; i-- > 0; ) {
+ if (scb_p->scb_status == (SCB_SW_CX|SCB_SW_CNA))
+ break;
+ }
+ if (!i)
+ printf("pc%d bldcu(): not ready after reset.\n", unit);
+ pc586ack(unit);
+
+ cb_p->ac_status = 0;
+ cb_p->ac_command = AC_CW_EL;
+ cb_p->ac_link_offset = OFFSET_CU;
+
+ tbd_p->act_count = 0;
+ tbd_p->next_tbd_offset = PC586NULL;
+ tbd_p->buffer_addr = 0;
+ tbd_p->buffer_base = 0;
+ return;
+}
+
+/*
+ * pc586bldru:
+ *
+ * This function builds the linear linked lists of fd's and
+ * rbd's. Based on page 4-32 of 1986 Intel microcom handbook.
+ *
+ */
+char *
+pc586bldru(unit)
+int unit;
+{
+ fd_t *fd_p = (fd_t *)(pc_softc[unit].sram + OFFSET_RU);
+ ru_t *rbd_p = (ru_t *)(pc_softc[unit].sram + OFFSET_RBD);
+ int i;
+
+ pc_softc[unit].begin_fd = fd_p;
+ for(i = 0; i < N_FD; i++, fd_p++) {
+ fd_p->status = 0;
+ fd_p->command = 0;
+ fd_p->link_offset = ptr_to_ram((char *)(fd_p + 1), unit);
+ fd_p->rbd_offset = PC586NULL;
+ }
+ pc_softc[unit].end_fd = --fd_p;
+ fd_p->link_offset = PC586NULL;
+ fd_p->command = AC_CW_EL;
+ fd_p = (fd_t *)(pc_softc[unit].sram + OFFSET_RU);
+
+ fd_p->rbd_offset = ptr_to_ram((char *)rbd_p, unit);
+ for(i = 0; i < N_RBD; i++, rbd_p = (ru_t *) &(rbd_p->rbuffer[RCVBUFSIZE])) {
+ rbd_p->r.status = 0;
+ rbd_p->r.buffer_addr = ptr_to_ram((char *)(rbd_p->rbuffer),
+ unit);
+ rbd_p->r.buffer_base = 0;
+ rbd_p->r.size = RCVBUFSIZE;
+ if (i != N_RBD-1) {
+ rbd_p->r.next_rbd_offset=ptr_to_ram(&(rbd_p->rbuffer[RCVBUFSIZE]),
+ unit);
+ } else {
+ rbd_p->r.next_rbd_offset = PC586NULL;
+ rbd_p->r.size |= AC_CW_EL;
+ pc_softc[unit].end_rbd = (rbd_t *)rbd_p;
+ }
+ }
+ return (char *)pc_softc[unit].begin_fd;
+}
+
+/*
+ * pc586rustrt:
+ *
+ * This routine starts the receive unit running. First checks if the
+ * board is actually ready, then the board is instructed to receive
+ * packets again.
+ *
+ */
+pc586rustrt(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ char *strt;
+
+ if ((scb_p->scb_status & SCB_RUS_READY) == SCB_RUS_READY)
+ return;
+
+ strt = pc586bldru(unit);
+ scb_p->scb_command = SCB_RU_STRT;
+ scb_p->scb_rfa_offset = ptr_to_ram(strt, unit);
+ pc586chatt(unit);
+ return;
+}
+
+/*
+ * pc586diag:
+ *
+ * This routine does a 586 op-code number 7, and obtains the
+ * diagnose status for the pc586.
+ *
+ */
+pc586diag(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
+ int i;
+
+ if (scb_p->scb_status & SCB_SW_INT) {
+ printf("pc%d diag(): bad initial state %\n",
+ unit, scb_p->scb_status);
+ pc586ack(unit);
+ }
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_DIAGNOSE|AC_CW_EL);
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+ for(i = 0; i < 0xffff; i++)
+ if ((cb_p->ac_status & AC_SW_C))
+ break;
+ if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
+ printf("pc%d: diag failed; status = %x\n",
+ unit, cb_p->ac_status);
+ return(FALSE);
+ }
+
+ if ( (scb_p->scb_status & SCB_SW_INT) && (scb_p->scb_status != SCB_SW_CNA) ) {
+ printf("pc%d diag(): bad final state %x\n",
+ unit, scb_p->scb_status);
+ pc586ack(unit);
+ }
+ return(TRUE);
+}
+
+/*
+ * pc586config:
+ *
+ * This routine does a standard config of the pc586 board.
+ *
+ */
+pc586config(unit)
+int unit;
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile ac_t *cb_p = (volatile ac_t *)(pc_softc[unit].sram + OFFSET_CU);
+ int i;
+
+
+/*
+ if ((scb_p->scb_status != SCB_SW_CNA) && (scb_p->scb_status & SCB_SW_INT) ) {
+ printf("pc%d config(): unexpected initial state %x\n",
+ unit, scb_p->scb_status);
+ }
+*/
+ pc586ack(unit);
+
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_CONFIGURE|AC_CW_EL);
+
+ /*
+ * below is the default board configuration from p2-28 from 586 book
+ */
+ cb_p->cmd.configure.fifolim_bytecnt = 0x080c;
+ cb_p->cmd.configure.addrlen_mode = 0x2600;
+ cb_p->cmd.configure.linprio_interframe = 0x6000;
+ cb_p->cmd.configure.slot_time = 0xf200;
+ cb_p->cmd.configure.hardware = 0x0000;
+ cb_p->cmd.configure.min_frame_len = 0x0040;
+
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+ for(i = 0; i < 0xffff; i++)
+ if ((cb_p->ac_status & AC_SW_C))
+ break;
+ if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
+ printf("pc%d: config-configure failed; status = %x\n",
+ unit, cb_p->ac_status);
+ return(FALSE);
+ }
+/*
+ if (scb_p->scb_status & SCB_SW_INT) {
+ printf("pc%d configure(): bad configure state %x\n",
+ unit, scb_p->scb_status);
+ pc586ack(unit);
+ }
+*/
+ cb_p->ac_status = 0;
+ cb_p->ac_command = (AC_IASETUP|AC_CW_EL);
+
+ bcopy16(pc_softc[unit].ds_addr, cb_p->cmd.iasetup, ETHER_ADD_SIZE);
+
+ scb_p->scb_command = SCB_CU_STRT;
+ pc586chatt(unit);
+
+ for (i = 0; i < 0xffff; i++)
+ if ((cb_p->ac_status & AC_SW_C))
+ break;
+ if (i == 0xffff || !(cb_p->ac_status & AC_SW_OK)) {
+ printf("pc%d: config-address failed; status = %x\n",
+ unit, cb_p->ac_status);
+ return(FALSE);
+ }
+/*
+ if ((scb_p->scb_status & SCB_SW_INT) != SCB_SW_CNA) {
+ printf("pc%d configure(): unexpected final state %x\n",
+ unit, scb_p->scb_status);
+ }
+*/
+ pc586ack(unit);
+
+ return(TRUE);
+}
+
+/*
+ * pc586ack:
+ */
+pc586ack(unit)
+{
+ volatile scb_t *scb_p = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ int i;
+
+ if (!(scb_p->scb_command = scb_p->scb_status & SCB_SW_INT))
+ return;
+ CMD(CHANATT, 0x0001, unit);
+ for (i = 1000000; scb_p->scb_command && (i-- > 0); );
+ if (!i)
+ printf("pc%d pc586ack(): board not accepting command.\n", unit);
+}
+
+char *
+ram_to_ptr(offset, unit)
+int unit;
+u_short offset;
+{
+ if (offset == PC586NULL)
+ return(NULL);
+ if (offset > 0x3fff) {
+ printf("ram_to_ptr(%x, %d)\n", offset, unit);
+ panic("range");
+ return(NULL);
+ }
+ return(pc_softc[unit].sram + offset);
+}
+
+#ifndef REQUEUE_DBG
+bad_rbd_chain(offset, unit)
+{
+ rbd_t *rbdp;
+ char *sram = pc_softc[unit].sram;
+
+ for (;;) {
+ if (offset == PC586NULL)
+ return 0;
+ if (offset > 0x3fff) {
+ printf("pc%d: bad_rbd_chain offset = %x\n",
+ unit, offset);
+ pc586_cntrs[unit].rcv.bad_chain++;
+ return 1;
+ }
+
+ rbdp = (rbd_t *)(sram + offset);
+ offset = rbdp->next_rbd_offset;
+ }
+}
+#endif REQUEUE_DBG
+
+u_short
+ptr_to_ram(k_va, unit)
+char *k_va;
+int unit;
+{
+ return((u_short)(k_va - pc_softc[unit].sram));
+}
+
+pc586scb(unit)
+{
+ volatile scb_t *scb = (volatile scb_t *)(pc_softc[unit].sram + OFFSET_SCB);
+ volatile u_short*cmd = (volatile u_short *)(pc_softc[unit].prom + OFFSET_NORMMODE);
+ u_short i;
+
+ i = scb->scb_status;
+ printf("stat: stat %x, cus %x, rus %x //",
+ (i&0xf000)>>12, (i&0x0700)>>8, (i&0x0070)>>4);
+ i = scb->scb_command;
+ printf(" cmd: ack %x, cuc %x, ruc %x\n",
+ (i&0xf000)>>12, (i&0x0700)>>8, (i&0x0070)>>4);
+
+ printf("crc %d[%d], align %d[%d], rsc %d[%d], ovr %d[%d]\n",
+ scb->scb_crcerrs, pc586_cntrs[unit].rcv.crc,
+ scb->scb_alnerrs, pc586_cntrs[unit].rcv.frame,
+ scb->scb_rscerrs, pc586_cntrs[unit].rcv.rscerrs,
+ scb->scb_ovrnerrs, pc586_cntrs[unit].rcv.ovrnerrs);
+
+ printf("cbl %x, rfa %x //", scb->scb_cbl_offset, scb->scb_rfa_offset);
+ printf(" norm %x, ena %x, xfer %x //",
+ cmd[0] & 1, cmd[3] & 1, cmd[4] & 1);
+ printf(" atn %x, reset %x, type %x, stat %x\n",
+ cmd[1] & 1, cmd[2] & 1, cmd[5] & 1, cmd[6] & 1);
+}
+
+pc586tbd(unit)
+{
+ pc_softc_t *is = &pc_softc[unit];
+ tbd_t *tbd_p = (tbd_t *)(is->sram + OFFSET_TBD);
+ int i = 0;
+ int sum = 0;
+
+ do {
+ sum += (tbd_p->act_count & ~TBD_SW_EOF);
+ printf("%d: addr %x, count %d (%d), next %x, base %x\n",
+ i++, tbd_p->buffer_addr,
+ (tbd_p->act_count & ~TBD_SW_EOF), sum,
+ tbd_p->next_tbd_offset,
+ tbd_p->buffer_base);
+ if (tbd_p->act_count & TBD_SW_EOF)
+ break;
+ tbd_p = (tbd_t *)(is->sram + tbd_p->next_tbd_offset);
+ } while (1);
+}
+
+#ifndef MACH_KERNEL
+pc586hdwsleaze(countp, mb_pp, tm_pp, unit)
+struct mbuf **tm_pp;
+u_char **mb_pp;
+u_short *countp;
+{
+ struct mbuf *tm_p = *tm_pp;
+ u_char *mb_p = *mb_pp;
+ u_short count = 0;
+ u_char *cp;
+ int len;
+
+ pc586_cntrs[unit].xmt.sleaze++;
+ /*
+ * can we get a run that will be coallesced or
+ * that terminates before breaking
+ */
+ do {
+ count += tm_p->m_len;
+ if (tm_p->m_len & 1)
+ break;
+ } while ((tm_p = tm_p->m_next) != (struct mbuf *)0);
+ if ( (tm_p == (struct mbuf *)0) ||
+ count > HDW_THRESHOLD) {
+ *countp = (*tm_pp)->m_len;
+ *mb_pp = mtod((*tm_pp), u_char *);
+ printf("\n");
+ return;
+ }
+
+ /* we need to copy */
+ pc586_cntrs[unit].xmt.intrinsic++;
+ tm_p = *tm_pp;
+ mb_p = *mb_pp;
+ count = 0;
+ cp = (u_char *) t_packet;
+ do {
+ bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
+ count += len;
+ if (count > HDW_THRESHOLD)
+ break;
+ cp += len;
+ if (tm_p->m_next == (struct mbuf *)0)
+ break;
+ tm_p = tm_p->m_next;
+ } while (1);
+ pc586_cntrs[unit].xmt.intrinsic_count += count;
+ *countp = count;
+ *mb_pp = (u_char *) t_packet;
+ *tm_pp = tm_p;
+ return;
+}
+
+pc586sftwsleaze(countp, mb_pp, tm_pp, unit)
+struct mbuf **tm_pp;
+u_char **mb_pp;
+u_short *countp;
+{
+ struct mbuf *tm_p = *tm_pp;
+ u_char *mb_p = *mb_pp;
+ u_short count = 0;
+ u_char *cp = (u_char *) t_packet;
+ int len;
+
+ pc586_cntrs[unit].xmt.chain++;
+ /* we need to copy */
+ do {
+ bcopy(mtod(tm_p, u_char *), cp, len = tm_p->m_len);
+ count += len;
+ cp += len;
+ if (tm_p->m_next == (struct mbuf *)0)
+ break;
+ tm_p = tm_p->m_next;
+ } while (1);
+
+ *countp = count;
+ *mb_pp = (u_char *) t_packet;
+ *tm_pp = tm_p;
+ return;
+}
+#endif MACH_KERNEL
diff --git a/i386/i386at/if_pc586.h b/i386/i386at/if_pc586.h
new file mode 100644
index 00000000..59614067
--- /dev/null
+++ b/i386/i386at/if_pc586.h
@@ -0,0 +1,139 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Olivetti PC586 Mach Ethernet driver v1.0
+ * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
+ * All rights reserved.
+ *
+ */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <i386at/i82586.h> /* chip/board specific defines */
+
+#define STATUS_TRIES 15000
+#define ETHER_ADD_SIZE 6 /* size of a MAC address */
+#define ETHER_PCK_SIZE 1500 /* maximum size of an ethernet packet */
+
+/*
+ * Board Specific Defines:
+ */
+
+#define OFFSET_NORMMODE 0x3000
+#define OFFSET_CHANATT 0x3002
+#define OFFSET_RESET 0x3004
+#define OFFSET_INTENAB 0x3006
+#define OFFSET_XFERMODE 0x3008
+#define OFFSET_SYSTYPE 0x300a
+#define OFFSET_INTSTAT 0x300c
+#define OFFSET_PROM 0x2000
+
+#define EXTENDED_ADDR 0x20000
+#define OFFSET_SCP (0x7ff6 - 0x4000)
+#define OFFSET_ISCP (0x7fee - 0x4000)
+#define OFFSET_SCB (0x7fde - 0x4000)
+#define OFFSET_RU (0x4000 - 0x4000)
+#define OFFSET_RBD (0x4228 - 0x4000)
+#define OFFSET_CU (0x7814 - 0x4000)
+
+#define OFFSET_TBD (0x7914 - 0x4000)
+#define OFFSET_TBUF (0x79a4 - 0x4000)
+#define N_FD 25
+#define N_RBD 25
+#define N_TBD 18
+#define RCVBUFSIZE 540
+#define DL_DEAD 0xffff
+
+#define CMD_0 0
+#define CMD_1 0xffff
+
+#define PC586NULL 0xffff /* pc586 NULL for lists */
+
+#define DSF_LOCK 1
+#define DSF_RUNNING 2
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
+
+/*
+ * Driver (not board) specific defines and structures:
+ */
+
+typedef struct {
+ rbd_t r;
+ char rbd_pad[2];
+ char rbuffer[RCVBUFSIZE];
+} ru_t;
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+#ifndef TRUE
+#define TRUE 1
+#endif TRUE
+#define HZ 100
+#endif MACH_KERNEL
diff --git a/i386/i386at/if_wd8003.h b/i386/i386at/if_wd8003.h
new file mode 100644
index 00000000..3fa35ddc
--- /dev/null
+++ b/i386/i386at/if_wd8003.h
@@ -0,0 +1,315 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Western Digital Mach Ethernet driver
+ * Copyright (c) 1990 OSF Research Institute
+ */
+/*
+ Copyright 1990 by Open Software Foundation,
+Cambridge, MA.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby granted,
+provided that the above copyright notice appears in all copies and
+that both the copyright notice and this permission notice appear in
+supporting documentation, and that the name of OSF or Open Software
+Foundation not be used in advertising or publicity pertaining to
+distribution of the software without specific, written prior
+permission.
+
+ OSF DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OSF BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/***********************************************************/
+/* Defines for the 583 chip. */
+/***********************************************************/
+
+/*--- 8390 Registers ---*/
+#define OFF_8390 0x10 /* offset of the 8390 chip */
+
+/* Register offsets */
+
+#define IFWD_REG_0 0x00
+#define IFWD_REG_1 0x01
+#define IFWD_REG_2 0x02
+#define IFWD_REG_3 0x03
+#define IFWD_REG_4 0x04
+#define IFWD_REG_5 0x05
+#define IFWD_REG_6 0x06
+#define IFWD_REG_7 0x07
+
+/* Register offset definitions for all boards */
+
+#define IFWD_LAR_0 0x08
+#define IFWD_LAR_1 0x09
+#define IFWD_LAR_2 0x0a
+#define IFWD_LAR_3 0x0b
+#define IFWD_LAR_4 0x0c
+#define IFWD_LAR_5 0x0d
+#define IFWD_BOARD_ID 0x0e
+#define IFWD_CHKSUM 0x0f
+
+/* revision number mask for BOARD_ID */
+#define IFWD_BOARD_REV_MASK 0x1e
+
+/* REG 1 */
+#define IFWD_MEMSIZE 0x08
+#define IFWD_16BIT 0x01
+
+/* REG 5 */
+#define IFWD_REG5_MEM_MASK 0x3f /* B23-B19 of address of the memory */
+#define IFWD_LA19 0x01 /* B19 of address of the memory */
+#define IFWD_MEM16ENB 0x80 /* Enable 16 bit memory access from bus */
+#define IFWD_LAN16ENB 0x40 /* Enable 16 bit memory access from chip*/
+#define IFWD_INIT_LAAR IFWD_LA19
+#define IFWD_SOFTINT 0x20 /* Enable interrupt from pc */
+
+/* Defs for board rev numbers > 1 */
+#define IFWD_MEDIA_TYPE 0x01
+#define IFWD_SOFT_CONFIG 0x20
+#define IFWD_RAM_SIZE 0x40
+#define IFWD_BUS_TYPE 0x80
+
+/* Register offsets for reading the EEPROM in the 584 chip */
+#define IFWD_EEPROM_0 0x08
+#define IFWD_EEPROM_1 0x09
+#define IFWD_EEPROM_2 0x0A
+#define IFWD_EEPROM_3 0x0B
+#define IFWD_EEPROM_4 0x0C
+#define IFWD_EEPROM_5 0x0D
+#define IFWD_EEPROM_6 0x0E
+#define IFWD_EEPROM_7 0x0F
+
+/**** defs for manipulating the 584 ****/
+#define IFWD_OTHER_BIT 0x02
+#define IFWD_ICR_MASK 0x0C
+#define IFWD_EAR_MASK 0x0F
+#define IFWD_ENGR_PAGE 0xA0
+/* #define IFWD_RLA 0x10 defined in ICR defs */
+#define IFWD_EA6 0x80
+#define IFWD_RECALL_DONE_MASK 0x10
+#define IFWD_EEPROM_MEDIA_MASK 0x07
+#define IFWD_STARLAN_TYPE 0x00
+#define IFWD_ETHERNET_TYPE 0x01
+#define IFWD_TP_TYPE 0x02
+#define IFWD_EW_TYPE 0x03
+#define IFWD_EEPROM_IRQ_MASK 0x18
+#define IFWD_PRIMARY_IRQ 0x00
+#define IFWD_ALTERNATE_IRQ_1 0x08
+#define IFWD_ALTERNATE_IRQ_2 0x10
+#define IFWD_ALTERNATE_IRQ_3 0x18
+#define IFWD_EEPROM_RAM_SIZE_MASK 0xE0
+#define IFWD_EEPROM_RAM_SIZE_RES1 0x00
+#define IFWD_EEPROM_RAM_SIZE_RES2 0x20
+#define IFWD_EEPROM_RAM_SIZE_8K 0x40
+#define IFWD_EEPROM_RAM_SIZE_16K 0x60
+#define IFWD_EEPROM_RAM_SIZE_32K 0x80
+#define IFWD_EEPROM_RAM_SIZE_64K 0xA0
+#define IFWD_EEPROM_RAM_SIZE_RES3 0xC0
+#define IFWD_EEPROM_RAM_SIZE_RES4 0xE0
+#define IFWD_EEPROM_BUS_TYPE_MASK 0x07
+#define IFWD_EEPROM_BUS_TYPE_AT 0x00
+#define IFWD_EEPROM_BUS_TYPE_MCA 0x01
+#define IFWD_EEPROM_BUS_TYPE_EISA 0x02
+#define IFWD_EEPROM_BUS_SIZE_MASK 0x18
+#define IFWD_EEPROM_BUS_SIZE_8BIT 0x00
+#define IFWD_EEPROM_BUS_SIZE_16BIT 0x08
+#define IFWD_EEPROM_BUS_SIZE_32BIT 0x10
+#define IFWD_EEPROM_BUS_SIZE_64BIT 0x18
+
+/*****************************************************************************
+ * *
+ * Definitions for board ID. *
+ * *
+ * note: board ID should be ANDed with the STATIC_ID_MASK *
+ * before comparing to a specific board ID *
+ * The high order 16 bits correspond to the Extra Bits which do not *
+ * change the boards ID. *
+ * *
+ * Note: not all are implemented. Rest are here for future enhancements...*
+ * *
+ *****************************************************************************/
+
+#define IFWD_STARLAN_MEDIA 0x00000001 /* StarLAN */
+#define IFWD_ETHERNET_MEDIA 0x00000002 /* Ethernet */
+#define IFWD_TWISTED_PAIR_MEDIA 0x00000003 /* Twisted Pair */
+#define IFWD_EW_MEDIA 0x00000004 /* Ethernet and Twisted Pair */
+#define IFWD_MICROCHANNEL 0x00000008 /* MicroChannel Adapter */
+#define IFWD_INTERFACE_CHIP 0x00000010 /* Soft Config Adapter */
+/* #define IFWD_UNUSED 0x00000020 */ /* used to be INTELLIGENT */
+#define IFWD_BOARD_16BIT 0x00000040 /* 16 bit capability */
+#define IFWD_RAM_SIZE_UNKNOWN 0x00000000 /* 000 => Unknown RAM Size */
+#define IFWD_RAM_SIZE_RES_1 0x00010000 /* 001 => Reserved */
+#define IFWD_RAM_SIZE_8K 0x00020000 /* 010 => 8k RAM */
+#define IFWD_RAM_SIZE_16K 0x00030000 /* 011 => 16k RAM */
+#define IFWD_RAM_SIZE_32K 0x00040000 /* 100 => 32k RAM */
+#define IFWD_RAM_SIZE_64K 0x00050000 /* 101 => 64k RAM */
+#define IFWD_RAM_SIZE_RES_6 0x00060000 /* 110 => Reserved */
+#define IFWD_RAM_SIZE_RES_7 0x00070000 /* 111 => Reserved */
+#define IFWD_SLOT_16BIT 0x00080000 /* 16 bit board - 16 bit slot*/
+#define IFWD_NIC_690_BIT 0x00100000 /* NIC is 690 */
+#define IFWD_ALTERNATE_IRQ_BIT 0x00200000 /* Alternate IRQ is used */
+#define IFWD_INTERFACE_584_CHIP 0x00400000 /* Interface chip is a 584 */
+
+#define IFWD_MEDIA_MASK 0x00000007 /* Isolates Media Type */
+#define IFWD_RAM_SIZE_MASK 0x00070000 /* Isolates RAM Size */
+#define IFWD_STATIC_ID_MASK 0x0000FFFF /* Isolates Board ID */
+
+/* Word definitions for board types */
+#define WD8003E IFWD_ETHERNET_MEDIA
+#define WD8003EBT WD8003E /* functionally identical to WD8003E */
+#define WD8003S IFWD_STARLAN_MEDIA
+#define WD8003SH WD8003S /* functionally identical to WD8003S */
+#define WD8003WT IFWD_TWISTED_PAIR_MEDIA
+#define WD8003W (IFWD_TWISTED_PAIR_MEDIA | IFWD_INTERFACE_CHIP)
+#define WD8003EB (IFWD_ETHERNET_MEDIA | IFWD_INTERFACE_CHIP)
+#define WD8003EP WD8003EB /* with IFWD_INTERFACE_584_CHIP bit set */a
+#define WD8003EW (IFWD_EW_MEDIA | IFWD_INTERFACE_CHIP)
+#define WD8003ETA (IFWD_ETHERNET_MEDIA | IFWD_MICROCHANNEL)
+#define WD8003STA (IFWD_STARLAN_MEDIA | IFWD_MICROCHANNEL)
+#define WD8003EA (IFWD_ETHERNET_MEDIA | IFWD_MICROCHANNEL | \
+ IFWD_INTERFACE_CHIP)
+#define WD8003SHA (IFWD_STARLAN_MEDIA | IFWD_MICROCHANNEL | \
+ IFWD_INTERFACE_CHIP)
+#define WD8003WA (IFWD_TWISTED_PAIR_MEDIA | IFWD_MICROCHANNEL | \
+ IFWD_INTERFACE_CHIP)
+#define WD8013EBT (IFWD_ETHERNET_MEDIA | IFWD_BOARD_16BIT)
+#define WD8013EB (IFWD_ETHERNET_MEDIA | IFWD_BOARD_16BIT | \
+ IFWD_INTERFACE_CHIP)
+#define WD8013EP WD8013EB /* with IFWD_INTERFACE_584_CHIP bit set */
+#define WD8013W (IFWD_TWISTED_PAIR_MEDIA | IFWD_BOARD_16BIT | \
+ IFWD_INTERFACE_CHIP)
+#define WD8013EW (IFWD_EW_MEDIA | IFWD_BOARD_16BIT | \
+ IFWD_INTERFACE_CHIP)
+
+
+/**** Western digital node bytes ****/
+#define WD_NODE_ADDR_0 0x00
+#define WD_NODE_ADDR_1 0x00
+#define WD_NODE_ADDR_2 0xC0
+
+/*--- 83c583 registers ---*/
+#define IFWD_MSR 0x00 /* memory select register */
+ /* In 584 Board's command register */
+#define IFWD_ICR 0x01 /* interface configuration register */
+ /* In 584 8013 bus size register */
+#define IFWD_IAR 0x02 /* io address register */
+#define IFWD_BIO 0x03 /* bios ROM address register */
+#define IFWD_IRR 0x04 /* interrupt request register */
+#define IFWD_GP1 0x05 /* general purpose register 1 */
+#define IFWD_IOD 0x06 /* io data latch */
+#define IFWD_GP2 0x07 /* general purpose register 2 */
+#define IFWD_LAR 0x08 /* LAN address register */
+#define IFWD_LAR2 0x09 /* */
+#define IFWD_LAR3 0x0A /* */
+#define IFWD_LAR4 0x0B /* */
+#define IFWD_LAR5 0x0C /* */
+#define IFWD_LAR6 0x0D /* */
+#define IFWD_LAR7 0x0E /* */
+#define IFWD_LAR8 0x0F /* LAN address register */
+
+/********************* Register Bit Definitions **************************/
+/* MSR definitions */
+#define IFWD_RST 0x80 /* 1 => reset */
+#define IFWD_MENB 0x40 /* 1 => memory enable */
+#define IFWD_SA18 0x20 /* Memory enable bits */
+#define IFWD_SA17 0x10 /* telling where shared */
+#define IFWD_SA16 0x08 /* mem is to start. */
+#define IFWD_SA15 0x04 /* Assume SA19 = 1 */
+#define IFWD_SA14 0x02 /* */
+#define IFWD_SA13 0x01 /* */
+
+/* ICR definitions */
+#define IFWD_STR 0x80 /* Non-volatile EEPROM store */
+#define IFWD_RCL 0x40 /* Recall I/O Address from EEPROM */
+#define IFWD_RX7 0x20 /* Recall all but I/O and LAN address*/
+#define IFWD_RLA 0x10 /* Recall LAN Address */
+#define IFWD_MSZ 0x08 /* Shared Memory Size */
+#define IFWD_DMAE 0x04 /* DMA Enable */
+#define IFWD_IOPE 0x02 /* I/O Port Enable */
+#define IFWD_WTS 0x01 /* Word Transfer Select */
+
+/* IAR definitions */
+#define IFWD_IA15 0x80 /* I/O Address Bits */
+/* . */
+/* . */
+/* . */
+#define IFWD_IA5 0x01 /* */
+
+/* BIO definitions */
+#define IFWD_RS1 0x80 /* BIOS size bit 1 */
+#define IFWD_RS0 0x40 /* BIOS size bit 0 */
+#define IFWD_BA18 0x20 /* BIOS ROM Memory Address Bits */
+#define IFWD_BA17 0x10 /* */
+#define IFWD_BA16 0x08 /* */
+#define IFWD_BA15 0x04 /* */
+#define IFWD_BA14 0x02 /* BIOS ROM Memory Address Bits */
+#define IFWD_WINT 0x01 /* W8003 interrupt */
+
+/* IRR definitions */
+#define IFWD_IEN 0x80 /* Interrupt Enable */
+#define IFWD_IR1 0x40 /* Interrupt request bit 1 */
+#define IFWD_IR0 0x20 /* Interrupt request bit 0 */
+#define IFWD_AMD 0x10 /* Alternate mode */
+#define IFWD_AINT 0x08 /* Alternate interrupt */
+#define IFWD_BW1 0x04 /* BIOS Wait State Control bit 1 */
+#define IFWD_BW0 0x02 /* BIOS Wait State Control bit 0 */
+#define IFWD_OWS 0x01 /* Zero Wait State Enable */
+
+/* GP1 definitions */
+
+/* IOD definitions */
+
+/* GP2 definitions */
+
+/*************************************************************/
+/* Shared RAM buffer definitions */
+/*************************************************************/
+
+/**** NIC definitions ****/
+#define NIC_8003_SRAM_SIZE 0x2000 /* size of shared RAM buffer */
+#define NIC_HEADER_SIZE 4 /* size of receive header */
+#define NIC_PAGE_SIZE 0x100 /* each page of rcv ring is 256 byte */
+
+#define ETHER_ADDR_SIZE 6 /* size of a MAC address */
+
+#ifdef MACH
+#define HZ 100
+#endif
+
+#define DSF_LOCK 1
+#define DSF_RUNNING 2
+
+#define MOD_ENAL 1
+#define MOD_PROM 2
diff --git a/i386/i386at/immc.c b/i386/i386at/immc.c
new file mode 100644
index 00000000..d6756e31
--- /dev/null
+++ b/i386/i386at/immc.c
@@ -0,0 +1,77 @@
+/*
+ * Copyright (c) 1995-1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+
+#ifdef ENABLE_IMMEDIATE_CONSOLE
+
+/* This is a special "feature" (read: kludge)
+ intended for use only for kernel debugging.
+ It enables an extremely simple console output mechanism
+ that sends text straight to CGA/EGA/VGA video memory.
+ It has the nice property of being functional right from the start,
+ so it can be used to debug things that happen very early
+ before any devices are initialized. */
+
+int immediate_console_enable = 1;
+
+void
+immc_cnputc(unsigned char c)
+{
+ static int ofs = -1;
+
+ if (!immediate_console_enable)
+ return;
+ if (ofs < 0)
+ {
+ ofs = 0;
+ immc_cnputc('\n');
+ }
+ else if (c == '\n')
+ {
+ bcopy(0xb8000+80*2, 0xb8000, 80*2*24);
+ bzero(0xb8000+80*2*24, 80*2);
+ ofs = 0;
+ }
+ else
+ {
+ volatile unsigned char *p;
+
+ if (ofs >= 80)
+ {
+ immc_cnputc('\r');
+ immc_cnputc('\n');
+ }
+
+ p = (void*)0xb8000 + 80*2*24 + ofs*2;
+ p[0] = c;
+ p[1] = 0x0f;
+ ofs++;
+ }
+}
+
+int immc_cnmaygetc(void)
+{
+ return -1;
+}
+
+#endif ENABLE_IMMEDIATE_CONSOLE
+
diff --git a/i386/i386at/int_init.c b/i386/i386at/int_init.c
new file mode 100644
index 00000000..819201f3
--- /dev/null
+++ b/i386/i386at/int_init.c
@@ -0,0 +1,39 @@
+/*
+ * Copyright (c) 1994 The University of Utah and
+ * the Computer Systems Laboratory at the University of Utah (CSL).
+ * All rights reserved.
+ *
+ * Permission to use, copy, modify and distribute this software is hereby
+ * granted provided that (1) source code retains these copyright, permission,
+ * and disclaimer notices, and (2) redistributions including binaries
+ * reproduce the notices in supporting documentation, and (3) all advertising
+ * materials mentioning features or use of this software display the following
+ * acknowledgement: ``This product includes software developed by the
+ * Computer Systems Laboratory at the University of Utah.''
+ *
+ * THE UNIVERSITY OF UTAH AND CSL ALLOW FREE USE OF THIS SOFTWARE IN ITS "AS
+ * IS" CONDITION. THE UNIVERSITY OF UTAH AND CSL DISCLAIM ANY LIABILITY OF
+ * ANY KIND FOR ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * CSL requests users of this software to return to csl-dist@cs.utah.edu any
+ * improvements that they make and grant CSL redistribution rights.
+ *
+ * Author: Bryan Ford, University of Utah CSL
+ */
+
+#include "idt.h"
+#include "gdt.h"
+
+/* defined in locore.S */
+extern vm_offset_t int_entry_table[];
+
+void int_init()
+{
+ int i;
+
+ for (i = 0; i < 16; i++)
+ fill_idt_gate(PIC_INT_BASE + i,
+ int_entry_table[i], KERNEL_CS,
+ ACC_PL_K|ACC_INTR_GATE, 0);
+}
+
diff --git a/i386/i386at/interrupt.S b/i386/i386at/interrupt.S
new file mode 100644
index 00000000..25cd66e7
--- /dev/null
+++ b/i386/i386at/interrupt.S
@@ -0,0 +1,48 @@
+/*
+ * Copyright (c) 1995 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#include <mach/machine/asm.h>
+
+#include "ipl.h"
+#include "pic.h"
+#include "i386asm.h"
+
+#define READ_ISR (OCW_TEMPLATE|READ_NEXT_RD|READ_IS_ONRD)
+
+/*
+ * Generic interrupt handler.
+ *
+ * On entry, %eax contains the irq number.
+ */
+ENTRY(interrupt)
+ movl %eax,%ecx /* save irq number */
+ movb $(NON_SPEC_EOI),%al /* non-specific EOI */
+ outb %al,$(PIC_MASTER_ICW) /* ack interrupt to master */
+ cmpl $8,%ecx /* do we need to ack slave? */
+ jl 1f /* no, skip it */
+ outb %al,$(PIC_SLAVE_ICW)
+1:
+ shll $2,%ecx /* irq * 4 */
+ movl EXT(intpri)(%ecx),%edx /* get new ipl */
+ call spl /* set ipl */
+ movl EXT(iunit)(%ecx),%edx /* get device unit number */
+ pushl %eax /* push previous ipl */
+ pushl %edx /* push unit number */
+ call *EXT(ivect)(%ecx) /* call interrupt handler */
+ addl $4,%esp /* pop unit number */
+ call splx_cli /* restore previous ipl */
+ cli /* XXX no more nested interrupts */
+ addl $4,%esp /* pop previous ipl */
+ ret /* return */
diff --git a/i386/i386at/iopl.c b/i386/i386at/iopl.c
new file mode 100644
index 00000000..ae67568c
--- /dev/null
+++ b/i386/i386at/iopl.c
@@ -0,0 +1,287 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <mach/vm_prot.h>
+#include <mach/machine/vm_types.h>
+#include <mach/machine/vm_param.h>
+#include <mach/machine/eflags.h>
+
+#include <ipc/ipc_port.h>
+
+#include <device/io_req.h>
+
+#include <i386/io_port.h>
+#include <i386/pit.h>
+
+/*
+ * IOPL device.
+ */
+ipc_port_t iopl_device_port = IP_NULL;
+mach_device_t iopl_device = 0;
+
+/*
+ * Ports that we allow access to.
+ */
+io_reg_t iopl_port_list[] = {
+ /* timer 2 */
+ 0x42,
+ /* speaker output */
+ 0x61,
+ /* ATI - savage */
+ 0x1ce, 0x1cf,
+ /* game port */
+ 0x201,
+ /* sound board */
+ 0x220, 0x221, 0x222, 0x223, 0x224, 0x225, 0x226, 0x227,
+ 0x228, 0x229, 0x22a, 0x22b, 0x22c, 0x22d, 0x22e, 0x22f,
+ /* printer */
+ 0x278, 0x279, 0x27a,
+ 0x378, 0x379, 0x37a,
+ /* ega/vga */
+ 0x3b0, 0x3b1, 0x3b2, 0x3b3, 0x3b4, 0x3b5, 0x3b6, 0x3b7,
+ 0x3b8, 0x3b9, 0x3ba, 0x3bb, 0x3bc, 0x3bd, 0x3be, 0x3bf,
+ 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7,
+ 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3cc, 0x3cd, 0x3ce, 0x3cf,
+ 0x3d0, 0x3d1, 0x3d2, 0x3d3, 0x3d4, 0x3d5, 0x3d6, 0x3d7,
+ 0x3d8, 0x3d9, 0x3da, 0x3db, 0x3dc, 0x3dd, 0x3de, 0x3df,
+ /* end of list */
+ IO_REG_NULL,
+ /* patch space */
+ 0, 0, 0, 0, 0, 0, 0, 0,
+ 0, 0, 0, 0, 0, 0, 0, 0
+};
+
+int
+ioplopen(dev, flag, ior)
+ int dev;
+ int flag;
+ io_req_t ior;
+{
+ iopl_device_port = ior->io_device->port;
+ iopl_device = ior->io_device;
+
+ io_port_create(iopl_device, iopl_port_list);
+ return (0);
+}
+
+
+/*ARGSUSED*/
+ioplclose(dev, flags)
+ int dev;
+ int flags;
+{
+ io_port_destroy(iopl_device);
+ iopl_device_port = IP_NULL;
+ iopl_device = 0;
+ return 0;
+}
+
+/*ARGSUSED*/
+int iopl_all = 1;
+ioplmmap(dev, off, prot)
+int dev;
+vm_offset_t off;
+vm_prot_t prot;
+{
+ extern vm_offset_t phys_last_addr;
+
+ if (iopl_all) {
+ if (off == 0)
+ return 0;
+ else if (off < 0xa0000)
+ return -1;
+ else if (off >= 0x100000 && off <= phys_last_addr)
+ return -1;
+ else
+ return i386_btop(off);
+
+ }
+ if (off > 0x60000)
+ return(-1);
+
+ /* Get page frame number for the page to be mapped. */
+
+ return(i386_btop(0xa0000 + off));
+}
+
+/*
+ * For DOS compatibility, it's easier to list the ports we don't
+ * allow access to.
+ */
+#define IOPL_PORTS_USED_MAX 256
+io_reg_t iopl_ports_used[IOPL_PORTS_USED_MAX] = {
+ IO_REG_NULL
+};
+
+boolean_t
+iopl_port_forbidden(io_port)
+ int io_port;
+{
+ int i;
+
+#if 0 /* we only read from these... it should be OK */
+
+ if (io_port <= 0xff)
+ return TRUE; /* system ports. 42,61,70,71 allowed above */
+
+ if (io_port >= 0x130 && io_port <= 0x137)
+ return TRUE; /* AHA disk */
+
+ if (io_port >= 0x170 && io_port <= 0x177)
+ return TRUE; /* HD disk */
+
+ if (io_port >= 0x1f0 && io_port <= 0x1f7)
+ return TRUE; /* HD disk */
+
+ if (io_port >= 0x230 && io_port <= 0x237)
+ return TRUE; /* AHA disk */
+
+ if (io_port >= 0x280 && io_port <= 0x2df)
+ return TRUE; /* 8390 network */
+
+ if (io_port >= 0x300 && io_port <= 0x31f)
+ return TRUE; /* 8390 network */
+
+ if (io_port >= 0x330 && io_port <= 0x337)
+ return TRUE; /* AHA disk */
+
+ if (io_port >= 0x370 && io_port <= 0x377)
+ return TRUE; /* FD disk */
+
+ if (io_port >= 0x3f0 && io_port <= 0x3f7)
+ return TRUE; /* FD disk */
+
+#endif
+
+ /*
+ * Must be OK, as far as we know...
+ * Record the port in the list, for
+ * curiosity seekers.
+ */
+ for (i = 0; i < IOPL_PORTS_USED_MAX; i++) {
+ if (iopl_ports_used[i] == io_port)
+ break; /* in list */
+ if (iopl_ports_used[i] == IO_REG_NULL) {
+ iopl_ports_used[i] = io_port;
+ iopl_ports_used[i+1] = IO_REG_NULL;
+ break;
+ }
+ }
+
+ return FALSE;
+}
+
+/*
+ * Emulate certain IO instructions for the AT bus.
+ *
+ * We emulate writes to the timer control port, 43.
+ * Only writes to timer 2 are allowed.
+ *
+ * Temporarily, we allow reads of any IO port,
+ * but ONLY if the thread has the IOPL device mapped
+ * and is not in V86 mode.
+ *
+ * This is a HACK and MUST go away when the DOS emulator
+ * emulates these IO ports, or when we decide that
+ * the DOS world can get access to all uncommitted IO
+ * ports. In that case, config() should remove the IO
+ * ports for devices it exists from the allowable list.
+ */
+boolean_t
+iopl_emulate(regs, opcode, io_port)
+ struct i386_saved_state *regs;
+ int opcode;
+ int io_port;
+{
+ iopb_tss_t iopb;
+
+ iopb = current_thread()->pcb->ims.io_tss;
+ if (iopb == 0)
+ return FALSE; /* no IO mapped */
+
+ /*
+ * Handle outb to the timer control port,
+ * for timer 2 only.
+ */
+ if (io_port == PITCTL_PORT) {
+
+ int io_byte = regs->eax & 0xff;
+
+ if (((iopb->bitmap[PITCTR2_PORT >> 3] & (1 << (PITCTR2_PORT & 0x7)))
+ == 0) /* allowed */
+ && (opcode == 0xe6 || opcode == 0xee) /* outb */
+ && (io_byte & 0xc0) == 0x80) /* timer 2 */
+ {
+ outb(io_port, io_byte);
+ return TRUE;
+ }
+ return FALSE; /* invalid IO to port 42 */
+ }
+
+ /*
+ * If the thread has the IOPL device mapped, and
+ * the io port is not on the 'forbidden' list, allow
+ * reads from it. Reject writes.
+ *
+ * Don`t do this for V86 mode threads
+ * (hack for DOS emulator XXX!)
+ */
+ if (!(regs->efl & EFL_VM) &&
+ iopb_check_mapping(current_thread(), iopl_device) &&
+ !iopl_port_forbidden(io_port))
+ {
+ /*
+ * handle inb, inw, inl
+ */
+ switch (opcode) {
+ case 0xE4: /* inb imm */
+ case 0xEC: /* inb dx */
+ regs->eax = (regs->eax & 0xffffff00)
+ | inb(io_port);
+ return TRUE;
+
+ case 0x66E5: /* inw imm */
+ case 0x66ED: /* inw dx */
+ regs->eax = (regs->eax & 0xffff0000)
+ | inw(io_port);
+ return TRUE;
+
+ case 0xE5: /* inl imm */
+ case 0xED: /* inl dx */
+ regs->eax = inl(io_port);
+ return TRUE;
+
+ default:
+ return FALSE; /* OUT not allowed */
+ }
+ }
+
+ /*
+ * Not OK.
+ */
+ return FALSE;
+}
+
diff --git a/i386/i386at/kd.c b/i386/i386at/kd.c
new file mode 100644
index 00000000..6c41ec56
--- /dev/null
+++ b/i386/i386at/kd.c
@@ -0,0 +1,2990 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Olivetti Mach Console driver v0.0
+ * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989
+ * All rights reserved.
+ *
+ */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* $ Header: $ */
+
+#include <mach_kdb.h>
+
+#include <sys/types.h>
+#include <kern/time_out.h>
+#include <device/conf.h>
+#include <device/tty.h>
+#include <device/io_req.h>
+#include <device/buf.h> /* for struct uio (!) */
+#include <i386/io_port.h>
+#include <vm/vm_kern.h>
+#include "vm_param.h"
+#include <i386/machspl.h>
+#include <i386at/cram.h>
+#include <i386at/kd.h>
+#include <i386at/kdsoft.h>
+#include <cons.h>
+
+#include <blit.h>
+#if NBLIT > 0
+#include <i386at/blitvar.h>
+#else
+#define blit_present() FALSE
+#define blit_init() /* nothing */
+#endif
+
+#include <evc.h>
+#if NEVC > 0
+int evc1init();
+#else
+#define evc1init() FALSE
+#endif
+
+#define DEBUG 1 /* export feep() */
+
+#define DEFAULT -1 /* see kd_atoi */
+
+void kd_enqsc(); /* enqueues a scancode */
+
+void timeout();
+
+#define BROKEN_KEYBOARD_RESET
+
+
+struct tty kd_tty;
+extern int rebootflag;
+
+static void charput(), charmvup(), charmvdown(), charclear(), charsetcursor();
+static void kd_noopreset();
+boolean_t kdcheckmagic();
+
+int kdcnprobe(struct consdev *cp);
+int kdcninit(struct consdev *cp);
+int kdcngetc(dev_t dev, int wait);
+int kdcnputc(dev_t dev, int c);
+
+/*
+ * These routines define the interface to the device-specific layer.
+ * See kdsoft.h for a more complete description of what each routine does.
+ */
+void (*kd_dput)() = charput; /* put attributed char */
+void (*kd_dmvup)() = charmvup; /* block move up */
+void (*kd_dmvdown)() = charmvdown; /* block move down */
+void (*kd_dclear)() = charclear; /* block clear */
+void (*kd_dsetcursor)() = charsetcursor;
+ /* set cursor position on displayed page */
+void (*kd_dreset)() = kd_noopreset; /* prepare for reboot */
+
+/* forward declarations */
+unsigned char kd_getdata(), state2leds();
+
+
+/*
+ * Globals used for both character-based controllers and bitmap-based
+ * controllers. Default is EGA.
+ */
+
+vm_offset_t kd_bitmap_start = (vm_offset_t)0xa0000; /* XXX - put in kd.h */
+u_char *vid_start = (u_char *)EGA_START;
+ /* VM start of video RAM or frame buffer */
+csrpos_t kd_curpos = 0; /* set indirectly by kd_setpos--see kdsoft.h */
+short kd_lines = 25;
+short kd_cols = 80;
+char kd_attr = KA_NORMAL; /* current attribute */
+
+/*
+ * kd_state shows the state of the modifier keys (ctrl, caps lock,
+ * etc.) It should normally be changed by calling set_kd_state(), so
+ * that the keyboard status LEDs are updated correctly.
+ */
+int kd_state = KS_NORMAL;
+int kb_mode = KB_ASCII; /* event/ascii */
+
+/*
+ * State for the keyboard "mouse".
+ */
+int kd_kbd_mouse = 0;
+int kd_kbd_magic_scale = 6;
+int kd_kbd_magic_button = 0;
+
+/*
+ * Some keyboard commands work by sending a command, waiting for an
+ * ack (handled by kdintr), then sending data, which generates a
+ * second ack. If we are in the middle of such a sequence, kd_ack
+ * shows what the ack is for.
+ *
+ * When a byte is sent to the keyboard, it is kept around in last_sent
+ * in case it needs to be resent.
+ *
+ * The rest of the variables here hold the data required to complete
+ * the sequence.
+ *
+ * XXX - the System V driver keeps a command queue, I guess in case we
+ * want to start a command while another is in progress. Is this
+ * something we should worry about?
+ */
+enum why_ack {NOT_WAITING, SET_LEDS, DATA_ACK};
+enum why_ack kd_ack = NOT_WAITING;
+
+u_char last_sent = 0;
+
+u_char kd_nextled = 0;
+
+/*
+ * We don't provide any mutex protection for this flag because we know
+ * that this module will have been initialized by the time multiple
+ * threads are running.
+ */
+boolean_t kd_initialized = FALSE; /* driver initialized? */
+boolean_t kd_extended = FALSE;
+
+/* Array for processing escape sequences. */
+#define K_MAXESC 16
+u_char esc_seq[K_MAXESC];
+u_char *esc_spt = (u_char *)0;
+
+/*
+ * This array maps scancodes to Ascii characters (or character
+ * sequences).
+ * Each row corresponds to one key. There are NUMOUTPUT bytes per key
+ * state. The states are ordered: Normal, SHIFT, CTRL, ALT,
+ * SHIFT/ALT.
+ */
+unsigned char key_map[NUMKEYS][WIDTH_KMAP] = {
+{NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC},
+{K_ESC,NC,NC, K_ESC,NC,NC, K_ESC,NC,NC, K_ESC,NC,NC, K_ESC,NC,NC},
+{K_ONE,NC,NC, K_BANG,NC,NC, K_ONE,NC,NC, 0x1b,0x4e,0x31, 0x1b,0x4e,0x21},
+{K_TWO,NC,NC, K_ATSN,NC,NC, K_NUL,NC,NC, 0x1b,0x4e,0x32, 0x1b,0x4e,0x40},
+{K_THREE,NC,NC, K_POUND,NC,NC, K_THREE,NC,NC, 0x1b,0x4e,0x33, 0x1b,0x4e,0x23},
+{K_FOUR,NC,NC, K_DOLLAR,NC,NC, K_FOUR,NC,NC, 0x1b,0x4e,0x34, 0x1b,0x4e,0x24},
+{K_FIVE,NC,NC, K_PERC,NC,NC, K_FIVE,NC,NC, 0x1b,0x4e,0x35, 0x1b,0x4e,0x25},
+{K_SIX,NC,NC, K_CARET,NC,NC, K_RS,NC,NC, 0x1b,0x4e,0x36, 0x1b,0x4e,0x5e},
+{K_SEVEN,NC,NC, K_AMPER,NC,NC, K_SEVEN,NC,NC, 0x1b,0x4e,0x37, 0x1b,0x4e,0x26},
+{K_EIGHT,NC,NC, K_ASTER,NC,NC, K_EIGHT,NC,NC, 0x1b,0x4e,0x38, 0x1b,0x4e,0x2a},
+{K_NINE,NC,NC, K_LPAREN,NC,NC, K_NINE,NC,NC, 0x1b,0x4e,0x39,0x1b,0x4e,0x28},
+{K_ZERO,NC,NC, K_RPAREN,NC,NC, K_ZERO,NC,NC, 0x1b,0x4e,0x30,0x1b,0x4e,0x29},
+{K_MINUS,NC,NC, K_UNDSC,NC,NC, K_US,NC,NC, 0x1b,0x4e,0x2d, 0x1b,0x4e,0x5f},
+{K_EQL,NC,NC, K_PLUS,NC,NC, K_EQL,NC,NC, 0x1b,0x4e,0x3d, 0x1b,0x4e,0x2b},
+{K_BS,NC,NC, K_BS,NC,NC, K_BS,NC,NC, K_BS,NC,NC, K_BS,NC,NC},
+{K_HT,NC,NC, K_GS,NC,NC, K_HT,NC,NC, K_HT,NC,NC, K_GS,NC,NC},
+{K_q,NC,NC, K_Q,NC,NC, K_DC1,NC,NC, 0x1b,0x4e,0x71, 0x1b,0x4e,0x51},
+{K_w,NC,NC, K_W,NC,NC, K_ETB,NC,NC, 0x1b,0x4e,0x77, 0x1b,0x4e,0x57},
+{K_e,NC,NC, K_E,NC,NC, K_ENQ,NC,NC, 0x1b,0x4e,0x65, 0x1b,0x4e,0x45},
+{K_r,NC,NC, K_R,NC,NC, K_DC2,NC,NC, 0x1b,0x4e,0x72, 0x1b,0x4e,0x52},
+{K_t,NC,NC, K_T,NC,NC, K_DC4,NC,NC, 0x1b,0x4e,0x74, 0x1b,0x4e,0x54},
+{K_y,NC,NC, K_Y,NC,NC, K_EM,NC,NC, 0x1b,0x4e,0x79, 0x1b,0x4e,0x59},
+{K_u,NC,NC, K_U,NC,NC, K_NAK,NC,NC, 0x1b,0x4e,0x75, 0x1b,0x4e,0x55},
+{K_i,NC,NC, K_I,NC,NC, K_HT,NC,NC, 0x1b,0x4e,0x69, 0x1b,0x4e,0x49},
+{K_o,NC,NC, K_O,NC,NC, K_SI,NC,NC, 0x1b,0x4e,0x6f, 0x1b,0x4e,0x4f},
+{K_p,NC,NC, K_P,NC,NC, K_DLE,NC,NC, 0x1b,0x4e,0x70, 0x1b,0x4e,0x50},
+{K_LBRKT,NC,NC, K_LBRACE,NC,NC, K_ESC,NC,NC, 0x1b,0x4e,0x5b, 0x1b,0x4e,0x7b},
+{K_RBRKT,NC,NC, K_RBRACE,NC,NC, K_GS,NC,NC, 0x1b,0x4e,0x5d, 0x1b,0x4e,0x7d},
+{K_CR,NC,NC, K_CR,NC,NC, K_CR,NC,NC, K_CR,NC,NC, K_CR,NC,NC},
+{K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC, K_SCAN,K_CTLSC,NC,
+ K_SCAN,K_CTLSC,NC},
+{K_a,NC,NC, K_A,NC,NC, K_SOH,NC,NC, 0x1b,0x4e,0x61, 0x1b,0x4e,0x41},
+{K_s,NC,NC, K_S,NC,NC, K_DC3,NC,NC, 0x1b,0x4e,0x73, 0x1b,0x4e,0x53},
+{K_d,NC,NC, K_D,NC,NC, K_EOT,NC,NC, 0x1b,0x4e,0x65, 0x1b,0x4e,0x45},
+{K_f,NC,NC, K_F,NC,NC, K_ACK,NC,NC, 0x1b,0x4e,0x66, 0x1b,0x4e,0x46},
+{K_g,NC,NC, K_G,NC,NC, K_BEL,NC,NC, 0x1b,0x4e,0x67, 0x1b,0x4e,0x47},
+{K_h,NC,NC, K_H,NC,NC, K_BS,NC,NC, 0x1b,0x4e,0x68, 0x1b,0x4e,0x48},
+{K_j,NC,NC, K_J,NC,NC, K_LF,NC,NC, 0x1b,0x4e,0x6a, 0x1b,0x4e,0x4a},
+{K_k,NC,NC, K_K,NC,NC, K_VT,NC,NC, 0x1b,0x4e,0x6b, 0x1b,0x4e,0x4b},
+{K_l,NC,NC, K_L,NC,NC, K_FF,NC,NC, 0x1b,0x4e,0x6c, 0x1b,0x4e,0x4c},
+{K_SEMI,NC,NC, K_COLON,NC,NC, K_SEMI,NC,NC, 0x1b,0x4e,0x3b, 0x1b,0x4e,0x3a},
+{K_SQUOTE,NC,NC,K_DQUOTE,NC,NC,K_SQUOTE,NC,NC,0x1b,0x4e,0x27,0x1b,0x4e,0x22},
+{K_GRAV,NC,NC, K_TILDE,NC,NC, K_RS,NC,NC, 0x1b,0x4e,0x60, 0x1b,0x4e,0x7e},
+{K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC, K_SCAN,K_LSHSC,NC,
+ K_SCAN,K_LSHSC,NC},
+{K_BSLSH,NC,NC, K_PIPE,NC,NC, K_FS,NC,NC, 0x1b,0x4e,0x5c, 0x1b,0x4e,0x7c},
+{K_z,NC,NC, K_Z,NC,NC, K_SUB,NC,NC, 0x1b,0x4e,0x7a, 0x1b,0x4e,0x5a},
+{K_x,NC,NC, K_X,NC,NC, K_CAN,NC,NC, 0x1b,0x4e,0x78, 0x1b,0x4e,0x58},
+{K_c,NC,NC, K_C,NC,NC, K_ETX,NC,NC, 0x1b,0x4e,0x63, 0x1b,0x4e,0x43},
+{K_v,NC,NC, K_V,NC,NC, K_SYN,NC,NC, 0x1b,0x4e,0x76, 0x1b,0x4e,0x56},
+{K_b,NC,NC, K_B,NC,NC, K_STX,NC,NC, 0x1b,0x4e,0x62, 0x1b,0x4e,0x42},
+{K_n,NC,NC, K_N,NC,NC, K_SO,NC,NC, 0x1b,0x4e,0x6e, 0x1b,0x4e,0x4e},
+{K_m,NC,NC, K_M,NC,NC, K_CR,NC,NC, 0x1b,0x4e,0x6d, 0x1b,0x4e,0x4d},
+{K_COMMA,NC,NC, K_LTHN,NC,NC, K_COMMA,NC,NC, 0x1b,0x4e,0x2c, 0x1b,0x4e,0x3c},
+{K_PERIOD,NC,NC, K_GTHN,NC,NC, K_PERIOD,NC,NC,0x1b,0x4e,0x2e,0x1b,0x4e,0x3e},
+{K_SLASH,NC,NC, K_QUES,NC,NC, K_SLASH,NC,NC, 0x1b,0x4e,0x2f, 0x1b,0x4e,0x3f},
+{K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC, K_SCAN,K_RSHSC,NC,
+ K_SCAN,K_RSHSC,NC},
+{K_ASTER,NC,NC, K_ASTER,NC,NC, K_ASTER,NC,NC, 0x1b,0x4e,0x2a,0x1b,0x4e,0x2a},
+{K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC, K_SCAN,K_ALTSC,NC,
+ K_SCAN,K_ALTSC,NC},
+{K_SPACE,NC,NC, K_SPACE,NC,NC, K_NUL,NC,NC, K_SPACE,NC,NC, K_SPACE,NC,NC},
+{K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC,
+ K_SCAN,K_CLCKSC,NC, K_SCAN,K_CLCKSC,NC},
+{K_F1, K_F1S, K_F1, K_F1, K_F1S},
+{K_F2, K_F2S, K_F2, K_F2, K_F2S},
+{K_F3, K_F3S, K_F3, K_F3, K_F3S},
+{K_F4, K_F4S, K_F4, K_F4, K_F4S},
+{K_F5, K_F5S, K_F5, K_F5, K_F5S},
+{K_F6, K_F6S, K_F6, K_F6, K_F6S},
+{K_F7, K_F7S, K_F7, K_F7, K_F7S},
+{K_F8, K_F8S, K_F8, K_F8, K_F8S},
+{K_F9, K_F9S, K_F9, K_F9, K_F9S},
+{K_F10, K_F10S, K_F10, K_F10, K_F10S},
+{K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC,
+ K_SCAN,K_NLCKSC,NC, K_SCAN,K_NLCKSC,NC},
+{K_SCRL, K_NUL,NC,NC, K_SCRL, K_SCRL, K_NUL,NC,NC},
+{K_HOME, K_SEVEN,NC,NC, K_HOME, K_HOME, 0x1b,0x4e,0x37},
+{K_UA, K_EIGHT,NC,NC, K_UA, K_UA, 0x1b,0x4e,0x38},
+{K_PUP, K_NINE,NC,NC, K_PUP, K_PUP, 0x1b,0x4e,0x39},
+{0x1b,0x5b,0x53, K_MINUS,NC,NC, 0x1b,0x5b,0x53,0x1b,0x5b,0x53,0x1b,0x4e,0x2d},
+{K_LA, K_FOUR,NC,NC, K_LA, K_LA, 0x1b,0x4e,0x34},
+{0x1b,0x5b,0x47,K_FIVE,NC,NC,0x1b,0x5b,0x47, 0x1b,0x5b,0x47, 0x1b,0x4e,0x35},
+{K_RA, K_SIX,NC,NC, K_RA, K_RA, 0x1b,0x4e,0x36},
+{0x1b,0x5b,0x54,K_PLUS,NC,NC, 0x1b,0x5b,0x54, 0x1b,0x5b,0x54, 0x1b,0x4e,0x2b},
+{K_END, K_ONE,NC,NC, K_END, K_END, 0x1b,0x4e,0x31},
+{K_DA, K_TWO,NC,NC, K_DA, K_DA, 0x1b,0x4e,0x32},
+{K_PDN, K_THREE,NC,NC, K_PDN, K_PDN, 0x1b,0x4e,0x33},
+{K_INS, K_ZERO,NC,NC, K_INS, K_INS, 0x1b,0x4e,0x30},
+{K_DEL,NC,NC, K_PERIOD,NC,NC, K_DEL,NC,NC, K_DEL,NC,NC, 0x1b,0x4e,0x2e},
+{NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC},
+{NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC},
+{NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC,NC},
+{K_F11, K_F11S, K_F11, K_F11, K_F11S},
+{K_F12, K_F12S, K_F12, K_F12, K_F12S}
+};
+
+
+/*
+ * Globals used only for character-based controllers.
+ */
+
+short kd_index_reg = EGA_IDX_REG;
+short kd_io_reg = EGA_IO_REG;
+
+/*
+ * IO port sets for different controllers.
+ */
+io_reg_t vga_port_list[] = {
+ 0x3b4, 0x3b5, 0x3b8, 0x3b9, 0x3ba, /* MDA/EGA */
+ 0x3d4, 0x3d5, 0x3d8, 0x3d9, 0x3da, /* CGA/EGA */
+ 0x3c0, 0x3c1, 0x3c2, 0x3c3, 0x3c4, 0x3c5, 0x3c6, 0x3c7,
+ 0x3c8, 0x3c9, 0x3ca, 0x3cb, 0x3cc, 0x3cd, 0x3ce, 0x3cf,
+ IO_REG_NULL
+};
+
+mach_device_t kd_io_device = 0;
+
+kd_io_map_open(device)
+ mach_device_t device;
+{
+ kd_io_device = device;
+ io_port_create(device, vga_port_list);
+}
+
+kd_io_map_close()
+{
+ io_port_destroy(kd_io_device);
+ kd_io_device = 0;
+}
+
+/*
+ * Globals used only for bitmap-based controllers. See kdsoft.h for
+ * an explanation of what some of these variables are used for.
+ */
+
+u_char *font_start = 0; /* starting addr of font */
+
+short fb_width = 0; /* bits in frame buffer scan line */
+short fb_height = 0; /* scan lines in frame buffer*/
+short char_width = 0; /* bit width of 1 char */
+short char_height = 0; /* bit height of 1 char */
+short chars_in_font = 0;
+short cursor_height = 0; /* bit height of cursor */
+
+/* These initial values are simply guesses. */
+u_char char_black = 0;
+u_char char_white = 0xff;
+
+short xstart = 0;
+short ystart = 0;
+
+short char_byte_width = 0; /* char_width/NBBY */
+short fb_byte_width = 0; /* fb_width/NBBY */
+short font_byte_width = 0; /* num bytes in 1 scan line of font */
+
+/*
+ * Switch for poll vs. interrupt.
+ */
+int kd_pollc = 0;
+
+#ifdef DEBUG
+/*
+ * feep:
+ *
+ * Ring the bell for a short time.
+ * Warning: uses outb(). You may prefer to use kd_debug_put.
+ */
+feep()
+{
+ int i;
+
+ kd_bellon();
+ for (i = 0; i < 50000; ++i)
+ ;
+ kd_belloff();
+}
+
+pause()
+{
+ int i;
+
+ for (i = 0; i < 50000; ++i)
+ ;
+}
+
+/*
+ * Put a debugging character on the screen.
+ * LOC=0 means put it in the bottom right corner, LOC=1 means put it
+ * one column to the left, etc.
+ */
+kd_debug_put(loc, c)
+int loc;
+char c;
+{
+ csrpos_t pos = ONE_PAGE - (loc+1) * ONE_SPACE;
+
+ (*kd_dput)(pos, c, KA_NORMAL);
+}
+#endif /* DEBUG */
+
+
+extern int mouse_in_use;
+int old_kb_mode;
+
+cnpollc(on)
+boolean_t on;
+{
+ if (mouse_in_use) {
+ if (on) {
+ /* switch into X */
+ old_kb_mode = kb_mode;
+ kb_mode = KB_ASCII;
+ X_kdb_enter();
+
+ kd_pollc++;
+ } else {
+ --kd_pollc;
+
+ /* switch out of X */
+ X_kdb_exit();
+ kb_mode = old_kb_mode;
+ }
+ } else {
+ if (on) {
+ kd_pollc++;
+ } else {
+ --kd_pollc;
+ }
+ }
+}
+
+
+
+/*
+ * kdopen:
+ *
+ * This opens the console driver and sets up the tty and other
+ * rudimentary stuff including calling the line discipline for
+ * setting up the device independent stuff for a tty driver.
+ *
+ * input: device number 'dev', and flag
+ *
+ * output: device is opened and setup
+ *
+ */
+kdopen(dev, flag, ior)
+ dev_t dev;
+ int flag;
+ io_req_t ior;
+{
+ struct tty *tp;
+ int kdstart();
+ spl_t o_pri;
+ int kdstop();
+
+ tp = &kd_tty;
+ o_pri = spltty();
+ simple_lock(&tp->t_lock);
+ if (!(tp->t_state & (TS_ISOPEN|TS_WOPEN))) {
+ /* XXX ttychars allocates memory */
+ simple_unlock(&tp->t_lock);
+ ttychars(tp);
+ simple_lock(&tp->t_lock);
+ /*
+ * Special support for boot-time rc scripts, which don't
+ * stty the console.
+ */
+ tp->t_oproc = kdstart;
+ tp->t_stop = kdstop;
+ tp->t_ospeed = tp->t_ispeed = B9600;
+ tp->t_flags = ODDP|EVENP|ECHO|CRMOD|XTABS;
+ kdinit();
+
+ /* XXX kd_io_map_open allocates memory */
+ simple_unlock(&tp->t_lock);
+ kd_io_map_open(ior->io_device);
+ simple_lock(&tp->t_lock);
+ }
+ tp->t_state |= TS_CARR_ON;
+ simple_unlock(&tp->t_lock);
+ splx(o_pri);
+ return (char_open(dev, tp, flag, ior));
+}
+
+
+/*
+ * kdclose:
+ *
+ * This function merely executes the device independent code for
+ * closing the line discipline.
+ *
+ * input: device number 'dev', and flag
+ *
+ * output: device is closed
+ *
+ */
+/*ARGSUSED*/
+kdclose(dev, flag)
+int dev;
+int flag;
+{
+ struct tty *tp;
+
+ tp = &kd_tty;
+ {
+ spl_t s = spltty();
+ simple_lock(&tp->t_lock);
+ ttyclose(tp);
+ simple_unlock(&tp->t_lock);
+ splx(s);
+ }
+
+ kd_io_map_close();
+
+ return;
+
+}
+
+
+/*
+ * kdread:
+ *
+ * This function executes the device independent code to read from
+ * the tty.
+ *
+ * input: device number 'dev'
+ *
+ * output: characters are read from tty clists
+ *
+ */
+/*ARGSUSED*/
+kdread(dev, uio)
+int dev;
+struct uio *uio;
+{
+ struct tty *tp;
+
+ tp = &kd_tty;
+ tp->t_state |= TS_CARR_ON;
+ return((*linesw[kd_tty.t_line].l_read)(tp, uio));
+}
+
+
+/*
+ * kdwrite:
+ *
+ * This function does the device independent write action for this
+ * console (tty) driver.
+ *
+ * input: device number 'dev'
+ *
+ * output: characters are written to tty clists
+ *
+ */
+/*ARGSUSED*/
+kdwrite(dev, uio)
+int dev;
+struct uio *uio;
+{
+ return((*linesw[kd_tty.t_line].l_write)(&kd_tty, uio));
+}
+
+/*
+ * Mmap.
+ */
+
+/*ARGSUSED*/
+int
+kdmmap(dev, off, prot)
+ dev_t dev;
+ off_t off;
+ int prot;
+{
+ if ((u_int) off >= (128*1024))
+ return(-1);
+
+ /* Get page frame number for the page to be mapped. */
+ return(i386_btop(kd_bitmap_start+off));
+}
+
+kdportdeath(dev, port)
+ dev_t dev;
+ mach_port_t port;
+{
+ return (tty_portdeath(&kd_tty, port));
+}
+
+/*ARGSUSED*/
+io_return_t kdgetstat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int * data; /* pointer to OUT array */
+ unsigned int *count; /* OUT */
+{
+ io_return_t result;
+
+ switch (flavor) {
+ case KDGSTATE:
+ if (*count < 1)
+ return (D_INVALID_OPERATION);
+ *data = kd_state;
+ *count = 1;
+ result = D_SUCCESS;
+ break;
+
+ case KDGKBENT:
+ result = kdgetkbent((struct kbentry *)data);
+ *count = sizeof(struct kbentry)/sizeof(int);
+ break;
+
+ default:
+ result = tty_get_status(&kd_tty, flavor, data, count);
+ break;
+ }
+ return (result);
+}
+
+/*ARGSUSED*/
+io_return_t kdsetstat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int * data;
+ unsigned int count;
+{
+ io_return_t result;
+
+ switch (flavor) {
+ case KDSKBENT:
+ if (count < sizeof(struct kbentry)/sizeof(int)) {
+ return (D_INVALID_OPERATION);
+ }
+ result = kdsetkbent((struct kbentry *)data, 0);
+ break;
+
+ case KDSETBELL:
+ if (count < 1)
+ return (D_INVALID_OPERATION);
+ result = kdsetbell(*data, 0);
+ break;
+
+ default:
+ result = tty_set_status(&kd_tty, flavor, data, count);
+ }
+ return (result);
+}
+
+
+
+/*
+ * kdsetbell:
+ *
+ * Turn the bell on or off. Returns error code, if given bogus
+ * on/off value.
+ */
+kdsetbell(val, flags)
+int val; /* on or off */
+int flags; /* flags set for console */
+{
+ int err = 0;
+
+
+ if (val == KD_BELLON)
+ kd_bellon();
+ else if (val == KD_BELLOFF)
+ kd_belloff();
+ else
+ err = D_INVALID_OPERATION;
+
+ return(err);
+}
+
+
+/*
+ * kdgetkbent:
+ *
+ * Get entry from key mapping table. Returns error code, if any.
+ */
+kdgetkbent(kbent)
+struct kbentry * kbent;
+{
+ u_char *cp;
+ spl_t o_pri = SPLKD(); /* probably superfluous */
+
+ cp = &key_map[kbent->kb_index][CHARIDX(kbent->kb_state)];
+ kbent->kb_value[0] = *cp++;
+ kbent->kb_value[1] = *cp++;
+ kbent->kb_value[2] = *cp;
+ (void)splx(o_pri);
+ return(0);
+}
+
+
+/*
+ * kdsetkbent:
+ *
+ * Set entry in key mapping table. Return error code, if any.
+ */
+int
+kdsetkbent(kbent, flags)
+struct kbentry * kbent;
+int flags; /* flags set for console */
+{
+ u_char *cp;
+ spl_t o_pri;
+
+ o_pri = SPLKD();
+ cp = &key_map[kbent->kb_index][CHARIDX(kbent->kb_state)];
+ *cp++ = kbent->kb_value[0];
+ *cp++ = kbent->kb_value[1];
+ *cp = kbent->kb_value[2];
+ (void)splx(o_pri);
+ return(0);
+}
+
+/*
+ * kdintr:
+ *
+ * This function is the interrupt code for the driver. Since this is
+ * a special tty (console), interrupts are only for input, so we read in
+ * the character. If in ascii mode, we then do the mapping translation
+ * from the keyboard switch table and place the characters on the tty's
+ * input switch table. If in event mode, we create and queue a kd_event.
+ *
+ * input: interrupt vector 'vec'
+ *
+ * output: character or sequence is placed on appropriate queue
+ *
+ */
+/*ARGSUSED*/
+kdintr(vec, regs)
+int vec;
+int regs;
+{
+ struct tty *tp;
+ unsigned char c;
+ unsigned char scancode;
+ int o_pri;
+ int char_idx;
+ boolean_t up = FALSE; /* key-up event */
+ extern int mouse_in_use;
+ if (kd_pollc)
+ return; /* kdb polling kbd */
+
+ tp = &kd_tty;
+#ifdef old
+ while ((inb(K_STATUS) & K_OBUF_FUL) == 0); /* this should never loop */
+#else old
+ {
+ /*
+ * Allow for keyboards that raise interrupt before
+ * the character gets to the buffer. But don't wait
+ * forever if grabbing the character by polling leaves
+ * the interrupt on but buffer empty.
+ */
+ /*
+ * Micronics VLB motherboard with 486DX2 can report keyboard
+ * interrupt before K_STATUS register indicates that the
+ * output buffer is full. Moreover, the bus won't settle w
+ * while we poll K_STATUS at speed. Temporary fix is to break
+ * out after safety runs out and pick up keyboard event. This
+ * should be fixed eventually by putting a 1us timout between
+ * inb's to K_STATUS and fix the pic initialization order to
+ * avoid bootup keyboard wedging (ie make kd a real device)
+ */
+ int safety = 1000;
+ while ((inb(K_STATUS) & K_OBUF_FUL) == 0)
+ if (!safety--) break; /* XXX */
+ }
+#endif old
+ /*
+ * We may have seen a mouse event.
+ */
+ if ((inb(K_STATUS) & 0x20) == 0x20) {
+ if (mouse_in_use) {
+ mouse_handle_byte((u_char)inb(K_RDWR));
+ return;
+ } else {
+ printf("M%xI", inb(K_RDWR));
+ return;
+ }
+ }
+
+ scancode = inb(K_RDWR);
+ if (scancode == K_EXTEND) {
+ if (kb_mode != KB_EVENT)
+ kd_extended = TRUE;
+ goto done;
+ } else if (scancode == K_RESEND) {
+ kd_resend();
+ goto done;
+ } else if (scancode == K_ACKSC) {
+ kd_handle_ack();
+ goto done;
+ } else if (kd_kbd_mouse && kd_kbd_magic(scancode)) {
+ goto done;
+ } else if (kdcheckmagic(scancode, &regs)) {
+ goto done;
+ } else if (kb_mode == KB_EVENT) {
+ kd_enqsc(scancode);
+ goto done;
+ } /* else... */
+
+ if (scancode & K_UP) {
+ up = TRUE;
+ scancode &= ~K_UP;
+ }
+ if (scancode < NUMKEYS) {
+ /* Lookup in map, then process. */
+ char_idx = kdstate2idx(kd_state, kd_extended);
+ c = key_map[scancode][char_idx];
+ if (c == K_SCAN) {
+ c = key_map[scancode][++char_idx];
+ set_kd_state(do_modifier(kd_state, c, up));
+ } else if (!up) {
+ /* regular key-down */
+ int max; /* max index for char sequence */
+
+ max = char_idx + NUMOUTPUT;
+ char_idx++;
+ if (!kd_extended) {
+ if (kd_state&KS_CLKED) {
+ if (kd_isupper(c)) {
+ c += ('a' - 'A');
+ max = char_idx;
+ }
+ else if (kd_islower(c)) {
+ c -= ('a' - 'A');
+ max = char_idx;
+ }
+ }
+ /*
+ * Notice that even if the keypad is remapped,
+ * NumLock only effects the keys that are
+ * physically part of the keypad. Is this
+ * The Right Thing?
+ */
+ if ((kd_state&KS_NLKED) &&
+ (((K_HOMESC) <= scancode) &&
+ (scancode <= (K_DELSC)))) {
+ char_idx = CHARIDX(SHIFT_STATE);
+ c = key_map[scancode][char_idx];
+ max = char_idx + NUMOUTPUT;
+ char_idx++;
+ }
+ }
+
+ /*
+ * here's where we actually put the char (or
+ * char sequence, for function keys) onto the
+ * input queue.
+ */
+ for ( ; (c != K_DONE) && (char_idx <= max);
+ c = key_map[scancode][char_idx++]) {
+ (*linesw[tp->t_line].l_rint)(c, tp);
+ }
+ kd_extended = FALSE;
+ }
+ }
+
+ done:
+ return;
+}
+
+/*
+ * kd_handle_ack:
+ *
+ * For pending commands, complete the command. For data bytes,
+ * drop the ack on the floor.
+ */
+kd_handle_ack()
+{
+ switch (kd_ack) {
+ case SET_LEDS:
+ kd_setleds2();
+ kd_ack = DATA_ACK;
+ break;
+ case DATA_ACK:
+ kd_ack = NOT_WAITING;
+ break;
+ case NOT_WAITING:
+ printf("unexpected ACK from keyboard\n");
+ break;
+ default:
+ panic("bogus kd_ack\n");
+ break;
+ }
+}
+
+/*
+ * kd_resend:
+ *
+ * Resend a missed keyboard command or data byte.
+ */
+kd_resend()
+{
+ if (kd_ack == NOT_WAITING)
+ printf("unexpected RESEND from keyboard\n");
+ else
+ kd_senddata(last_sent);
+}
+
+
+/*
+ * do_modifier:
+ *
+ * Change keyboard state according to which modifier key and
+ * whether it went down or up.
+ *
+ * input: the current state, the key, and the key's direction.
+ * The key can be any key, not just a modifier key.
+ *
+ * output: the new state
+ */
+do_modifier(state, c, up)
+int state;
+Scancode c;
+boolean_t up;
+{
+ switch (c) {
+ case (K_ALTSC):
+ if (up)
+ state &= ~KS_ALTED;
+ else
+ state |= KS_ALTED;
+ kd_extended = FALSE;
+ break;
+#ifndef ORC
+ case (K_CLCKSC):
+#endif ORC
+ case (K_CTLSC):
+ if (up)
+ state &= ~KS_CTLED;
+ else
+ state |= KS_CTLED;
+ kd_extended = FALSE;
+ break;
+#ifdef ORC
+ case (K_CLCKSC):
+ if (!up)
+ state ^= KS_CLKED;
+ break;
+#endif ORC
+ case (K_NLCKSC):
+ if (!up)
+ state ^= KS_NLKED;
+ break;
+ case (K_LSHSC):
+ case (K_RSHSC):
+ if (up)
+ state &= ~KS_SHIFTED;
+ else
+ state |= KS_SHIFTED;
+ kd_extended = FALSE;
+ break;
+ }
+
+ return(state);
+}
+
+
+/*
+ * kdcheckmagic:
+ *
+ * Check for magic keystrokes for invoking the debugger or
+ * rebooting or ...
+ *
+ * input: an unprocessed scancode
+ *
+ * output: TRUE if a magic key combination was recognized and
+ * processed. FALSE otherwise.
+ *
+ * side effects:
+ * various actions possible, depending on which keys are
+ * pressed. If the debugger is called, steps are taken
+ * to ensure that the system doesn't think the magic keys
+ * are still held down.
+ */
+boolean_t
+kdcheckmagic(scancode, regs)
+Scancode scancode;
+int *regs;
+{
+ static int magic_state = KS_NORMAL; /* like kd_state */
+ boolean_t up = FALSE;
+ extern int rebootflag;
+
+ if (scancode == 0x46) /* scroll lock */
+/* if (scancode == 0x52) ** insert key */
+ {
+ kd_kbd_mouse = !kd_kbd_mouse;
+ kd_kbd_magic_button = 0;
+ return(TRUE);
+ }
+ if (scancode & K_UP) {
+ up = TRUE;
+ scancode &= ~K_UP;
+ }
+ magic_state = do_modifier(magic_state, scancode, up);
+
+ if ((magic_state&(KS_CTLED|KS_ALTED)) == (KS_CTLED|KS_ALTED)) {
+ switch (scancode) {
+#if MACH_KDB
+ case K_dSC: /* ctl-alt-d */
+ kdb_kintr(); /* invoke debugger */
+ /* Returned from debugger, so reset kbd state. */
+ (void)SPLKD();
+ magic_state = KS_NORMAL;
+ if (kb_mode == KB_ASCII)
+ kd_state = KS_NORMAL;
+ /* setting leds kills kbd */
+ else {
+ kd_enqsc(K_ALTSC | K_UP);
+ kd_enqsc(K_CTLSC | K_UP);
+ kd_enqsc(K_dSC | K_UP);
+ }
+ return(TRUE);
+ break;
+#endif MACH_KDB
+ case K_DELSC: /* ctl-alt-del */
+ /* if rebootflag is on, reboot the system */
+ if (rebootflag)
+ kdreboot();
+ break;
+ }
+ }
+ return(FALSE);
+}
+
+
+/*
+ * kdstate2idx:
+ *
+ * Return the value for the 2nd index into key_map that
+ * corresponds to the given state.
+ */
+kdstate2idx(state, extended)
+int state; /* bit vector, not a state index */
+boolean_t extended;
+{
+ int state_idx = NORM_STATE;
+
+ if ((!extended) && state != KS_NORMAL) {
+ if ((state&(KS_SHIFTED|KS_ALTED)) == (KS_SHIFTED|KS_ALTED))
+ state_idx = SHIFT_ALT;
+ else if (state&KS_SHIFTED)
+ state_idx = SHIFT_STATE;
+ else if (state&KS_ALTED)
+ state_idx = ALT_STATE;
+ else if (state&KS_CTLED)
+ state_idx = CTRL_STATE;
+ }
+
+ return (CHARIDX(state_idx));
+}
+
+/*
+ * kdstart:
+ *
+ * This function does the general processing of characters and other
+ * operations for the device driver. The device independent portion of
+ * the tty driver calls this routine (it's setup in kdinit) with a
+ * given command. That command is then processed, and control is passed
+ * back to the kernel.
+ *
+ * input: tty pointer 'tp', and command to execute 'cmd'
+ *
+ * output: command is executed
+ *
+ * Entered and left at spltty. Drops priority to spl0 to display character.
+ * ASSUMES that it is never called from interrupt-driven code.
+ */
+kdstart(tp)
+struct tty *tp;
+{
+ spl_t o_pri;
+ int ch;
+ unsigned char c;
+
+ if (tp->t_state & TS_TTSTOP)
+ return;
+ for ( ; ; ) {
+ tp->t_state &= ~TS_BUSY;
+ if (tp->t_state & TS_TTSTOP)
+ break;
+ if ((tp->t_outq.c_cc <= 0) || (ch = getc(&tp->t_outq)) == -1)
+ break;
+ c = ch;
+ /*
+ * Drop priority for long screen updates. ttstart() calls us at
+ * spltty.
+ */
+ o_pri = splsoftclock(); /* block timeout */
+ if (c == (K_ESC)) {
+ if (esc_spt == esc_seq) {
+ *(esc_spt++)=(K_ESC);
+ *(esc_spt) = '\0';
+ } else {
+ kd_putc((K_ESC));
+ esc_spt = esc_seq;
+ }
+ } else {
+ if (esc_spt - esc_seq) {
+ if (esc_spt - esc_seq > K_MAXESC - 1)
+ esc_spt = esc_seq;
+ else {
+ *(esc_spt++) = c;
+ *(esc_spt) = '\0';
+ kd_parseesc();
+ }
+ } else {
+ kd_putc(c);
+ }
+ }
+ splx(o_pri);
+ }
+ if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
+ tt_write_wakeup(tp);
+ }
+}
+
+/*ARGSUSED*/
+kdstop(tp, flags)
+ register struct tty *tp;
+ int flags;
+{
+ /*
+ * do nothing - all characters are output by one call to
+ * kdstart.
+ */
+}
+
+/*
+ * kdinit:
+ *
+ * This code initializes the structures and sets up the port registers
+ * for the console driver.
+ *
+ * Each bitmap-based graphics card is likely to require a unique
+ * way to determine the card's presence. The driver runs through
+ * each "special" card that it knows about and uses the first one
+ * that it finds. If it doesn't find any, it assumes that an
+ * EGA-like card is installed.
+ *
+ * input : None. Interrupts are assumed to be disabled
+ * output : Driver is initialized
+ *
+ */
+kdinit()
+{
+ void kd_xga_init();
+ unsigned char k_comm; /* keyboard command byte */
+
+ if (kd_initialized)
+ return;
+
+ esc_spt = esc_seq;
+ kd_attr = KA_NORMAL;
+
+ /*
+ * board specific initialization: set up globals and kd_dxxx
+ * pointers, and synch displayed cursor with logical cursor.
+ */
+ if (!evc1init())
+ if (blit_present())
+ blit_init();
+ else
+ kd_xga_init();
+
+ /* get rid of any garbage in output buffer */
+ if (inb(K_STATUS) & K_OBUF_FUL)
+ (void)inb(K_RDWR);
+
+ kd_sendcmd(KC_CMD_READ); /* ask for the ctlr command byte */
+ k_comm = kd_getdata();
+ k_comm &= ~K_CB_DISBLE; /* clear keyboard disable bit */
+ k_comm |= K_CB_ENBLIRQ; /* enable interrupt */
+ kd_sendcmd(KC_CMD_WRITE); /* write new ctlr command byte */
+ kd_senddata(k_comm);
+ kd_initialized = TRUE;
+
+#ifdef ENABLE_IMMEDIATE_CONSOLE
+ /* Now that we're set up, we no longer need or want the
+ immediate console. */
+ {
+ extern int immediate_console_enable;
+ immediate_console_enable = 0;
+ }
+
+ /* The immediate console printed stuff at the bottom of the
+ screen rather than at the cursor position, so that's where
+ we should start. */
+ kd_setpos(ONE_PAGE - ONE_LINE); printf("\n");
+#endif
+
+ cnsetleds(kd_state = KS_NORMAL);
+ /* clear the LEDs AFTER we
+ enable the keyboard controller.
+ This keeps NUM-LOCK from being
+ set on the NEC Versa. */
+}
+
+/*
+ * kd_belloff:
+ *
+ * This routine shuts the bell off, by sending the appropriate code
+ * to the speaker port.
+ *
+ * input : None
+ * output : bell is turned off
+ *
+ */
+static unsigned int kd_bellstate = 0;
+kd_belloff()
+{
+ unsigned char status;
+
+ status = (inb(K_PORTB) & ~(K_SPKRDATA | K_ENABLETMR2));
+ outb(K_PORTB, status);
+ kd_bellstate = 0;
+ return;
+}
+
+
+/*
+ * kd_bellon:
+ *
+ * This routine turns the bell on.
+ *
+ * input : None
+ * output : bell is turned on
+ *
+ */
+kd_bellon()
+{
+ unsigned char status;
+
+ /* program timer 2 */
+ outb(K_TMRCTL, K_SELTMR2 | K_RDLDTWORD | K_TSQRWAVE | K_TBINARY);
+ outb(K_TMR2, 1500 & 0xff); /* LSB */
+ outb(K_TMR2, (int)1500 >> 8); /* MSB */
+
+ /* start speaker - why must we turn on K_SPKRDATA? */
+ status = (inb(K_PORTB)| K_ENABLETMR2 | K_SPKRDATA);
+ outb(K_PORTB, status);
+ return;
+}
+
+/*
+ *
+ * Function kd_putc():
+ *
+ * This function simply puts a character on the screen. It does some
+ * special processing for linefeed, carriage return, backspace and
+ * the bell.
+ *
+ * input : character to be displayed
+ * output : character is displayed, or some action is taken
+ *
+ */
+int sit_for_0 = 1;
+
+kd_putc(ch)
+u_char ch;
+{
+ if ((!ch) && sit_for_0)
+ return;
+
+ switch (ch) {
+ case ((K_LF)):
+ kd_down();
+ break;
+ case ((K_CR)):
+ kd_cr();
+ break;
+ case ((K_BS)):
+ kd_left();
+ break;
+ case ((K_HT)):
+ kd_tab();
+ break;
+ case ((K_BEL)):
+ /*
+ * Similar problem to K_BS here (behavior might depend
+ * on tty setting). Also check LF and CR.
+ */
+ if (!kd_bellstate)
+ {
+ kd_bellon();
+ timeout(kd_belloff, 0, hz/8 );
+ kd_bellstate = 1;
+ }
+ break;
+ default:
+ (*kd_dput)(kd_curpos, ch, kd_attr);
+ kd_right();
+ break;
+ }
+ return;
+}
+
+
+/*
+ * kd_setpos:
+ *
+ * This function sets the software and hardware cursor position
+ * on the screen, using device-specific code to actually move and
+ * display the cursor.
+ *
+ * input : position on (or off) screen to move the cursor to
+ * output : cursor position is updated, screen has been scrolled
+ * if necessary to bring cursor position back onto
+ * screen.
+ *
+ */
+kd_setpos(newpos)
+csrpos_t newpos;
+{
+ if (newpos > ONE_PAGE) {
+ kd_scrollup();
+ newpos = BOTTOM_LINE;
+ }
+ if (newpos < 0) {
+ kd_scrolldn();
+ newpos = 0;
+ }
+
+ (*kd_dsetcursor)(newpos);
+}
+
+
+/*
+ * kd_scrollup:
+ *
+ * This function scrolls the screen up one line using a DMA memory
+ * copy.
+ *
+ * input : None
+ * output : lines on screen appear to be shifted up one line
+ *
+ */
+kd_scrollup()
+{
+ csrpos_t to;
+ csrpos_t from;
+ int count;
+
+ /* scroll up */
+ to = 0;
+ from = ONE_LINE;
+ count = (ONE_PAGE - ONE_LINE)/ONE_SPACE;
+ (*kd_dmvup)(from, to, count);
+
+ /* clear bottom line */
+ to = BOTTOM_LINE;
+ count = ONE_LINE/ONE_SPACE;
+ (*kd_dclear)(to, count, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_scrolldn:
+ *
+ * Scrolls the characters on the screen down one line.
+ *
+ * input : None
+ * output : Lines on screen appear to be moved down one line
+ *
+ */
+kd_scrolldn()
+{
+ csrpos_t to;
+ csrpos_t from;
+ int count;
+
+ /* move down */
+ to = ONE_PAGE - ONE_SPACE;
+ from = ONE_PAGE - ONE_LINE - ONE_SPACE;
+ count = (ONE_PAGE - ONE_LINE) / ONE_SPACE;
+ (*kd_dmvdown)(from, to, count);
+
+ /* clear top line */
+ to = 0;
+ count = ONE_LINE/ONE_SPACE;
+ (*kd_dclear)(to, count, kd_attr);
+ return;
+
+}
+
+
+/*
+ * kd_parseesc:
+ *
+ * This routine begins the parsing of an escape sequence. It uses the
+ * escape sequence array and the escape spot pointer to handle
+ * asynchronous parsing of escape sequences.
+ *
+ * input : String of characters prepended by an escape
+ * output : Appropriate actions are taken depending on the string as
+ * defined by the ansi terminal specification
+ *
+ */
+kd_parseesc()
+{
+ u_char *escp;
+
+ escp = esc_seq + 1; /* point to char following ESC */
+ switch(*(escp)) {
+ case 'c':
+ kd_cls();
+ kd_home();
+ esc_spt = esc_seq; /* reset spot in ESC sequence */
+ break;
+ case '[':
+ escp++;
+ kd_parserest(escp);
+ break;
+ case '\0':
+ break; /* not enough info yet */
+ default:
+ kd_putc(*escp);
+ esc_spt = esc_seq; /* inv sequence char, reset */
+ break;
+ }
+ return;
+
+}
+
+
+/*
+ * kd_parserest:
+ *
+ * This function will complete the parsing of an escape sequence and
+ * call the appropriate support routine if it matches a character. This
+ * function could be greatly improved by using a function jump table, and
+ * removing this bulky switch statement.
+ *
+ * input : An string
+ * output : Appropriate action based on whether the string matches a
+ * sequence acceptable to the ansi terminal specification
+ *
+ */
+kd_parserest(cp)
+u_char *cp;
+{
+ int number;
+ csrpos_t newpos;
+
+ cp += kd_atoi(cp, &number);
+ switch(*cp) {
+ case 'm':
+ switch(number) {
+ case DEFAULT:
+ case 0:
+ kd_attr = KA_NORMAL;
+ break;
+ case 7:
+ kd_attr = KA_REVERSE;
+ break;
+ default:
+ kd_attr = KA_NORMAL;
+ break;
+ }
+ esc_spt = esc_seq;
+ break;
+ case '@':
+ if (number == DEFAULT)
+ kd_insch(1);
+ else
+ kd_insch(number);
+ esc_spt = esc_seq;
+ break;
+ case 'H':
+ kd_home();
+ esc_spt = esc_seq;
+ break;
+ case 'A':
+ if (number == DEFAULT)
+ kd_up();
+ else
+ while (number--)
+ kd_up();
+ esc_spt = esc_seq;
+ break;
+ case 'B':
+ if (number == DEFAULT)
+ kd_down();
+ else
+ while (number--)
+ kd_down();
+ esc_spt = esc_seq;
+ break;
+ case 'C':
+ if (number == DEFAULT)
+ kd_right();
+ else
+ while (number--)
+ kd_right();
+ esc_spt = esc_seq;
+ break;
+ case 'D':
+ if (number == DEFAULT)
+ kd_left();
+ else
+ while (number--)
+ kd_left();
+ esc_spt = esc_seq;
+ break;
+ case 'E':
+ kd_cr();
+ if (number == DEFAULT)
+ kd_down();
+ else
+ while (number--)
+ kd_down();
+ esc_spt = esc_seq;
+ break;
+ case 'F':
+ kd_cr();
+ if (number == DEFAULT)
+ kd_up();
+ else
+ while (number--)
+ kd_up();
+ esc_spt = esc_seq;
+ break;
+ case 'G':
+ if (number == DEFAULT)
+ number = 0;
+ else
+ if (number > 0)
+ --number; /* because number is from 1 */
+ kd_setpos(BEG_OF_LINE(kd_curpos) + number * ONE_SPACE);
+ esc_spt = esc_seq;
+ break;
+ case ';':
+ ++cp;
+ if (*cp == '\0')
+ break; /* not ready yet */
+ if (number == DEFAULT)
+ number = 0;
+ else
+ if (number > 0)
+ --number; /* numbered from 1 */
+ newpos = (number * ONE_LINE); /* setup row */
+ cp += kd_atoi(cp, &number);
+ if (*cp == '\0')
+ break; /* not ready yet */
+ if (number == DEFAULT)
+ number = 0;
+ else if (number > 0)
+ number--;
+ newpos += (number * ONE_SPACE); /* setup column */
+ if (newpos < 0)
+ newpos = 0; /* upper left */
+ if (newpos > ONE_PAGE)
+ newpos = (ONE_PAGE - ONE_SPACE);
+ /* lower right */
+ if (*cp == '\0')
+ break; /* not ready yet */
+ if (*cp == 'H') {
+ kd_setpos(newpos);
+ esc_spt = esc_seq; /* done, reset */
+ }
+ else
+ esc_spt = esc_seq;
+ break; /* done or not ready */
+ case 'J':
+ switch(number) {
+ case DEFAULT:
+ case 0:
+ kd_cltobcur(); /* clears from current
+ pos to bottom.
+ */
+ break;
+ case 1:
+ kd_cltopcur(); /* clears from top to
+ current pos.
+ */
+ break;
+ case 2:
+ kd_cls();
+ break;
+ default:
+ break;
+ }
+ esc_spt = esc_seq; /* reset it */
+ break;
+ case 'K':
+ switch(number) {
+ case DEFAULT:
+ case 0:
+ kd_cltoecur(); /* clears from current
+ pos to eoln.
+ */
+ break;
+ case 1:
+ kd_clfrbcur(); /* clears from begin
+ of line to current
+ pos.
+ */
+ break;
+ case 2:
+ kd_eraseln(); /* clear entire line */
+ break;
+ default:
+ break;
+ }
+ esc_spt = esc_seq;
+ break;
+ case 'L':
+ if (number == DEFAULT)
+ kd_insln(1);
+ else
+ kd_insln(number);
+ esc_spt = esc_seq;
+ break;
+ case 'M':
+ if (number == DEFAULT)
+ kd_delln(1);
+ else
+ kd_delln(number);
+ esc_spt = esc_seq;
+ break;
+ case 'P':
+ if (number == DEFAULT)
+ kd_delch(1);
+ else
+ kd_delch(number);
+ esc_spt = esc_seq;
+ break;
+ case 'S':
+ if (number == DEFAULT)
+ kd_scrollup();
+ else
+ while (number--)
+ kd_scrollup();
+ esc_spt = esc_seq;
+ break;
+ case 'T':
+ if (number == DEFAULT)
+ kd_scrolldn();
+ else
+ while (number--)
+ kd_scrolldn();
+ esc_spt = esc_seq;
+ break;
+ case 'X':
+ if (number == DEFAULT)
+ kd_erase(1);
+ else
+ kd_erase(number);
+ esc_spt = esc_seq;
+ break;
+ case '\0':
+ break; /* not enough yet */
+ default:
+ kd_putc(*cp); /* show inv character */
+ esc_spt = esc_seq; /* inv entry, reset */
+ break;
+ }
+ return;
+}
+
+/*
+ * kd_atoi:
+ *
+ * This function converts an ascii string into an integer, and
+ * returns DEFAULT if no integer was found. Note that this is why
+ * we don't use the regular atio(), because ZERO is ZERO and not
+ * the DEFAULT in all cases.
+ *
+ * input : string
+ * output : a number or possibly DEFAULT, and the count of characters
+ * consumed by the conversion
+ *
+ */
+int
+kd_atoi(cp, nump)
+u_char *cp;
+int *nump;
+{
+ int number;
+ u_char *original;
+
+ original = cp;
+ for (number = 0; ('0' <= *cp) && (*cp <= '9'); cp++)
+ number = (number * 10) + (*cp - '0');
+ if (original == cp)
+ *nump = DEFAULT;
+ else
+ *nump = number;
+ return(cp - original);
+}
+
+kd_tab()
+{
+ int i;
+
+ for (i = 8 - (CURRENT_COLUMN(kd_curpos) % 8); i > 0; i--) {
+ kd_putc(' ');
+ }
+
+}
+
+
+/*
+ * kd_cls:
+ *
+ * This function clears the screen with spaces and the current attribute.
+ *
+ * input : None
+ * output : Screen is cleared
+ *
+ */
+kd_cls()
+{
+ (*kd_dclear)(0, ONE_PAGE/ONE_SPACE, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_home:
+ *
+ * This function will move the cursor to the home position on the screen,
+ * as well as set the internal cursor position (kd_curpos) to home.
+ *
+ * input : None
+ * output : Cursor position is moved
+ *
+ */
+kd_home()
+{
+ kd_setpos(0);
+ return;
+}
+
+
+/*
+ * kd_up:
+ *
+ * This function moves the cursor up one line position.
+ *
+ * input : None
+ * output : Cursor moves up one line, or screen is scrolled
+ *
+ */
+kd_up()
+{
+ if (kd_curpos < ONE_LINE)
+ kd_scrolldn();
+ else
+ kd_setpos(kd_curpos - ONE_LINE);
+ return;
+}
+
+
+/*
+ * kd_down:
+ *
+ * This function moves the cursor down one line position.
+ *
+ * input : None
+ * output : Cursor moves down one line or the screen is scrolled
+ *
+ */
+kd_down()
+{
+ if (kd_curpos >= (ONE_PAGE - ONE_LINE))
+ kd_scrollup();
+ else
+ kd_setpos(kd_curpos + ONE_LINE);
+ return;
+}
+
+
+/*
+ * kd_right:
+ *
+ * This function moves the cursor one position to the right.
+ *
+ * input : None
+ * output : Cursor moves one position to the right
+ *
+ */
+kd_right()
+{
+ if (kd_curpos < (ONE_PAGE - ONE_SPACE))
+ kd_setpos(kd_curpos + ONE_SPACE);
+ else {
+ kd_scrollup();
+ kd_setpos(BEG_OF_LINE(kd_curpos));
+ }
+ return;
+}
+
+
+/*
+ * kd_left:
+ *
+ * This function moves the cursor one position to the left.
+ *
+ * input : None
+ * output : Cursor moves one position to the left
+ *
+ */
+kd_left()
+{
+ if (0 < kd_curpos)
+ kd_setpos(kd_curpos - ONE_SPACE);
+ return;
+}
+
+
+/*
+ * kd_cr:
+ *
+ * This function moves the cursor to the beginning of the current
+ * line.
+ *
+ * input : None
+ * output : Cursor moves to the beginning of the current line
+ *
+ */
+kd_cr()
+{
+ kd_setpos(BEG_OF_LINE(kd_curpos));
+ return;
+}
+
+
+/*
+ * kd_cltobcur:
+ *
+ * This function clears from the current cursor position to the bottom
+ * of the screen.
+ *
+ * input : None
+ * output : Screen is cleared from current cursor postion to bottom
+ *
+ */
+kd_cltobcur()
+{
+ csrpos_t start;
+ int count;
+
+ start = kd_curpos;
+ count = (ONE_PAGE - kd_curpos)/ONE_SPACE;
+ (*kd_dclear)(start, count, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_cltopcur:
+ *
+ * This function clears from the current cursor position to the top
+ * of the screen.
+ *
+ * input : None
+ * output : Screen is cleared from current cursor postion to top
+ *
+ */
+kd_cltopcur()
+{
+ int count;
+
+ count = (kd_curpos + ONE_SPACE) / ONE_SPACE;
+ (*kd_dclear)(0, count, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_cltoecur:
+ *
+ * This function clears from the current cursor position to eoln.
+ *
+ * input : None
+ * output : Line is cleared from current cursor position to eoln
+ *
+ */
+kd_cltoecur()
+{
+ csrpos_t i;
+ csrpos_t hold;
+
+ hold = BEG_OF_LINE(kd_curpos) + ONE_LINE;
+ for (i = kd_curpos; i < hold; i += ONE_SPACE) {
+ (*kd_dput)(i, K_SPACE, kd_attr);
+ }
+}
+
+
+/*
+ * kd_clfrbcur:
+ *
+ * This function clears from the beginning of the line to the current
+ * cursor position.
+ *
+ * input : None
+ * output : Line is cleared from beginning to current position
+ *
+ */
+kd_clfrbcur()
+{
+ csrpos_t i;
+
+ for (i = BEG_OF_LINE(kd_curpos); i <= kd_curpos; i += ONE_SPACE) {
+ (*kd_dput)(i, K_SPACE, kd_attr);
+ }
+}
+
+
+/*
+ * kd_delln:
+ *
+ * This function deletes 'number' lines on the screen by effectively
+ * scrolling the lines up and replacing the old lines with spaces.
+ *
+ * input : number of lines to delete
+ * output : lines appear to be deleted
+ *
+ */
+kd_delln(number)
+int number;
+{
+ csrpos_t to;
+ csrpos_t from;
+ int delbytes; /* num of bytes to delete */
+ int count; /* num of words to move or fill */
+
+ if (number <= 0)
+ return;
+
+ delbytes = number * ONE_LINE;
+ to = BEG_OF_LINE(kd_curpos);
+ if (to + delbytes >= ONE_PAGE)
+ delbytes = ONE_PAGE - to;
+ if (to + delbytes < ONE_PAGE) {
+ from = to + delbytes;
+ count = (ONE_PAGE - from) / ONE_SPACE;
+ (*kd_dmvup)(from, to, count);
+ }
+
+ to = ONE_PAGE - delbytes;
+ count = delbytes / ONE_SPACE;
+ (*kd_dclear)(to, count, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_insln:
+ *
+ * This function inserts a line above the current one by
+ * scrolling the current line and all the lines below it down.
+ *
+ * input : number of lines to insert
+ * output : New lines appear to be inserted
+ *
+ */
+kd_insln(number)
+int number;
+{
+ csrpos_t to;
+ csrpos_t from;
+ int count;
+ csrpos_t top; /* top of block to be moved */
+ int insbytes; /* num of bytes inserted */
+
+ if (number <= 0)
+ return;
+
+ top = BEG_OF_LINE(kd_curpos);
+ insbytes = number * ONE_LINE;
+ if (top + insbytes > ONE_PAGE)
+ insbytes = ONE_PAGE - top;
+ to = ONE_PAGE - ONE_SPACE;
+ from = to - insbytes;
+ if (from > top) {
+ count = (from - top + ONE_SPACE) / ONE_SPACE;
+ (*kd_dmvdown)(from, to, count);
+ }
+
+ count = insbytes / ONE_SPACE;
+ (*kd_dclear)(top, count, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_delch:
+ *
+ * This function deletes a number of characters from the current
+ * position in the line.
+ *
+ * input : number of characters to delete
+ * output : characters appear to be deleted
+ *
+ */
+kd_delch(number)
+int number;
+{
+ int count; /* num words moved/filled */
+ int delbytes; /* bytes to delete */
+ register csrpos_t to;
+ csrpos_t from;
+ csrpos_t nextline; /* start of next line */
+
+ if (number <= 0)
+ return;
+
+ nextline = BEG_OF_LINE(kd_curpos) + ONE_LINE;
+ delbytes = number * ONE_SPACE;
+ if (kd_curpos + delbytes > nextline)
+ delbytes = nextline - kd_curpos;
+ if (kd_curpos + delbytes < nextline) {
+ from = kd_curpos + delbytes;
+ to = kd_curpos;
+ count = (nextline - from) / ONE_SPACE;
+ (*kd_dmvup)(from, to, count);
+ }
+
+ to = nextline - delbytes;
+ count = delbytes / ONE_SPACE;
+ (*kd_dclear)(to, count, kd_attr);
+ return;
+
+}
+
+
+/*
+ * kd_erase:
+ *
+ * This function overwrites characters with a space starting with the
+ * current cursor position and ending in number spaces away.
+ *
+ * input : number of characters to erase
+ * output : characters appear to be blanked or erased
+ *
+ */
+kd_erase(number)
+int number;
+{
+ csrpos_t i;
+ csrpos_t stop;
+
+ stop = kd_curpos + (ONE_SPACE * number);
+ if (stop > BEG_OF_LINE(kd_curpos) + ONE_LINE)
+ stop = BEG_OF_LINE(kd_curpos) + ONE_LINE;
+ for (i = kd_curpos; i < stop; i += ONE_SPACE) {
+ (*kd_dput)(i, K_SPACE, kd_attr);
+ }
+ return;
+}
+
+
+/*
+ * kd_eraseln:
+ *
+ * This function erases the current line with spaces.
+ *
+ * input : None
+ * output : Current line is erased
+ *
+ */
+kd_eraseln()
+{
+ csrpos_t i;
+ csrpos_t stop;
+
+ stop = BEG_OF_LINE(kd_curpos) + ONE_LINE;
+ for (i = BEG_OF_LINE(kd_curpos); i < stop; i += ONE_SPACE) {
+ (*kd_dput)(i, K_SPACE, kd_attr);
+ }
+ return;
+}
+
+
+/*
+ * kd_insch:
+ *
+ * This function inserts a blank at the current cursor position
+ * and moves all other characters on the line over.
+ *
+ * input : number of blanks to insert
+ * output : Blanks are inserted at cursor position
+ *
+ */
+kd_insch(number)
+int number;
+{
+ csrpos_t to;
+ csrpos_t from;
+ int count;
+ csrpos_t nextline; /* start of next line */
+ int insbytes; /* num of bytes inserted */
+
+ if (number <= 0)
+ return;
+
+ nextline = BEG_OF_LINE(kd_curpos) + ONE_LINE;
+ insbytes = number * ONE_SPACE;
+ if (kd_curpos + insbytes > nextline)
+ insbytes = nextline - kd_curpos;
+
+ to = nextline - ONE_SPACE;
+ from = to - insbytes;
+ if (from >= kd_curpos) {
+ count = (from - kd_curpos + ONE_SPACE) / ONE_SPACE;
+ (*kd_dmvdown)(from, to, count);
+ }
+
+ count = insbytes / ONE_SPACE;
+ (*kd_dclear)(kd_curpos, count, kd_attr);
+ return;
+}
+
+
+/*
+ * kd_isupper, kd_islower:
+ *
+ * Didn't want to include ctype.h because it brings in stdio.h, and
+ * only want to see if the darn character is uppercase or lowercase.
+ *
+ * input : Character 'c'
+ * output : isuuper gives TRUE if character is uppercase, islower
+ * returns TRUE if character is lowercase
+ *
+ */
+kd_isupper(c)
+u_char c;
+{
+ if (('A' <= c) && (c <= 'Z'))
+ return(TRUE);
+ return(FALSE);
+}
+
+kd_islower(c)
+u_char c;
+{
+ if (('a' <= c) && (c <= 'z'))
+ return(TRUE);
+ return(FALSE);
+}
+
+/*
+ * kd_senddata:
+ *
+ * This function sends a byte to the keyboard RDWR port, but
+ * first waits until the input/output data buffer is clear before
+ * sending the data. Note that this byte can be either data or a
+ * keyboard command.
+ *
+ */
+kd_senddata(ch)
+unsigned char ch;
+{
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_RDWR, ch);
+ last_sent = ch;
+ return;
+}
+
+/*
+ * kd_sendcmd:
+ *
+ * This function sends a command byte to the keyboard command
+ * port, but first waits until the input/output data buffer is
+ * clear before sending the data.
+ *
+ */
+kd_sendcmd(ch)
+unsigned char ch;
+{
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_CMD, ch);
+ return;
+}
+
+
+/*
+ * kd_getdata:
+ *
+ * This function returns a data byte from the keyboard RDWR port,
+ * after waiting until the port is flagged as having something to
+ * read.
+ */
+unsigned char
+kd_getdata()
+{
+ while ((inb(K_STATUS) & K_OBUF_FUL) == 0);
+ return(inb(K_RDWR));
+}
+
+kd_cmdreg_read()
+{
+int ch=KC_CMD_READ;
+
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_CMD, ch);
+
+ while ((inb(K_STATUS) & K_OBUF_FUL) == 0);
+ return(inb(K_RDWR));
+}
+
+kd_cmdreg_write(val)
+{
+int ch=KC_CMD_WRITE;
+
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_CMD, ch);
+
+ while (inb(K_STATUS) & K_IBUF_FUL);
+ outb(K_RDWR, val);
+}
+
+kd_mouse_drain()
+{
+ int i;
+ while(inb(K_STATUS) & K_IBUF_FUL);
+ while((i = inb(K_STATUS)) & K_OBUF_FUL)
+ printf("kbd: S = %x D = %x\n", i, inb(K_RDWR));
+}
+
+/*
+ * set_kd_state:
+ *
+ * Set kd_state and update the keyboard status LEDs.
+ */
+
+set_kd_state(newstate)
+int newstate;
+{
+ kd_state = newstate;
+ kd_setleds1(state2leds(newstate));
+}
+
+/*
+ * state2leds:
+ *
+ * Return a byte containing LED settings for the keyboard, given
+ * a state vector.
+ */
+u_char
+state2leds(state)
+int state;
+{
+ u_char result = 0;
+
+ if (state & KS_NLKED)
+ result |= K_LED_NUMLK;
+ if (state & KS_CLKED)
+ result |= K_LED_CAPSLK;
+ return(result);
+}
+
+/*
+ * kd_setleds[12]:
+ *
+ * Set the keyboard LEDs according to the given byte.
+ */
+kd_setleds1(val)
+u_char val;
+{
+ if (kd_ack != NOT_WAITING) {
+ printf("kd_setleds1: unexpected state (%d)\n", kd_ack);
+ return;
+ }
+
+ kd_ack = SET_LEDS;
+ kd_nextled = val;
+ kd_senddata(K_CMD_LEDS);
+}
+
+kd_setleds2()
+{
+ kd_senddata(kd_nextled);
+}
+
+
+/*
+ * cnsetleds:
+ *
+ * like kd_setleds[12], but not interrupt-based.
+ * Currently disabled because cngetc ignores caps lock and num
+ * lock anyway.
+ */
+cnsetleds(val)
+u_char val;
+{
+ kd_senddata(K_CMD_LEDS);
+ (void)kd_getdata(); /* XXX - assume is ACK */
+ kd_senddata(val);
+ (void)kd_getdata(); /* XXX - assume is ACK */
+}
+
+kdreboot()
+{
+ (*kd_dreset)();
+
+#ifndef BROKEN_KEYBOARD_RESET
+ kd_sendcmd(0xFE); /* XXX - magic # */
+ delay(1000000); /* wait to see if anything happens */
+#endif
+
+ /*
+ * If that didn't work, then we'll just have to try and
+ * do it the hard way.
+ */
+ cpu_shutdown();
+}
+
+static int which_button[] = {0, MOUSE_LEFT, MOUSE_MIDDLE, MOUSE_RIGHT};
+static struct mouse_motion moved;
+
+kd_kbd_magic(scancode)
+{
+int new_button = 0;
+
+ if (kd_kbd_mouse == 2)
+ printf("sc = %x\n", scancode);
+
+ switch (scancode) {
+/* f1 f2 f3 */
+ case 0x3d:
+ new_button++;
+ case 0x3c:
+ new_button++;
+ case 0x3b:
+ new_button++;
+ if (kd_kbd_magic_button && (new_button != kd_kbd_magic_button)) {
+ /* down w/o up */
+ mouse_button(which_button[kd_kbd_magic_button], 1);
+ }
+ /* normal */
+ if (kd_kbd_magic_button == new_button) {
+ mouse_button(which_button[new_button], 1);
+ kd_kbd_magic_button = 0;
+ } else {
+ mouse_button(which_button[new_button], 0);
+ kd_kbd_magic_button = new_button;
+ }
+ break;
+
+/* right left up down */
+ case 0x4d:
+ moved.mm_deltaX = kd_kbd_magic_scale;
+ moved.mm_deltaY = 0;
+ mouse_moved(moved);
+ break;
+ case 0x4b:
+ moved.mm_deltaX = -kd_kbd_magic_scale;
+ moved.mm_deltaY = 0;
+ mouse_moved(moved);
+ break;
+ case 0x48:
+ moved.mm_deltaX = 0;
+ moved.mm_deltaY = kd_kbd_magic_scale;
+ mouse_moved(moved);
+ break;
+ case 0x50:
+ moved.mm_deltaX = 0;
+ moved.mm_deltaY = -kd_kbd_magic_scale;
+ mouse_moved(moved);
+ break;
+/* home pageup end pagedown */
+ case 0x47:
+ moved.mm_deltaX = -2*kd_kbd_magic_scale;
+ moved.mm_deltaY = 2*kd_kbd_magic_scale;
+ mouse_moved(moved);
+ break;
+ case 0x49:
+ moved.mm_deltaX = 2*kd_kbd_magic_scale;
+ moved.mm_deltaY = 2*kd_kbd_magic_scale;
+ mouse_moved(moved);
+ break;
+ case 0x4f:
+ moved.mm_deltaX = -2*kd_kbd_magic_scale;
+ moved.mm_deltaY = -2*kd_kbd_magic_scale;
+ mouse_moved(moved);
+ break;
+ case 0x51:
+ moved.mm_deltaX = 2*kd_kbd_magic_scale;
+ moved.mm_deltaY = -2*kd_kbd_magic_scale;
+ mouse_moved(moved);
+ break;
+
+ default:
+ return 0;
+ }
+ return 1;
+}
+
+
+
+/*
+ * Code specific to EGA/CGA/VGA boards. This code relies on the fact
+ * that the "slam" functions take a word count and ONE_SPACE takes up
+ * 1 word.
+ */
+#define SLAMBPW 2 /* bytes per word for "slam" fcns */
+
+
+/*
+ * kd_xga_init:
+ *
+ * Initialization specific to character-based graphics adapters.
+ */
+void
+kd_xga_init()
+{
+ csrpos_t xga_getpos();
+ unsigned char screen;
+
+ outb(CMOS_ADDR, CMOS_EB);
+ screen = inb(CMOS_DATA) & CM_SCRMSK;
+ switch(screen) {
+ case CM_EGA_VGA:
+ /*
+ * Here we'll want to query to bios on the card
+ * itself, because then we can figure out what
+ * type we have exactly. At this point we only
+ * know that the card is NOT CGA or MONO. For
+ * now, however, we assume backwards compatibility
+ * with 0xb8000 as the starting screen offset
+ * memory location for these cards.
+ *
+ */
+
+ vid_start = (u_char *)phystokv(EGA_START);
+ kd_index_reg = EGA_IDX_REG;
+ kd_io_reg = EGA_IO_REG;
+ kd_lines = 25;
+ kd_cols = 80;
+ kd_bitmap_start = 0xa0000; /* XXX - magic numbers */
+ { /* XXX - is there a cleaner way to do this? */
+ char *addr = (char *)phystokv(kd_bitmap_start);
+ int i;
+ for (i = 0; i < 200; i++)
+ addr[i] = 0x00;
+ }
+ break;
+ case CM_CGA_40:
+ vid_start = (u_char *)phystokv(CGA_START);
+ kd_index_reg = CGA_IDX_REG;
+ kd_io_reg = CGA_IO_REG;
+ kd_lines = 25;
+ kd_cols = 40;
+ break;
+ case CM_CGA_80:
+ vid_start = (u_char *)phystokv(CGA_START);
+ kd_index_reg = CGA_IDX_REG;
+ kd_io_reg = CGA_IO_REG;
+ kd_lines = 25;
+ kd_cols = 80;
+ break;
+ case CM_MONO_80:
+ vid_start = (u_char *)phystokv(MONO_START);
+ kd_index_reg = MONO_IDX_REG;
+ kd_io_reg = MONO_IO_REG;
+ kd_lines = 25;
+ kd_cols = 80;
+ break;
+ default:
+ printf("kd: unknown screen type, defaulting to EGA\n");
+ }
+
+ kd_setpos(xga_getpos());
+}
+
+
+/*
+ * xga_getpos:
+ *
+ * This function returns the current hardware cursor position on the
+ * screen, scaled for compatibility with kd_curpos.
+ *
+ * input : None
+ * output : returns the value of cursor position on screen
+ *
+ */
+csrpos_t
+xga_getpos()
+
+{
+ unsigned char low;
+ unsigned char high;
+ short pos;
+
+ outb(kd_index_reg, C_HIGH);
+ high = inb(kd_io_reg);
+ outb(kd_index_reg, C_LOW);
+ low = inb(kd_io_reg);
+ pos = (0xff&low) + ((unsigned short)high<<8);
+
+ return(ONE_SPACE * (csrpos_t)pos);
+}
+
+
+/*
+ * charput:
+ *
+ * Put attributed character for EGA/CGA/etc.
+ */
+static void
+charput(pos, ch, chattr)
+csrpos_t pos; /* where to put it */
+char ch; /* the character */
+char chattr; /* its attribute */
+{
+ *(vid_start + pos) = ch;
+ *(vid_start + pos + 1) = chattr;
+}
+
+
+/*
+ * charsetcursor:
+ *
+ * Set hardware cursor position for EGA/CGA/etc.
+ */
+static void
+charsetcursor(newpos)
+csrpos_t newpos;
+{
+ short curpos; /* position, not scaled for attribute byte */
+
+ curpos = newpos / ONE_SPACE;
+ outb(kd_index_reg, C_HIGH);
+ outb(kd_io_reg, (u_char)(curpos>>8));
+ outb(kd_index_reg, C_LOW);
+ outb(kd_io_reg, (u_char)(curpos&0xff));
+
+ kd_curpos = newpos;
+}
+
+
+/*
+ * charmvup:
+ *
+ * Block move up for EGA/CGA/etc.
+ */
+static void
+charmvup(from, to, count)
+csrpos_t from, to;
+int count;
+{
+ kd_slmscu(vid_start+from, vid_start+to, count);
+}
+
+
+/*
+ * charmvdown:
+ *
+ * Block move down for EGA/CGA/etc.
+ */
+static void
+charmvdown(from, to, count)
+csrpos_t from, to;
+int count;
+{
+ kd_slmscd(vid_start+from, vid_start+to, count);
+}
+
+
+/*
+ * charclear:
+ *
+ * Fast clear for CGA/EGA/etc.
+ */
+static void
+charclear(to, count, chattr)
+csrpos_t to;
+int count;
+char chattr;
+{
+ kd_slmwd(vid_start+to, count, ((unsigned short)chattr<<8)+K_SPACE);
+}
+
+
+/*
+ * kd_noopreset:
+ *
+ * No-op reset routine for kd_dreset.
+ */
+static void
+kd_noopreset()
+{
+}
+
+
+
+/*
+ * Generic routines for bitmap devices (i.e., assume no hardware
+ * assist). Assumes a simple byte ordering (i.e., a byte at a lower
+ * address is to the left of the byte at the next higher address).
+ * For the 82786, this works anyway if the characters are 2 bytes
+ * wide. (more bubble gum and paper clips.)
+ *
+ * See the comments above about SLAMBPW.
+ */
+
+void bmpch2bit(), bmppaintcsr();
+u_char *bit2fbptr();
+
+
+/*
+ * bmpput: Copy a character from the font to the frame buffer.
+ */
+
+void
+bmpput(pos, ch, chattr)
+csrpos_t pos;
+char ch, chattr;
+{
+ short xbit, ybit; /* u/l corner of char pos */
+ register u_char *to, *from;
+ register short i, j;
+ u_char mask = (chattr == KA_REVERSE ? 0xff : 0);
+
+ if ((u_char)ch >= chars_in_font)
+ ch = K_QUES;
+
+ bmpch2bit(pos, &xbit, &ybit);
+ to = bit2fbptr(xbit, ybit);
+ from = font_start + ch * char_byte_width;
+ for (i = 0; i < char_height; ++i) {
+ for (j = 0; j < char_byte_width; ++j)
+ *(to+j) = *(from+j) ^ mask;
+ to += fb_byte_width;
+ from += font_byte_width;
+ }
+}
+
+/*
+ * bmpcp1char: copy 1 char from one place in the frame buffer to
+ * another.
+ */
+void
+bmpcp1char(from, to)
+csrpos_t from, to;
+{
+ short from_xbit, from_ybit;
+ short to_xbit, to_ybit;
+ register u_char *tp, *fp;
+ register short i, j;
+
+ bmpch2bit(from, &from_xbit, &from_ybit);
+ bmpch2bit(to, &to_xbit, &to_ybit);
+
+ tp = bit2fbptr(to_xbit, to_ybit);
+ fp = bit2fbptr(from_xbit, from_ybit);
+
+ for (i = 0; i < char_height; ++i) {
+ for (j = 0; j < char_byte_width; ++j)
+ *(tp+j) = *(fp+j);
+ tp += fb_byte_width;
+ fp += fb_byte_width;
+ }
+}
+
+/*
+ * bmpvmup: Copy a block of character positions upwards.
+ */
+void
+bmpmvup(from, to, count)
+csrpos_t from, to;
+int count;
+{
+ short from_xbit, from_ybit;
+ short to_xbit, to_ybit;
+ short i;
+
+ bmpch2bit(from, &from_xbit, &from_ybit);
+ bmpch2bit(to, &to_xbit, &to_ybit);
+
+ if (from_xbit == xstart && to_xbit == xstart && count%kd_cols == 0) {
+ /* fast case - entire lines */
+ from_xbit = to_xbit = 0;
+ bmppaintcsr(kd_curpos, char_black); /* don't copy cursor */
+ count /= kd_cols; /* num lines */
+ count *= fb_byte_width * (char_height+cursor_height);
+ kd_slmscu(bit2fbptr(from_xbit, from_ybit),
+ bit2fbptr(to_xbit, to_ybit),
+ count/SLAMBPW);
+ bmppaintcsr(kd_curpos, char_white);
+ } else {
+ /* slow case - everything else */
+ for (i=0; i < count; ++i) {
+ bmpcp1char(from, to);
+ from += ONE_SPACE;
+ to += ONE_SPACE;
+ }
+ }
+}
+
+/*
+ * bmpmvdown: copy a block of characters down.
+ */
+void
+bmpmvdown(from, to, count)
+csrpos_t from, to;
+int count;
+{
+ short from_xbit, from_ybit;
+ short to_xbit, to_ybit;
+ short i;
+
+ bmpch2bit(from, &from_xbit, &from_ybit);
+ bmpch2bit(to, &to_xbit, &to_ybit);
+
+ if (from_xbit == xstart + (kd_cols - 1) * char_width
+ && to_xbit == xstart + (kd_cols - 1) * char_width
+ && count%kd_cols == 0) {
+ /* fast case - entire lines*/
+ from_xbit = to_xbit = 8 * (fb_byte_width - 1);
+ /* last byte on line */
+ bmppaintcsr(kd_curpos, char_black); /* don't copy cursor */
+ count /= kd_cols; /* num lines */
+ count *= fb_byte_width * (char_height+cursor_height);
+ kd_slmscd(bit2fbptr(from_xbit, from_ybit),
+ bit2fbptr(to_xbit, to_ybit),
+ count/SLAMBPW);
+ bmppaintcsr(kd_curpos, char_white);
+ } else {
+ /* slow case - everything else */
+ for (i=0; i < count; ++i) {
+ bmpcp1char(from, to);
+ from -= ONE_SPACE;
+ to -= ONE_SPACE;
+ }
+ }
+}
+
+/*
+ * bmpclear: clear one or more character positions.
+ */
+void
+bmpclear(to, count, chattr)
+csrpos_t to; /* 1st char */
+int count; /* num chars */
+char chattr; /* reverse or normal */
+{
+ register short i;
+ u_short clearval;
+ u_short clearbyte = (chattr == KA_REVERSE ? char_white : char_black);
+
+ clearval = (u_short)(clearbyte<<8) + clearbyte;
+ if (to == 0 && count >= kd_lines * kd_cols) {
+ /* fast case - entire page */
+ kd_slmwd(vid_start, (fb_byte_width * fb_height)/SLAMBPW,
+ clearval);
+ } else
+ /* slow case */
+ for (i = 0; i < count; ++i) {
+ bmpput(to, K_SPACE, chattr);
+ to += ONE_SPACE;
+ }
+}
+
+/*
+ * bmpsetcursor: update the display and set the logical cursor.
+ */
+void
+bmpsetcursor(pos)
+csrpos_t pos;
+{
+ /* erase old cursor & paint new one */
+ bmppaintcsr(kd_curpos, char_black);
+ bmppaintcsr(pos, char_white);
+ kd_curpos = pos;
+}
+
+/*
+ * bmppaintcsr: paint cursor bits.
+ */
+void
+bmppaintcsr(pos, val)
+csrpos_t pos;
+u_char val;
+{
+ short xbit, ybit;
+ register u_char *cp;
+ register short line, byte;
+
+ bmpch2bit(pos, &xbit, &ybit);
+ ybit += char_height; /* position at bottom of line */
+ cp = bit2fbptr(xbit, ybit);
+ for (line = 0; line < cursor_height; ++line) {
+ for (byte = 0; byte < char_byte_width; ++byte)
+ *(cp+byte) = val;
+ cp += fb_byte_width;
+ }
+}
+
+/*
+ * bmpch2bit: convert character position to x and y bit addresses.
+ * (0, 0) is the upper left corner.
+ */
+void
+bmpch2bit(pos, xb, yb)
+csrpos_t pos;
+short *xb, *yb; /* x, y bit positions, u/l corner */
+{
+ register short xch, ych;
+
+ xch = (pos / ONE_SPACE) % kd_cols;
+ ych = pos / (ONE_SPACE * kd_cols);
+ *xb = xstart + xch * char_width;
+ *yb = ystart + ych * (char_height + cursor_height);
+}
+
+/*
+ * bit2fbptr: return a pointer into the frame buffer corresponding to
+ * the bit address (x, y).
+ * Assumes that xb and yb don't point to the middle of a
+ * byte.
+ */
+u_char *
+bit2fbptr(xb, yb)
+short xb, yb;
+{
+ return(vid_start + yb * fb_byte_width + xb/8);
+}
+
+
+/*
+ * console stuff
+ */
+
+/*
+ * XXX we assume that pcs *always* have a console
+ */
+int
+kdcnprobe(struct consdev *cp)
+{
+ int maj, unit, pri;
+
+ maj = 0;
+ unit = 0;
+ pri = CN_INTERNAL;
+
+ cp->cn_dev = makedev(maj, unit);
+ cp->cn_pri = pri;
+}
+
+int
+kdcninit(struct consdev *cp)
+{
+ kdinit();
+ return 0;
+}
+
+int
+kdcngetc(dev_t dev, int wait)
+{
+ if (wait) {
+ int c;
+ while ((c = kdcnmaygetc()) < 0)
+ continue;
+ return c;
+ }
+ else
+ return kdcnmaygetc();
+}
+
+int
+kdcnputc(dev_t dev, int c)
+{
+ int i;
+
+ if (!kd_initialized)
+ return;
+
+ /* Note that tab is handled in kd_putc */
+ if (c == '\n')
+ kd_putc('\r');
+ kd_putc(c);
+}
+
+/*
+ * kdcnmaygetc:
+ *
+ * Get one character using polling, rather than interrupts. Used
+ * by the kernel debugger. Note that Caps Lock is ignored.
+ * Normally this routine is called with interrupts already
+ * disabled, but there is code in place so that it will be more
+ * likely to work even if interrupts are turned on.
+ */
+int
+kdcnmaygetc(void)
+{
+ unsigned char c;
+ unsigned char scancode;
+ unsigned int char_idx;
+#ifdef notdef
+ spl_t o_pri;
+#endif
+ boolean_t up;
+
+ if (! kd_initialized)
+ return -1;
+
+ kd_extended = FALSE;
+#ifdef notdef
+ o_pri = splhi();
+#endif
+ for ( ; ; ) {
+ if (!(inb(K_STATUS) & K_OBUF_FUL))
+ return -1;
+
+ up = FALSE;
+ /*
+ * We'd come here for mouse events in debugger, if
+ * the mouse were on.
+ */
+ if ((inb(K_STATUS) & 0x20) == 0x20) {
+ printf("M%xP", inb(K_RDWR));
+ continue;
+ }
+ scancode = inb(K_RDWR);
+ /*
+ * Handle extend modifier and
+ * ack/resend, otherwise we may never receive
+ * a key.
+ */
+ if (scancode == K_EXTEND) {
+ kd_extended = TRUE;
+ continue;
+ } else if (scancode == K_RESEND) {
+ printf("cngetc: resend");
+ kd_resend();
+ continue;
+ } else if (scancode == K_ACKSC) {
+ printf("cngetc: handle_ack");
+ kd_handle_ack();
+ continue;
+ }
+ if (scancode & K_UP) {
+ up = TRUE;
+ scancode &= ~K_UP;
+ }
+ if (kd_kbd_mouse)
+ kd_kbd_magic(scancode);
+ if (scancode < NUMKEYS) {
+ /* Lookup in map, then process. */
+ char_idx = kdstate2idx(kd_state, kd_extended);
+ c = key_map[scancode][char_idx];
+ if (c == K_SCAN) {
+ c = key_map[scancode][++char_idx];
+ kd_state = do_modifier(kd_state, c, up);
+#ifdef notdef
+ cnsetleds(state2leds(kd_state));
+#endif
+ } else if (!up) {
+ /* regular key-down */
+ if (c == K_CR)
+ c = K_LF;
+#ifdef notdef
+ splx(o_pri);
+#endif
+ return(c & 0177);
+ }
+ }
+ }
+}
diff --git a/i386/i386at/kd.h b/i386/i386at/kd.h
new file mode 100644
index 00000000..f2d3f5ec
--- /dev/null
+++ b/i386/i386at/kd.h
@@ -0,0 +1,663 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: kd.h
+ Description: definitions for AT keyboard/display driver
+ Authors: Eugene Kuerner, Adrienne Jardetzky, Mike Kupfer
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * This file contains defines and structures that implement hardware
+ * keyboard mapping into ansi defined output codes. Note that this
+ * is structured so that "re-mapping" of actual keys is allowed at
+ * anytime during execution of the console driver. And each scan code
+ * is potentially expanded into NUMKEYS characters. Which is programmable
+ * at runtime or whenever.
+ *
+ * 02 Nov 1988 orc!eugene
+ *
+ */
+
+#ifndef _KD_H_
+#define _KD_H_
+
+#include <sys/ioctl.h>
+#include <mach/boolean.h>
+#include <sys/types.h>
+#include <sys/time.h>
+
+
+/*
+ * Where memory for various graphics adapters starts.
+ */
+#define EGA_START 0x0b8000
+#define CGA_START 0x0b8000
+#define MONO_START 0x0b0000
+
+/*
+ * Common I/O ports.
+ */
+#define K_TMR0 0x40 /* timer 0, 1, or 2 value (r/w) */
+#define K_TMR1 0x41
+#define K_TMR2 0x42
+#define K_TMRCTL 0x43 /* timer control (write-only) */
+#define K_RDWR 0x60 /* keyboard data & cmds (read/write) */
+#define K_PORTB 0x61 /* r/w. speaker & status lines */
+#define K_STATUS 0x64 /* keybd status (read-only) */
+#define K_CMD 0x64 /* keybd ctlr command (write-only) */
+
+/*
+ * I/O ports for various graphics adapters.
+ */
+#define EGA_IDX_REG 0x3d4
+#define EGA_IO_REG 0x3d5
+#define CGA_IDX_REG 0x3d4
+#define CGA_IO_REG 0x3d5
+#define MONO_IDX_REG 0x3b4
+#define MONO_IO_REG 0x3b5
+
+/*
+ * Commands sent to graphics adapter.
+ */
+#define C_LOW 0x0f /* return low byte of cursor addr */
+#define C_HIGH 0x0e /* high byte */
+
+/*
+ * Bit definitions for K_STATUS port.
+ */
+#define K_OBUF_FUL 0x01 /* output (from keybd) buffer full */
+#define K_IBUF_FUL 0x02 /* input (to keybd) buffer full */
+#define K_SYSFLAG 0x04 /* "System Flag" */
+#define K_CMD_DATA 0x08 /* 1 = input buf has cmd, 0 = data */
+#define K_KBD_INHBT 0x10 /* 0 if keyboard inhibited */
+
+/*
+ * Keyboard controller commands (sent to K_CMD port).
+ */
+#define KC_CMD_READ 0x20 /* read controller command byte */
+#define KC_CMD_WRITE 0x60 /* write controller command byte */
+#define KC_CMD_TEST 0xab /* test interface */
+#define KC_CMD_DUMP 0xac /* diagnostic dump */
+#define KC_CMD_DISBLE 0xad /* disable keyboard */
+#define KC_CMD_ENBLE 0xae /* enable keyboard */
+#define KC_CMD_RDKBD 0xc4 /* read keyboard ID */
+#define KC_CMD_ECHO 0xee /* used for diagnostic testing */
+
+/*
+ * Keyboard commands (send to K_RDWR).
+ */
+#define K_CMD_LEDS 0xed /* set status LEDs (caps lock, etc.) */
+
+/*
+ * Bit definitions for controller command byte (sent following
+ * K_CMD_WRITE command).
+ */
+#define K_CB_ENBLIRQ 0x01 /* enable data-ready intrpt */
+#define K_CB_SETSYSF 0x04 /* Set System Flag */
+#define K_CB_INHBOVR 0x08 /* Inhibit Override */
+#define K_CB_DISBLE 0x10 /* disable keyboard */
+
+/*
+ * Bit definitions for "Indicator Status Byte" (sent after a
+ * K_CMD_LEDS command). If the bit is on, the LED is on. Undefined
+ * bit positions must be 0.
+ */
+#define K_LED_SCRLLK 0x1 /* scroll lock */
+#define K_LED_NUMLK 0x2 /* num lock */
+#define K_LED_CAPSLK 0x4 /* caps lock */
+
+/*
+ * Bit definitions for "Miscellaneous port B" (K_PORTB).
+ */
+/* read/write */
+#define K_ENABLETMR2 0x01 /* enable output from timer 2 */
+#define K_SPKRDATA 0x02 /* direct input to speaker */
+#define K_ENABLEPRTB 0x04 /* "enable" port B */
+#define K_EIOPRTB 0x08 /* enable NMI on parity error */
+/* read-only */
+#define K_REFRESHB 0x10 /* refresh flag from INLTCONT PAL */
+#define K_OUT2B 0x20 /* timer 2 output */
+#define K_ICKB 0x40 /* I/O channel check (parity error) */
+
+/*
+ * Bit definitions for timer control port (K_TMRCTL).
+ */
+/* select timer 0, 1, or 2. Don't mess with 0 or 1. */
+#define K_SELTMRMASK 0xc0
+#define K_SELTMR0 0x00
+#define K_SELTMR1 0x40
+#define K_SELTMR2 0x80
+
+/* read/load control */
+#define K_RDLDTMRMASK 0x30
+#define K_HOLDTMR 0x00 /* freeze timer until read */
+#define K_RDLDTLSB 0x10 /* read/load LSB */
+#define K_RDLDTMSB 0x20 /* read/load MSB */
+#define K_RDLDTWORD 0x30 /* read/load LSB then MSB */
+
+/* mode control */
+#define K_TMDCTLMASK 0x0e
+#define K_TCOUNTINTR 0x00 /* "Term Count Intr" */
+#define K_TONESHOT 0x02 /* "Progr One-Shot" */
+#define K_TRATEGEN 0x04 /* "Rate Gen (/n)" */
+#define K_TSQRWAVE 0x06 /* "Sqr Wave Gen" */
+#define K_TSOFTSTRB 0x08 /* "Softw Trig Strob" */
+#define K_THARDSTRB 0x0a /* "Hardw Trig Strob" */
+
+/* count mode */
+#define K_TCNTMDMASK 0x01
+#define K_TBINARY 0x00 /* 16-bit binary counter */
+#define K_TBCD 0x01 /* 4-decade BCD counter */
+
+
+
+/*
+ * Fun definitions for displayed characters and characters read from
+ * the keyboard.
+ */
+
+/*
+ * Attributes for character sent to display.
+ */
+#define KA_NORMAL 0x07
+#define KA_REVERSE 0x70
+
+/*
+ * For an EGA-like display, each character takes two bytes, one for the
+ * actual character, followed by one for its attributes.
+ * Be very careful if you change ONE_SPACE, as these constants are also used
+ * to define the device-independent display implemented by kd.c.
+ * (See kdsoft.h for more details on the device-independent display.)
+ */
+#define ONE_SPACE 2 /* bytes in 1 char, EGA-like display */
+#define BOTTOM_LINE 3840 /* 1st byte in last line of display */
+#define ONE_PAGE 4000 /* number of bytes in page */
+#define ONE_LINE 160 /* number of bytes in line */
+
+#define BEG_OF_LINE(pos) ((pos) - (pos)%ONE_LINE)
+#define CURRENT_COLUMN(pos) (((pos) % ONE_LINE) / ONE_SPACE)
+
+#define NUMKEYS 89
+#define NUMSTATES 5 /* NORMAL_STATE, ... */
+#define NUMOUTPUT 3 /* max size of byte seq from key */
+#define WIDTH_KMAP (NUMSTATES * NUMOUTPUT)
+
+/*
+ * Keyboard states. Used for KDGKBENT, KDSKBENT ioctl's. If you
+ * change these values, you should also rearrange the entries in
+ * key_map.
+ */
+/* "state indices" (for computing key_map index) */
+#define NORM_STATE 0
+#define SHIFT_STATE 1
+#define CTRL_STATE 2
+#define ALT_STATE 3
+#define SHIFT_ALT 4
+/* macro to convert from state index to actual key_map index */
+#define CHARIDX(sidx) ((sidx) * NUMOUTPUT)
+ /* where sidx is in [NORM_STATE ... SHIFT_ALT] */
+
+/* "state bits" for kd_state vector */
+#define KS_NORMAL 0x00
+#define KS_SLKED 0x01
+#define KS_NLKED 0x02
+#define KS_CLKED 0x04
+#define KS_ALTED 0x08
+#define KS_SHIFTED 0x10
+#define KS_CTLED 0x20
+
+
+/*
+ * Scancode values, not to be confused with Ascii values.
+ */
+typedef u_char Scancode;
+
+/* special codes */
+#define K_UP 0x80 /* OR'd in if key below is released */
+#define K_EXTEND 0xe0 /* marker for "extended" sequence */
+#define K_ACKSC 0xfa /* ack for keyboard command */
+#define K_RESEND 0xfe /* request to resend keybd cmd */
+
+/* modifier keys */
+#define K_CTLSC 0x1d /* control down */
+#define K_LSHSC 0x2a /* left shift down */
+#define K_RSHSC 0x36 /* right shift down */
+#define K_ALTSC 0x38 /* alt key down */
+#define K_CLCKSC 0x3a /* caps lock */
+#define K_NLCKSC 0x45 /* num lock down */
+
+/* "special keys" */
+#define K_BSSC 0x0e /* backspace */
+#define K_TABSC 0x0f /* tab */
+#define K_RETSC 0x1c /* return */
+#define K_SPSC 0x39 /* space */
+#define K_ESCSC 0x01 /* ESC */
+
+/* alphabetic keys */
+#define K_qSC 0x10
+#define K_wSC 0x11
+#define K_eSC 0x12
+#define K_rSC 0x13
+#define K_tSC 0x14
+#define K_ySC 0x15
+#define K_uSC 0x16
+#define K_iSC 0x17
+#define K_oSC 0x18
+#define K_pSC 0x19
+
+#define K_aSC 0x1e
+#define K_sSC 0x1f
+#define K_dSC 0x20
+#define K_fSC 0x21
+#define K_gSC 0x22
+#define K_hSC 0x23
+#define K_jSC 0x24
+#define K_kSC 0x25
+#define K_lSC 0x26
+
+#define K_zSC 0x2c
+#define K_xSC 0x2d
+#define K_cSC 0x2e
+#define K_vSC 0x2f
+#define K_bSC 0x30
+#define K_nSC 0x31
+#define K_mSC 0x32
+
+/* numbers and punctuation */
+#define K_ONESC 0x02 /* 1 */
+#define K_TWOSC 0x03 /* 2 */
+#define K_THREESC 0x04 /* 3 */
+#define K_FOURSC 0x05 /* 4 */
+#define K_FIVESC 0x06 /* 5 */
+#define K_SIXSC 0x07 /* 6 */
+#define K_SEVENSC 0x08 /* 7 */
+#define K_EIGHTSC 0x09 /* 8 */
+#define K_NINESC 0x0a /* 9 */
+#define K_ZEROSC 0x0b /* 0 */
+
+#define K_MINUSSC 0x0c /* - */
+#define K_EQLSC 0x0d /* = */
+#define K_LBRKTSC 0x1a /* [ */
+#define K_RBRKTSC 0x1b /* ] */
+#define K_SEMISC 0x27 /* ; */
+#define K_SQUOTESC 0x28 /* ' */
+#define K_GRAVSC 0x29 /* ` */
+#define K_BSLSHSC 0x2b /* \ */
+#define K_COMMASC 0x33 /* , */
+#define K_PERIODSC 0x34 /* . */
+#define K_SLASHSC 0x35 /* / */
+
+/* keypad keys */
+#define K_HOMESC 0x47 /* scancode for home */
+#define K_DELSC 0x53 /* scancode for del */
+
+/*
+ * Ascii values and flag characters for key map.
+ * A function key is represented by the 3-byte char sequence that it
+ * corresponds to.
+ * Other mappable non-Ascii keys (e.g., "ctrl") are represented by a
+ * two-byte sequence: K_SCAN, followed by the key's scan code.
+ */
+#define K_DONE 0xff /* must be same as NC */
+#define NC 0xff /* No character defined */
+
+#define K_SCAN 0xfe /* followed by scan code */
+
+/* ascii char set */
+#define K_NUL 0x00 /* Null character */
+#define K_SOH 0x01
+#define K_STX 0x02
+#define K_ETX 0x03
+#define K_EOT 0x04
+#define K_ENQ 0x05
+#define K_ACK 0x06
+#define K_BEL 0x07 /* bell character */
+#define K_BS 0x08 /* back space */
+#define K_HT 0x09
+#define K_LF 0x0a /* line feed */
+#define K_VT 0x0b
+#define K_FF 0x0c
+#define K_CR 0x0d /* carriage return */
+#define K_SO 0x0e
+#define K_SI 0x0f
+#define K_DLE 0x10
+#define K_DC1 0x11
+#define K_DC2 0x12
+#define K_DC3 0x13
+#define K_DC4 0x14
+#define K_NAK 0x15
+#define K_SYN 0x16
+#define K_ETB 0x17
+#define K_CAN 0x18
+#define K_EM 0x19
+#define K_SUB 0x1a
+#define K_ESC 0x1b /* escape character */
+#define K_FS 0x1c
+#define K_GS 0x1d
+#define K_RS 0x1e
+#define K_US 0x1f
+#define K_SPACE 0x20 /* space character */
+#define K_BANG 0x21 /* ! */
+#define K_DQUOTE 0x22 /* " */
+#define K_POUND 0x23 /* # */
+#define K_DOLLAR 0x24 /* $ */
+#define K_PERC 0x25 /* % */
+#define K_AMPER 0x26 /* & */
+#define K_SQUOTE 0x27 /* ' */
+#define K_LPAREN 0x28 /* ( */
+#define K_RPAREN 0x29 /* ) */
+#define K_ASTER 0x2a /* * */
+#define K_PLUS 0x2b /* + */
+#define K_COMMA 0x2c /* , */
+#define K_MINUS 0x2d /* - */
+#define K_PERIOD 0x2e /* . */
+#define K_SLASH 0x2f /* / */
+#define K_ZERO 0x30 /* 0 */
+#define K_ONE 0x31 /* 1 */
+#define K_TWO 0x32 /* 2 */
+#define K_THREE 0x33 /* 3 */
+#define K_FOUR 0x34 /* 4 */
+#define K_FIVE 0x35 /* 5 */
+#define K_SIX 0x36 /* 6 */
+#define K_SEVEN 0x37 /* 7 */
+#define K_EIGHT 0x38 /* 8 */
+#define K_NINE 0x39 /* 9 */
+#define K_COLON 0x3a /* : */
+#define K_SEMI 0x3b /* ; */
+#define K_LTHN 0x3c /* < */
+#define K_EQL 0x3d /* = */
+#define K_GTHN 0x3e /* > */
+#define K_QUES 0x3f /* ? */
+#define K_ATSN 0x40 /* @ */
+#define K_A 0x41 /* A */
+#define K_B 0x42 /* B */
+#define K_C 0x43 /* C */
+#define K_D 0x44 /* D */
+#define K_E 0x45 /* E */
+#define K_F 0x46 /* F */
+#define K_G 0x47 /* G */
+#define K_H 0x48 /* H */
+#define K_I 0x49 /* I */
+#define K_J 0x4a /* J */
+#define K_K 0x4b /* K */
+#define K_L 0x4c /* L */
+#define K_M 0x4d /* M */
+#define K_N 0x4e /* N */
+#define K_O 0x4f /* O */
+#define K_P 0x50 /* P */
+#define K_Q 0x51 /* Q */
+#define K_R 0x52 /* R */
+#define K_S 0x53 /* S */
+#define K_T 0x54 /* T */
+#define K_U 0x55 /* U */
+#define K_V 0x56 /* V */
+#define K_W 0x57 /* W */
+#define K_X 0x58 /* X */
+#define K_Y 0x59 /* Y */
+#define K_Z 0x5a /* Z */
+#define K_LBRKT 0x5b /* [ */
+#define K_BSLSH 0x5c /* \ */
+#define K_RBRKT 0x5d /* ] */
+#define K_CARET 0x5e /* ^ */
+#define K_UNDSC 0x5f /* _ */
+#define K_GRAV 0x60 /* ` */
+#define K_a 0x61 /* a */
+#define K_b 0x62 /* b */
+#define K_c 0x63 /* c */
+#define K_d 0x64 /* d */
+#define K_e 0x65 /* e */
+#define K_f 0x66 /* f */
+#define K_g 0x67 /* g */
+#define K_h 0x68 /* h */
+#define K_i 0x69 /* i */
+#define K_j 0x6a /* j */
+#define K_k 0x6b /* k */
+#define K_l 0x6c /* l */
+#define K_m 0x6d /* m */
+#define K_n 0x6e /* n */
+#define K_o 0x6f /* o */
+#define K_p 0x70 /* p */
+#define K_q 0x71 /* q */
+#define K_r 0x72 /* r */
+#define K_s 0x73 /* s */
+#define K_t 0x74 /* t */
+#define K_u 0x75 /* u */
+#define K_v 0x76 /* v */
+#define K_w 0x77 /* w */
+#define K_x 0x78 /* x */
+#define K_y 0x79 /* y */
+#define K_z 0x7a /* z */
+#define K_LBRACE 0x7b /* { */
+#define K_PIPE 0x7c /* | */
+#define K_RBRACE 0x7d /* } */
+#define K_TILDE 0x7e /* ~ */
+#define K_DEL 0x7f /* delete */
+
+/* Ascii sequences to be generated by the named key */
+#define K_F1 0x1b,0x4f,0x50
+#define K_F1S 0x1b,0x4f,0x70
+#define K_F2 0x1b,0x4f,0x51
+#define K_F2S 0x1b,0x4f,0x71
+#define K_F3 0x1b,0x4f,0x52
+#define K_F3S 0x1b,0x4f,0x72
+#define K_F4 0x1b,0x4f,0x53
+#define K_F4S 0x1b,0x4f,0x73
+#define K_F5 0x1b,0x4f,0x54
+#define K_F5S 0x1b,0x4f,0x74
+#define K_F6 0x1b,0x4f,0x55
+#define K_F6S 0x1b,0x4f,0x75
+#define K_F7 0x1b,0x4f,0x56
+#define K_F7S 0x1b,0x4f,0x76
+#define K_F8 0x1b,0x4f,0x57
+#define K_F8S 0x1b,0x4f,0x77
+#define K_F9 0x1b,0x4f,0x58
+#define K_F9S 0x1b,0x4f,0x78
+#define K_F10 0x1b,0x4f,0x59
+#define K_F10S 0x1b,0x4f,0x79
+#define K_F11 0x1b,0x4f,0x5a
+#define K_F11S 0x1b,0x4f,0x7a
+#define K_F12 0x1b,0x4f,0x41
+#define K_F12S 0x1b,0x4f,0x61
+
+#define K_SCRL 0x1b,0x5b,0x4d
+#define K_HOME 0x1b,0x5b,0x48
+#define K_UA 0x1b,0x5b,0x41
+#define K_PUP 0x1b,0x5b,0x56
+#define K_LA 0x1b,0x5b,0x44
+#define K_RA 0x1b,0x5b,0x43
+#define K_END 0x1b,0x5b,0x59
+#define K_DA 0x1b,0x5b,0x42
+#define K_PDN 0x1b,0x5b,0x55
+#define K_INS 0x1b,0x5b,0x40
+
+
+/*
+ * This array maps scancodes to Ascii characters (or character
+ * sequences).
+ * The first index is the scancode. The first NUMOUTPUT characters
+ * (accessed using the second index) correspond to the key's char
+ * sequence for the Normal state. The next NUMOUTPUT characters
+ * are for the Shift state, then Ctrl, then Alt, then Shift/Alt.
+ */
+#ifdef KERNEL
+extern u_char key_map[NUMKEYS][WIDTH_KMAP];
+#endif /* KERNEL */
+
+
+
+/*
+ * These routines are declared here so that all the modules making
+ * up the kd driver agree on how to do locking.
+ */
+
+#ifdef KERNEL
+#include <i386/machspl.h>
+extern void splx();
+extern spl_t spltty();
+#define SPLKD spltty
+#endif /* KERNEL */
+
+
+/*
+ * Ioctl's on /dev/console.
+ */
+
+/*
+ * KDGKBENT, KDSKBENT - Get and set keyboard table entry. Useful for
+ * remapping keys.
+ *
+ * KDGSTATE - Get the keyboard state variable, which flags the
+ * modifier keys (shift, ctrl, etc.) that are down. See
+ * KS_NORMAL et al above. Used for debugging.
+ *
+ * KDSETBELL - Turns the bell on or off.
+ */
+
+#define KDGKBENT _IOWR('k', 1, struct kbentry) /* get keybd entry */
+
+#define KDSKBENT _IOW('k', 2, struct kbentry) /* set keybd entry */
+
+#define KDGSTATE _IOR('k', 3, int) /* get keybd state */
+
+#define KDSETBELL _IOW('k', 4, int) /* turn bell on or off */
+# define KD_BELLON 1
+# define KD_BELLOFF 0
+
+/*
+ * This struct is used for getting and setting key definitions. The
+ * values for kb_index are obtainable from the man page for
+ * keyboard(7) (though they should really be defined here!).
+ */
+struct kbentry {
+ u_char kb_state; /* which state to use */
+ u_char kb_index; /* which keycode */
+ u_char kb_value[NUMOUTPUT]; /* value to get/set */
+};
+
+
+/*
+ * Ioctl's on /dev/kbd.
+ */
+
+/*
+ * KDSKBDMODE - When the console is in "ascii" mode, keyboard events are
+ * converted to Ascii characters that are readable from /dev/console.
+ * When the console is in "event" mode, keyboard events are
+ * timestamped and queued up on /dev/kbd as kd_events. When the last
+ * close is done on /dev/kbd, the console automatically reverts to ascii
+ * mode.
+ * When /dev/mouse is opened, mouse events are timestamped and queued
+ * on /dev/mouse, again as kd_events.
+ *
+ * KDGKBDTYPE - Returns the type of keyboard installed. Currently
+ * there is only one type, KB_VANILLAKB, which is your standard PC-AT
+ * keyboard.
+ */
+
+#ifdef KERNEL
+extern int kb_mode;
+#endif
+
+#define KDSKBDMODE _IOW('K', 1, int) /* set keyboard mode */
+#define KB_EVENT 1
+#define KB_ASCII 2
+
+#define KDGKBDTYPE _IOR('K', 2, int) /* get keyboard type */
+#define KB_VANILLAKB 0
+
+struct X_kdb {
+ u_int *ptr;
+ u_int size;
+};
+
+#define K_X_KDB_ENTER _IOW('K', 16, struct X_kdb)
+#define K_X_KDB_EXIT _IOW('K', 17, struct X_kdb)
+
+#define K_X_IN 0x01000000
+#define K_X_OUT 0x02000000
+#define K_X_BYTE 0x00010000
+#define K_X_WORD 0x00020000
+#define K_X_LONG 0x00040000
+#define K_X_TYPE 0x03070000
+#define K_X_PORT 0x0000ffff
+
+typedef u_short kev_type; /* kd event type */
+
+/* (used for event records) */
+struct mouse_motion {
+ short mm_deltaX; /* units? */
+ short mm_deltaY;
+};
+
+typedef struct {
+ kev_type type; /* see below */
+ struct timeval time; /* timestamp */
+ union { /* value associated with event */
+ boolean_t up; /* MOUSE_LEFT .. MOUSE_RIGHT */
+ Scancode sc; /* KEYBD_EVENT */
+ struct mouse_motion mmotion; /* MOUSE_MOTION */
+ } value;
+} kd_event;
+#define m_deltaX mmotion.mm_deltaX
+#define m_deltaY mmotion.mm_deltaY
+
+/*
+ * kd_event ID's.
+ */
+#define MOUSE_LEFT 1 /* mouse left button up/down */
+#define MOUSE_MIDDLE 2
+#define MOUSE_RIGHT 3
+#define MOUSE_MOTION 4 /* mouse motion */
+#define KEYBD_EVENT 5 /* key up/down */
+
+#endif /* _KD_H_ */
diff --git a/i386/i386at/kd_event.c b/i386/i386at/kd_event.c
new file mode 100644
index 00000000..9ee43749
--- /dev/null
+++ b/i386/i386at/kd_event.c
@@ -0,0 +1,560 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: kd_event.c
+ Description: Driver for event interface to keyboard.
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1989. All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <mach/boolean.h>
+#include <sys/types.h>
+#ifdef MACH_KERNEL
+#include <device/errno.h>
+#include <device/io_req.h>
+#else MACH_KERNEL
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <kern/thread.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#endif MACH_KERNEL
+#include <i386/machspl.h>
+#include <i386at/kd.h>
+#include <i386at/kd_queue.h>
+
+/*
+ * Code for /dev/kbd. The interrupt processing is done in kd.c,
+ * which calls into this module to enqueue scancode events when
+ * the keyboard is in Event mode.
+ */
+
+/*
+ * Note: These globals are protected by raising the interrupt level
+ * via SPLKD.
+ */
+
+kd_event_queue kbd_queue; /* queue of keyboard events */
+#ifdef MACH_KERNEL
+queue_head_t kbd_read_queue = { &kbd_read_queue, &kbd_read_queue };
+#else MACH_KERNEL
+struct proc *kbd_sel = 0; /* selecting process, if any */
+short kbdpgrp = 0; /* process group leader when dev is open */
+
+int kbdflag = 0;
+#define KBD_COLL 1 /* select collision */
+#define KBD_ASYNC 2 /* user wants asynch notification */
+#define KBD_NBIO 4 /* user wants non-blocking I/O */
+#endif MACH_KERNEL
+
+
+void kbd_enqueue();
+#ifdef MACH_KERNEL
+io_return_t X_kdb_enter_init();
+io_return_t X_kdb_exit_init();
+#endif MACH_KERNEL
+
+static boolean_t initialized = FALSE;
+
+
+/*
+ * kbdinit - set up event queue.
+ */
+
+kbdinit()
+{
+ spl_t s = SPLKD();
+
+ if (!initialized) {
+ kdq_reset(&kbd_queue);
+ initialized = TRUE;
+ }
+ splx(s);
+}
+
+
+/*
+ * kbdopen - Verify that open is read-only and remember process
+ * group leader.
+ */
+
+/*ARGSUSED*/
+kbdopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ kbdinit();
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (flags & FWRITE)
+ return(ENODEV);
+
+ if (kbdpgrp == 0)
+ kbdpgrp = u.u_procp->p_pgrp;
+#endif MACH_KERNEL
+ return(0);
+}
+
+
+/*
+ * kbdclose - Make sure that the kd driver is in Ascii mode and
+ * reset various flags.
+ */
+
+/*ARGSUSED*/
+kbdclose(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ spl_t s = SPLKD();
+
+ kb_mode = KB_ASCII;
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ kbdpgrp = 0;
+ kbdflag = 0;
+ kbd_sel = 0;
+#endif MACH_KERNEL
+ kdq_reset(&kbd_queue);
+ splx(s);
+}
+
+
+#ifdef MACH_KERNEL
+io_return_t kbdgetstat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int * data; /* pointer to OUT array */
+ unsigned int *count; /* OUT */
+{
+ io_return_t result;
+
+ switch (flavor) {
+ case KDGKBDTYPE:
+ *data = KB_VANILLAKB;
+ *count = 1;
+ break;
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+io_return_t kbdsetstat(dev, flavor, data, count)
+ dev_t dev;
+ int flavor;
+ int * data;
+ unsigned int count;
+{
+ io_return_t result;
+
+ switch (flavor) {
+ case KDSKBDMODE:
+ kb_mode = *data;
+ /* XXX - what to do about unread events? */
+ /* XXX - should check that 'data' contains an OK valud */
+ break;
+ case K_X_KDB_ENTER:
+ return X_kdb_enter_init(data, count);
+ case K_X_KDB_EXIT:
+ return X_kdb_exit_init(data, count);
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (D_SUCCESS);
+}
+
+#else MACH_KERNEL
+/*
+ * kbdioctl - handling for asynch & non-blocking I/O.
+ */
+
+/*ARGSUSED*/
+kbdioctl(dev, cmd, data, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ spl_t s = SPLKD();
+ int err = 0;
+
+ switch (cmd) {
+ case KDSKBDMODE:
+ kb_mode = *(int *)data;
+ /* XXX - what to do about unread events? */
+ /* XXX - should check that "data" contains an OK value */
+ break;
+ case KDGKBDTYPE:
+ *(int *)data = KB_VANILLAKB;
+ break;
+ case K_X_KDB_ENTER:
+ X_kdb_enter_init((struct X_kdb *) data);
+ break;
+ case K_X_KDB_EXIT:
+ X_kdb_exit_init( (struct X_kdb *) data);
+ break;
+ case FIONBIO:
+ if (*(int *)data)
+ kbdflag |= KBD_NBIO;
+ else
+ kbdflag &= ~KBD_NBIO;
+ break;
+ case FIOASYNC:
+ if (*(int *)data)
+ kbdflag |= KBD_ASYNC;
+ else
+ kbdflag &= ~KBD_ASYNC;
+ break;
+ default:
+ err = ENOTTY;
+ break;
+ }
+
+ splx(s);
+ return(err);
+}
+
+
+/*
+ * kbdselect
+ */
+
+/*ARGSUSED*/
+kbdselect(dev, rw)
+{
+ spl_t s = SPLKD();
+
+ if (!kdq_empty(&kbd_queue)) {
+ splx(s);
+ return(1);
+ }
+
+ if (kbd_sel)
+ kbdflag |= KBD_COLL;
+ else
+ kbd_sel = (struct proc *)current_thread();
+ /* eeeyuck */
+
+ splx(s);
+ return(0);
+}
+#endif MACH_KERNEL
+
+
+/*
+ * kbdread - dequeue and return any queued events.
+ */
+
+#ifdef MACH_KERNEL
+boolean_t kbd_read_done(); /* forward */
+
+kbdread(dev, ior)
+ dev_t dev;
+ register io_req_t ior;
+{
+ register int err, count;
+ register spl_t s;
+
+ err = device_read_alloc(ior, (vm_size_t)ior->io_count);
+ if (err != KERN_SUCCESS)
+ return (err);
+
+ s = SPLKD();
+ if (kdq_empty(&kbd_queue)) {
+ if (ior->io_mode & D_NOWAIT) {
+ splx(s);
+ return (D_WOULD_BLOCK);
+ }
+ ior->io_done = kbd_read_done;
+ enqueue_tail(&kbd_read_queue, (queue_entry_t) ior);
+ splx(s);
+ return (D_IO_QUEUED);
+ }
+ count = 0;
+ while (!kdq_empty(&kbd_queue) && count < ior->io_count) {
+ register kd_event *ev;
+
+ ev = kdq_get(&kbd_queue);
+ *(kd_event *)(&ior->io_data[count]) = *ev;
+ count += sizeof(kd_event);
+ }
+ splx(s);
+ ior->io_residual = ior->io_count - count;
+ return (D_SUCCESS);
+}
+
+boolean_t kbd_read_done(ior)
+ register io_req_t ior;
+{
+ register int count;
+ register spl_t s;
+
+ s = SPLKD();
+ if (kdq_empty(&kbd_queue)) {
+ ior->io_done = kbd_read_done;
+ enqueue_tail(&kbd_read_queue, (queue_entry_t)ior);
+ splx(s);
+ return (FALSE);
+ }
+
+ count = 0;
+ while (!kdq_empty(&kbd_queue) && count < ior->io_count) {
+ register kd_event *ev;
+
+ ev = kdq_get(&kbd_queue);
+ *(kd_event *)(&ior->io_data[count]) = *ev;
+ count += sizeof(kd_event);
+ }
+ splx(s);
+
+ ior->io_residual = ior->io_count - count;
+ ds_read_done(ior);
+
+ return (TRUE);
+}
+
+#else MACH_KERNEL
+/*ARGSUSED*/
+kbdread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ int s = SPLKD();
+ int err = 0;
+ kd_event *ev;
+ int i;
+ char *cp;
+
+ if (kdq_empty(&kbd_queue))
+ if (kbdflag & KBD_NBIO) {
+ err = EWOULDBLOCK;
+ goto done;
+ } else
+ while (kdq_empty(&kbd_queue)) {
+ splx(s);
+ sleep((caddr_t)&kbd_queue, TTIPRI);
+ s = SPLKD();
+ }
+
+ while (!kdq_empty(&kbd_queue) && uio->uio_resid >= sizeof(kd_event)) {
+ ev = kdq_get(&kbd_queue);
+ for (cp = (char *)ev, i = 0; i < sizeof(kd_event);
+ ++i, ++cp) {
+ err = ureadc(*cp, uio);
+ if (err)
+ goto done;
+ }
+ }
+
+done:
+ splx(s);
+ return(err);
+}
+#endif MACH_KERNEL
+
+
+/*
+ * kd_enqsc - enqueue a scancode. Should be called at SPLKD.
+ */
+
+void
+kd_enqsc(sc)
+ Scancode sc;
+{
+ kd_event ev;
+
+ ev.type = KEYBD_EVENT;
+ ev.time = time;
+ ev.value.sc = sc;
+ kbd_enqueue(&ev);
+}
+
+
+/*
+ * kbd_enqueue - enqueue an event and wake up selecting processes, if
+ * any. Should be called at SPLKD.
+ */
+
+void
+kbd_enqueue(ev)
+ kd_event *ev;
+{
+ if (kdq_full(&kbd_queue))
+ printf("kbd: queue full\n");
+ else
+ kdq_put(&kbd_queue, ev);
+
+#ifdef MACH_KERNEL
+ {
+ register io_req_t ior;
+ while ((ior = (io_req_t)dequeue_head(&kbd_read_queue)) != 0)
+ iodone(ior);
+ }
+#else MACH_KERNEL
+ if (kbd_sel) {
+ selwakeup(kbd_sel, kbdflag & KBD_COLL);
+ kbd_sel = 0;
+ kbdflag &= ~KBD_COLL;
+ }
+ if (kbdflag & KBD_ASYNC)
+ gsignal(kbdpgrp, SIGIO);
+ wakeup((caddr_t)&kbd_queue);
+#endif MACH_KERNEL
+}
+
+u_int X_kdb_enter_str[512], X_kdb_exit_str[512];
+int X_kdb_enter_len = 0, X_kdb_exit_len = 0;
+
+kdb_in_out(p)
+u_int *p;
+{
+register int t = p[0];
+
+ switch (t & K_X_TYPE) {
+ case K_X_IN|K_X_BYTE:
+ inb(t & K_X_PORT);
+ break;
+
+ case K_X_IN|K_X_WORD:
+ inw(t & K_X_PORT);
+ break;
+
+ case K_X_IN|K_X_LONG:
+ inl(t & K_X_PORT);
+ break;
+
+ case K_X_OUT|K_X_BYTE:
+ outb(t & K_X_PORT, p[1]);
+ break;
+
+ case K_X_OUT|K_X_WORD:
+ outw(t & K_X_PORT, p[1]);
+ break;
+
+ case K_X_OUT|K_X_LONG:
+ outl(t & K_X_PORT, p[1]);
+ break;
+ }
+}
+
+X_kdb_enter()
+{
+register u_int *u_ip, *endp;
+
+ for (u_ip = X_kdb_enter_str, endp = &X_kdb_enter_str[X_kdb_enter_len];
+ u_ip < endp;
+ u_ip += 2)
+ kdb_in_out(u_ip);
+}
+
+X_kdb_exit()
+{
+register u_int *u_ip, *endp;
+
+ for (u_ip = X_kdb_exit_str, endp = &X_kdb_exit_str[X_kdb_exit_len];
+ u_ip < endp;
+ u_ip += 2)
+ kdb_in_out(u_ip);
+}
+
+#ifdef MACH_KERNEL
+io_return_t
+X_kdb_enter_init(data, count)
+ u_int *data;
+ u_int count;
+{
+ if (count * sizeof X_kdb_enter_str[0] > sizeof X_kdb_enter_str)
+ return D_INVALID_OPERATION;
+
+ bcopy(data, X_kdb_enter_str, count * sizeof X_kdb_enter_str[0]);
+ X_kdb_enter_len = count;
+ return D_SUCCESS;
+}
+
+io_return_t
+X_kdb_exit_init(data, count)
+ u_int *data;
+ u_int count;
+{
+ if (count * sizeof X_kdb_exit_str[0] > sizeof X_kdb_exit_str)
+ return D_INVALID_OPERATION;
+
+ bcopy(data, X_kdb_exit_str, count * sizeof X_kdb_exit_str[0]);
+ X_kdb_exit_len = count;
+ return D_SUCCESS;
+}
+#else MACH_KERNEL
+X_kdb_enter_init(kp)
+struct X_kdb *kp;
+{
+ if (kp->size > sizeof X_kdb_enter_str)
+ u.u_error = ENOENT;
+ else if(copyin(kp->ptr, X_kdb_enter_str, kp->size) == EFAULT)
+ u.u_error = EFAULT;
+
+ X_kdb_enter_len = kp->size>>2;
+}
+
+X_kdb_exit_init(kp)
+struct X_kdb *kp;
+{
+ if (kp->size > sizeof X_kdb_exit_str)
+ u.u_error = ENOENT;
+ else if(copyin(kp->ptr, X_kdb_exit_str, kp->size) == EFAULT)
+ u.u_error = EFAULT;
+
+ X_kdb_exit_len = kp->size>>2;
+}
+#endif MACH_KERNEL
diff --git a/i386/i386at/kd_mouse.c b/i386/i386at/kd_mouse.c
new file mode 100644
index 00000000..8f4e09aa
--- /dev/null
+++ b/i386/i386at/kd_mouse.c
@@ -0,0 +1,899 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: kd_mouse.c
+ Description: mouse driver as part of keyboard/display driver
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Hacked up support for serial mouse connected to COM1, using Mouse
+ * Systems 5-byte protocol at 1200 baud. This should work for
+ * Mouse Systems, SummaMouse, and Logitek C7 mice.
+ *
+ * The interface provided by /dev/mouse is a series of events as
+ * described in i386at/kd.h.
+ */
+
+#include <mach/boolean.h>
+#include <sys/types.h>
+#ifdef MACH_KERNEL
+#include <device/errno.h>
+#include <device/io_req.h>
+#else MACH_KERNEL
+#include <sys/file.h>
+#include <sys/errno.h>
+#include <kern/thread.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/kernel.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#endif MACH_KERNEL
+#include <i386/ipl.h>
+#include <chips/busses.h>
+#include <i386at/kd.h>
+#include <i386at/kd_queue.h>
+#include <i386at/i8250.h>
+
+static int (*oldvect)(); /* old interrupt vector */
+static int oldunit;
+static spl_t oldspl;
+extern struct bus_device *cominfo[];
+
+kd_event_queue mouse_queue; /* queue of mouse events */
+boolean_t mouse_in_use = FALSE;
+#ifdef MACH_KERNEL
+queue_head_t mouse_read_queue = { &mouse_read_queue, &mouse_read_queue };
+#else MACH_KERNEL
+struct proc *mouse_sel = 0; /* selecting process, if any */
+short mousepgrp = 0; /* process group leader when dev is open */
+#endif MACH_KERNEL
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+int mouseflag = 0;
+#define MOUSE_COLL 1 /* select collision */
+#define MOUSE_ASYNC 2 /* user wants asynch notification */
+#define MOUSE_NBIO 4 /* user wants non-blocking I/O */
+#endif MACH_KERNEL
+
+/*
+ * The state of the 3 buttons is encoded in the low-order 3 bits (both
+ * here and in other variables in the driver).
+ */
+u_char lastbuttons; /* previous state of mouse buttons */
+#define MOUSE_UP 1
+#define MOUSE_DOWN 0
+#define MOUSE_ALL_UP 0x7
+
+int mouseintr();
+void mouse_enqueue();
+int mouse_baud = BCNT1200;
+
+boolean_t mouse_char_cmd = FALSE; /* mouse response is to cmd */
+boolean_t mouse_char_wanted = FALSE; /* want mouse response */
+boolean_t mouse_char_in = FALSE; /* have mouse response */
+unsigned char mouse_char; /* mouse response */
+
+
+/*
+ * init_mouse_hw - initialize the serial port.
+ */
+init_mouse_hw(unit, mode)
+{
+ caddr_t base_addr = (caddr_t)cominfo[unit]->address;
+
+ outb(base_addr + RIE, 0);
+ outb(base_addr + RLC, LCDLAB);
+ outb(base_addr + RDLSB, mouse_baud & 0xff);
+ outb(base_addr + RDMSB, (mouse_baud >> 8) & 0xff);
+ outb(base_addr + RLC, mode);
+ outb(base_addr + RMC, MCDTR | MCRTS | MCOUT2);
+ outb(base_addr + RIE, IERD | IELS);
+}
+
+
+/*
+ * mouseopen - Verify that the request is read-only, initialize,
+ * and remember process group leader.
+ */
+/*
+ * Low 3 bits of minor are the com port #.
+ * The high 5 bits of minor are the mouse type
+ */
+#define MOUSE_SYSTEM_MOUSE 0
+#define MICROSOFT_MOUSE 1
+#define IBM_MOUSE 2
+#define NO_MOUSE 3
+#define LOGITECH_TRACKMAN 4
+#define MICROSOFT_MOUSE7 5
+static int mouse_type;
+static int mousebufsize;
+static int mousebufindex = 0;
+int track_man[10];
+
+/*ARGSUSED*/
+mouseopen(dev, flags)
+ dev_t dev;
+ int flags;
+{
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ if (flags & FWRITE)
+ return(ENODEV);
+#endif MACH_KERNEL
+ if (mouse_in_use)
+ return(EBUSY);
+ mouse_in_use = TRUE; /* locking? */
+ kdq_reset(&mouse_queue);
+ lastbuttons = MOUSE_ALL_UP;
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ mousepgrp = u.u_procp->p_pgrp;
+#endif MACH_KERNEL
+
+ switch (mouse_type = ((minor(dev) & 0xf8) >> 3)) {
+ case MICROSOFT_MOUSE7:
+ mousebufsize = 3;
+ serial_mouse_open(dev);
+ init_mouse_hw(dev&7, LC7);
+ case MICROSOFT_MOUSE:
+ mousebufsize = 3;
+ serial_mouse_open(dev);
+ init_mouse_hw(dev&7, LC8);
+ break;
+ case MOUSE_SYSTEM_MOUSE:
+ mousebufsize = 5;
+ serial_mouse_open(dev);
+ init_mouse_hw(dev&7, LC8);
+ break;
+ case LOGITECH_TRACKMAN:
+ mousebufsize = 3;
+ serial_mouse_open(dev);
+ init_mouse_hw(dev&7, LC7);
+ track_man[0] = comgetc(dev&7);
+ track_man[1] = comgetc(dev&7);
+ if (track_man[0] != 0x4d &&
+ track_man[1] != 0x33) {
+ printf("LOGITECH_TRACKMAN: NOT M3");
+ }
+ break;
+ case IBM_MOUSE:
+ mousebufsize = 3;
+ kd_mouse_open(dev, 12);
+ ibm_ps2_mouse_open(dev);
+ break;
+ case NO_MOUSE:
+ break;
+ }
+ mousebufindex = 0;
+ return(0);
+}
+
+serial_mouse_open(dev)
+{
+ int unit = minor(dev) & 0x7;
+ int mouse_pic = cominfo[unit]->sysdep1;
+
+ spl_t s = splhi(); /* disable interrupts */
+
+ oldvect = ivect[mouse_pic];
+ ivect[mouse_pic] = mouseintr;
+
+ oldunit = iunit[mouse_pic];
+ iunit[mouse_pic] = unit;
+
+ /* XXX other arrays to init? */
+ splx(s); /* XXX - should come after init? */
+}
+
+int mouse_packets = 0;
+kd_mouse_open(dev, mouse_pic)
+{
+ spl_t s = splhi(); /* disable interrupts */
+ extern int kdintr();
+
+ oldvect = ivect[mouse_pic];
+ ivect[mouse_pic] = kdintr;
+ oldspl = intpri[mouse_pic];
+ intpri[mouse_pic] = SPL6;
+ form_pic_mask();
+ splx(s);
+}
+
+/*
+ * mouseclose - Disable interrupts on the serial port, reset driver flags,
+ * and restore the serial port interrupt vector.
+ */
+mouseclose(dev, flags)
+{
+ switch (mouse_type) {
+ case MICROSOFT_MOUSE:
+ case MICROSOFT_MOUSE7:
+ case MOUSE_SYSTEM_MOUSE:
+ case LOGITECH_TRACKMAN:
+ serial_mouse_close(dev);
+ break;
+ case IBM_MOUSE:
+ ibm_ps2_mouse_close(dev);
+ kd_mouse_close(dev, 12);
+ {int i = 20000; for (;i--;); }
+ kd_mouse_drain();
+ break;
+ case NO_MOUSE:
+ break;
+ }
+
+ kdq_reset(&mouse_queue); /* paranoia */
+ mouse_in_use = FALSE;
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ mousepgrp = 0;
+ mouseflag = 0;
+ mouse_sel = 0;
+#endif MACH_KERNEL
+}
+
+/*ARGSUSED*/
+serial_mouse_close(dev, flags)
+ dev_t dev;
+ int flags;
+{
+ spl_t o_pri = splhi(); /* mutex with open() */
+ int unit = minor(dev) & 0x7;
+ int mouse_pic = cominfo[unit]->sysdep1;
+ caddr_t base_addr = (caddr_t)cominfo[unit]->address;
+
+ assert(ivect[mouse_pic] == mouseintr);
+ outb(base_addr + RIE, 0); /* disable serial port */
+ outb(base_addr + RMC, 0); /* no rts */
+ ivect[mouse_pic] = oldvect;
+ iunit[mouse_pic] = oldunit;
+
+ (void)splx(o_pri);
+}
+
+kd_mouse_close(dev, mouse_pic)
+{
+ spl_t s = splhi();
+
+ ivect[mouse_pic] = oldvect;
+ intpri[mouse_pic] = oldspl;
+ form_pic_mask();
+ splx(s);
+}
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+/*
+ * mouseioctl - handling for asynch & non-blocking I/O.
+ */
+
+/*ARGSUSED*/
+mouseioctl(dev, cmd, data, flag)
+ dev_t dev;
+ int cmd;
+ caddr_t data;
+ int flag;
+{
+ int s = SPLKD();
+ int err = 0;
+
+ switch (cmd) {
+ case FIONBIO:
+ if (*(int *)data)
+ mouseflag |= MOUSE_NBIO;
+ else
+ mouseflag &= ~MOUSE_NBIO;
+ break;
+ case FIOASYNC:
+ if (*(int *)data)
+ mouseflag |= MOUSE_ASYNC;
+ else
+ mouseflag &= ~MOUSE_ASYNC;
+ break;
+ default:
+ err = ENOTTY;
+ break;
+ }
+
+ splx(s);
+ return(err);
+}
+
+
+/*
+ * mouseselect - check for pending events, etc.
+ */
+
+/*ARGSUSED*/
+mouseselect(dev, rw)
+{
+ int s = SPLKD();
+
+ if (!kdq_empty(&mouse_queue)) {
+ splx(s);
+ return(1);
+ }
+
+ if (mouse_sel)
+ mouseflag |= MOUSE_COLL;
+ else
+ mouse_sel = (struct proc *)current_thread();
+ /* eeeyuck */
+
+ splx(s);
+ return(0);
+}
+#endif MACH_KERNEL
+
+/*
+ * mouseread - dequeue and return any queued events.
+ */
+#ifdef MACH_KERNEL
+boolean_t mouse_read_done(); /* forward */
+
+mouseread(dev, ior)
+ dev_t dev;
+ register io_req_t ior;
+{
+ register int err, count;
+ register spl_t s;
+
+ err = device_read_alloc(ior, (vm_size_t)ior->io_count);
+ if (err != KERN_SUCCESS)
+ return (err);
+
+ s = SPLKD();
+ if (kdq_empty(&mouse_queue)) {
+ if (ior->io_mode & D_NOWAIT) {
+ splx(s);
+ return (D_WOULD_BLOCK);
+ }
+ ior->io_done = mouse_read_done;
+ enqueue_tail(&mouse_read_queue, (queue_entry_t)ior);
+ splx(s);
+ return (D_IO_QUEUED);
+ }
+ count = 0;
+ while (!kdq_empty(&mouse_queue) && count < ior->io_count) {
+ register kd_event *ev;
+
+ ev = kdq_get(&mouse_queue);
+ *(kd_event *)(&ior->io_data[count]) = *ev;
+ count += sizeof(kd_event);
+ }
+ splx(s);
+ ior->io_residual = ior->io_count - count;
+ return (D_SUCCESS);
+}
+
+boolean_t mouse_read_done(ior)
+ register io_req_t ior;
+{
+ register int count;
+ register spl_t s;
+
+ s = SPLKD();
+ if (kdq_empty(&mouse_queue)) {
+ ior->io_done = mouse_read_done;
+ enqueue_tail(&mouse_read_queue, (queue_entry_t)ior);
+ splx(s);
+ return (FALSE);
+ }
+
+ count = 0;
+ while (!kdq_empty(&mouse_queue) && count < ior->io_count) {
+ register kd_event *ev;
+
+ ev = kdq_get(&mouse_queue);
+ *(kd_event *)(&ior->io_data[count]) = *ev;
+ count += sizeof(kd_event);
+ }
+ splx(s);
+
+ ior->io_residual = ior->io_count - count;
+ ds_read_done(ior);
+
+ return (TRUE);
+}
+
+#else MACH_KERNEL
+/*ARGSUSED*/
+mouseread(dev, uio)
+ dev_t dev;
+ struct uio *uio;
+{
+ int s = SPLKD();
+ int err = 0;
+ kd_event *ev;
+ int i;
+ char *cp;
+
+ if (kdq_empty(&mouse_queue))
+ if (mouseflag & MOUSE_NBIO) {
+ err = EWOULDBLOCK;
+ goto done;
+ } else
+ while (kdq_empty(&mouse_queue)) {
+ splx(s);
+ sleep((caddr_t)&mouse_queue, TTIPRI);
+ s = SPLKD();
+ }
+
+ while (!kdq_empty(&mouse_queue) && uio->uio_resid >= sizeof(kd_event)) {
+ ev = kdq_get(&mouse_queue);
+ for (cp = (char *)ev, i = 0; i < sizeof(kd_event);
+ ++i, ++cp) {
+ err = ureadc(*cp, uio);
+ if (err)
+ goto done;
+ }
+ }
+
+done:
+ splx(s);
+ return(err);
+}
+#endif MACH_KERNEL
+
+
+/*
+ * mouseintr - Get a byte and pass it up for handling. Called at SPLKD.
+ */
+mouseintr(unit)
+{
+ caddr_t base_addr = (caddr_t)cominfo[unit]->address;
+ unsigned char id, ls;
+
+ /* get reason for interrupt and line status */
+ id = inb(base_addr + RID);
+ ls = inb(base_addr + RLS);
+
+ /* handle status changes */
+ if (id == IDLS) {
+ if (ls & LSDR) {
+ inb(base_addr + RDAT); /* flush bad character */
+ }
+ return; /* ignore status change */
+ }
+
+ if (id & IDRD) {
+ mouse_handle_byte((u_char)(inb(base_addr + RDAT) & 0xff));
+ }
+}
+
+
+/*
+ * handle_byte - Accumulate bytes until we have an entire packet.
+ * If the mouse has moved or any of the buttons have changed state (up
+ * or down), enqueue the corresponding events.
+ * Called at SPLKD.
+ * XXX - magic numbers.
+ */
+int show_mouse_byte = 0;
+/*
+ X down; middle down; middle up; X up 50 0 0; 50 0 0 22; 50 0 0 02; 40 0 0
+ X down; middle down; X up; middle up 50 0 0; 50 0 0 22; 40 0 0 22; 40 0 0 2
+ *
+ * The trick here is that all the while the middle button is down you get 4 byte
+ * packets with the last byte 0x22. When the middle button goes up you get a
+ * last packet with 0x02.
+ */
+int lastgitech = 0x40; /* figure whether the first 3 bytes imply */
+ /* its time to expect a fourth */
+int fourthgitech = 0; /* look for the 4th byte; we must process it */
+int middlegitech = 0; /* what should the middle button be */
+
+#define MOUSEBUFSIZE 5 /* num bytes def'd by protocol */
+static u_char mousebuf[MOUSEBUFSIZE]; /* 5-byte packet from mouse */
+
+mouse_handle_byte(ch)
+ u_char ch;
+{
+ if (show_mouse_byte) {
+ printf("%x(%c) ", ch, ch);
+ }
+
+ if (mouse_char_cmd) {
+ /*
+ * Mouse character is response to command
+ */
+ mouse_char = ch;
+ mouse_char_in = TRUE;
+ if (mouse_char_wanted) {
+ mouse_char_wanted = FALSE;
+ wakeup(&mouse_char);
+ }
+ return;
+ }
+
+ if (mousebufindex == 0) {
+ switch (mouse_type) {
+ case MICROSOFT_MOUSE7:
+ if ((ch & 0x40) != 0x40)
+ return;
+ break;
+ case MICROSOFT_MOUSE:
+ if ((ch & 0xc0) != 0xc0)
+ return;
+ break;
+ case MOUSE_SYSTEM_MOUSE:
+ if ((ch & 0xf8) != 0x80)
+ return;
+ break;
+ case LOGITECH_TRACKMAN:
+ if (fourthgitech == 1) {
+ fourthgitech = 0;
+ if (ch & 0xf0)
+ middlegitech = 0x4;
+ else
+ middlegitech = 0x0;
+ mouse_packet_microsoft_mouse(mousebuf);
+ return;
+ } else if ((ch & 0xc0) != 0x40)
+ return;
+ break;
+ case IBM_MOUSE:
+ break;
+ }
+ }
+
+ mousebuf[mousebufindex++] = ch;
+ if (mousebufindex < mousebufsize)
+ return;
+
+ /* got a packet */
+ mousebufindex = 0;
+
+ switch (mouse_type) {
+ case MICROSOFT_MOUSE7:
+ case MICROSOFT_MOUSE:
+ mouse_packet_microsoft_mouse(mousebuf);
+ break;
+ case MOUSE_SYSTEM_MOUSE:
+ mouse_packet_mouse_system_mouse(mousebuf);
+ break;
+ case LOGITECH_TRACKMAN:
+ if ( mousebuf[1] || mousebuf[2] ||
+ mousebuf[0] != lastgitech) {
+ mouse_packet_microsoft_mouse(mousebuf);
+ lastgitech = mousebuf[0] & 0xf0;
+ } else {
+ fourthgitech = 1;
+ }
+ break;
+ case IBM_MOUSE:
+ mouse_packet_ibm_ps2_mouse(mousebuf);
+ break;
+ }
+}
+
+mouse_packet_mouse_system_mouse(mousebuf)
+u_char mousebuf[MOUSEBUFSIZE];
+{
+ u_char buttons, buttonchanges;
+ struct mouse_motion moved;
+
+ buttons = mousebuf[0] & 0x7; /* get current state of buttons */
+ buttonchanges = buttons ^ lastbuttons;
+ moved.mm_deltaX = (char)mousebuf[1] + (char)mousebuf[3];
+ moved.mm_deltaY = (char)mousebuf[2] + (char)mousebuf[4];
+
+ if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0)
+ mouse_moved(moved);
+
+ if (buttonchanges != 0) {
+ lastbuttons = buttons;
+ if (buttonchanges & 1)
+ mouse_button(MOUSE_RIGHT, buttons & 1);
+ if (buttonchanges & 2)
+ mouse_button(MOUSE_MIDDLE, (buttons & 2) >> 1);
+ if (buttonchanges & 4)
+ mouse_button(MOUSE_LEFT, (buttons & 4) >> 2);
+ }
+}
+
+/* same as above for microsoft mouse */
+/*
+ * 3 byte microsoft format used
+ *
+ * 7 6 5 4 3 2 1 0
+ * 1 1 L R Y7 Y6 X7 X6
+ * 1 0 X5 X4 X3 X3 X1 X0
+ * 1 0 Y5 Y4 Y3 Y2 Y1 Y0
+ *
+ */
+mouse_packet_microsoft_mouse(mousebuf)
+u_char mousebuf[MOUSEBUFSIZE];
+{
+ u_char buttons, buttonchanges;
+ struct mouse_motion moved;
+
+ buttons = ((mousebuf[0] & 0x30) >> 4);
+ buttons |= middlegitech;
+ /* get current state of buttons */
+#ifdef gross_hack
+ if (buttons == 0x03) /* both buttons down */
+ buttons = 0x04;
+#endif /* gross_hack */
+ buttons = (~buttons) & 0x07; /* convert to not pressed */
+
+ buttonchanges = buttons ^ lastbuttons;
+ moved.mm_deltaX = ((mousebuf[0] & 0x03) << 6) | (mousebuf[1] & 0x3F);
+ moved.mm_deltaY = ((mousebuf[0] & 0x0c) << 4) | (mousebuf[2] & 0x3F);
+ if (moved.mm_deltaX & 0x80) /* negative, in fact */
+ moved.mm_deltaX = moved.mm_deltaX - 0x100;
+ if (moved.mm_deltaY & 0x80) /* negative, in fact */
+ moved.mm_deltaY = moved.mm_deltaY - 0x100;
+ /* and finally the Y orientation is different for the microsoft mouse */
+ moved.mm_deltaY = -moved.mm_deltaY;
+
+ if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0)
+ mouse_moved(moved);
+
+ if (buttonchanges != 0) {
+ lastbuttons = buttons;
+ if (buttonchanges & 1)
+ mouse_button(MOUSE_RIGHT, (buttons & 1) ?
+ MOUSE_UP : MOUSE_DOWN);
+ if (buttonchanges & 2)
+ mouse_button(MOUSE_LEFT, (buttons & 2) ?
+ MOUSE_UP : MOUSE_DOWN);
+ if (buttonchanges & 4)
+ mouse_button(MOUSE_MIDDLE, (buttons & 4) ?
+ MOUSE_UP : MOUSE_DOWN);
+ }
+}
+
+/*
+ * AUX device (PS2) open/close
+ */
+
+/*
+ * Write character to mouse. Called at spltty.
+ */
+void kd_mouse_write(
+ unsigned char ch)
+{
+ while (inb(K_STATUS) & K_IBUF_FUL)
+ continue; /* wait for 'input' port empty */
+ outb(K_CMD, 0xd4); /* send next character to mouse */
+
+ while (inb(K_STATUS) & K_IBUF_FUL)
+ continue; /* wait for 'input' port empty */
+ outb(K_RDWR, ch); /* send command to mouse */
+}
+
+/*
+ * Read next character from mouse, waiting for interrupt
+ * to deliver it. Called at spltty.
+ */
+int kd_mouse_read(void)
+{
+ int ch;
+
+ while (!mouse_char_in) {
+ mouse_char_wanted = TRUE;
+#ifdef MACH_KERNEL
+ assert_wait((event_t) &mouse_char, FALSE);
+ thread_block((void (*)()) 0);
+#else MACH_KERNEL
+ sleep(&mouse_char, PZERO);
+#endif MACH_KERNEL
+ }
+
+ ch = mouse_char;
+ mouse_char_in = FALSE;
+
+ return ch;
+}
+
+ibm_ps2_mouse_open(dev)
+{
+ spl_t s = spltty();
+
+ lastbuttons = 0;
+ mouse_char_cmd = TRUE; /* responses are to commands */
+
+ kd_sendcmd(0xa8); /* enable mouse in kbd */
+
+ kd_cmdreg_write(0x47); /* allow mouse interrupts */
+ /* magic number for ibm? */
+
+ kd_mouse_write(0xff); /* reset mouse */
+ if (kd_mouse_read() != 0xfa) {
+ splx(s);
+ return; /* need ACK */
+ }
+
+ (void) kd_mouse_read(); /* discard 2-character mouse ID */
+ (void) kd_mouse_read();
+
+ kd_mouse_write(0xea); /* set stream mode */
+ if (kd_mouse_read() != 0xfa) {
+ splx(s);
+ return; /* need ACK */
+ }
+
+ kd_mouse_write(0xf4); /* enable */
+ if (kd_mouse_read() != 0xfa) {
+ splx(s);
+ return; /* need ACK */
+ }
+
+ mouse_char_cmd = FALSE; /* now we get mouse packets */
+
+ splx(s);
+}
+
+ibm_ps2_mouse_close(dev)
+{
+ spl_t s = spltty();
+
+ mouse_char_cmd = TRUE; /* responses are to commands */
+
+ kd_mouse_write(0xff); /* reset mouse */
+ if (kd_mouse_read() == 0xfa) {
+ /* got ACK: discard 2-char mouse ID */
+ (void) kd_mouse_read();
+ (void) kd_mouse_read();
+ }
+
+ kd_sendcmd(0xa7); /* disable mouse in kbd */
+ kd_cmdreg_write(0x65); /* disallow mouse interrupts */
+ /* magic number for ibm? */
+
+ splx(s);
+}
+
+/*
+ * 3 byte ibm ps2 format used
+ *
+ * 7 6 5 4 3 2 1 0
+ * YO XO YS XS 1 M R L
+ * X7 X6 X5 X4 X3 X3 X1 X0
+ * Y7 Y6 Y5 Y4 Y3 Y2 Y1 Y0
+ *
+ */
+mouse_packet_ibm_ps2_mouse(mousebuf)
+u_char mousebuf[MOUSEBUFSIZE];
+{
+ u_char buttons, buttonchanges;
+ struct mouse_motion moved;
+
+ buttons = mousebuf[0] & 0x7; /* get current state of buttons */
+ buttonchanges = buttons ^ lastbuttons;
+ moved.mm_deltaX = ((mousebuf[0]&0x10) ? 0xffffff00 : 0 ) | (u_char)mousebuf[1];
+ moved.mm_deltaY = ((mousebuf[0]&0x20) ? 0xffffff00 : 0 ) | (u_char)mousebuf[2];
+ if (mouse_packets) {
+ printf("(%x:%x:%x)", mousebuf[0], mousebuf[1], mousebuf[2]);
+ return;
+ }
+
+ if (moved.mm_deltaX != 0 || moved.mm_deltaY != 0)
+ mouse_moved(moved);
+
+ if (buttonchanges != 0) {
+ lastbuttons = buttons;
+ if (buttonchanges & 1)
+ mouse_button(MOUSE_LEFT, !(buttons & 1));
+ if (buttonchanges & 2)
+ mouse_button(MOUSE_RIGHT, !((buttons & 2) >> 1));
+ if (buttonchanges & 4)
+ mouse_button(MOUSE_MIDDLE, !((buttons & 4) >> 2));
+ }
+}
+
+/*
+ * Enqueue a mouse-motion event. Called at SPLKD.
+ */
+mouse_moved(where)
+ struct mouse_motion where;
+{
+ kd_event ev;
+
+ ev.type = MOUSE_MOTION;
+ ev.time = time;
+ ev.value.mmotion = where;
+ mouse_enqueue(&ev);
+}
+
+
+/*
+ * Enqueue an event for mouse button press or release. Called at SPLKD.
+ */
+mouse_button(which, direction)
+ kev_type which;
+ u_char direction;
+{
+ kd_event ev;
+
+ ev.type = which;
+ ev.time = time;
+ ev.value.up = (direction == MOUSE_UP) ? TRUE : FALSE;
+ mouse_enqueue(&ev);
+}
+
+
+/*
+ * mouse_enqueue - enqueue an event and wake up selecting processes, if
+ * any. Called at SPLKD.
+ */
+
+void
+mouse_enqueue(ev)
+ kd_event *ev;
+{
+ if (kdq_full(&mouse_queue))
+ printf("mouse: queue full\n");
+ else
+ kdq_put(&mouse_queue, ev);
+
+#ifdef MACH_KERNEL
+ {
+ register io_req_t ior;
+ while ((ior = (io_req_t)dequeue_head(&mouse_read_queue)) != 0)
+ iodone(ior);
+ }
+#else MACH_KERNEL
+ if (mouse_sel) {
+ selwakeup(mouse_sel, mouseflag & MOUSE_COLL);
+ mouse_sel = 0;
+ mouseflag &= ~MOUSE_COLL;
+ }
+ if (mouseflag & MOUSE_ASYNC)
+ gsignal(mousepgrp, SIGIO);
+ wakeup((caddr_t)&mouse_queue);
+#endif MACH_KERNEL
+}
diff --git a/i386/i386at/kd_queue.c b/i386/i386at/kd_queue.c
new file mode 100644
index 00000000..2b83044a
--- /dev/null
+++ b/i386/i386at/kd_queue.c
@@ -0,0 +1,115 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: kd_queue.c
+ Description: Event queue code for keyboard/display (and mouse) driver.
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+
+#include <i386at/kd_queue.h>
+
+/*
+ * Notice that when adding an entry to the queue, the caller provides
+ * its own storage, which is copied into the queue. However, when
+ * removing an entry from the queue, the caller is given a pointer to a
+ * queue element. This means that the caller must either process the
+ * element or copy it into its own storage before unlocking the queue.
+ *
+ * These routines should be called only at a protected SPL.
+ */
+
+#define q_next(index) (((index)+1) % KDQSIZE)
+
+boolean_t
+kdq_empty(q)
+ kd_event_queue *q;
+{
+ return(q->firstfree == q->firstout);
+}
+
+boolean_t
+kdq_full(q)
+ kd_event_queue *q;
+{
+ return(q_next(q->firstfree) == q->firstout);
+}
+
+void
+kdq_put(q, ev)
+ kd_event_queue *q;
+ kd_event *ev;
+{
+ kd_event *qp = q->events + q->firstfree;
+
+ qp->type = ev->type;
+ qp->time = ev->time;
+ qp->value = ev->value;
+ q->firstfree = q_next(q->firstfree);
+}
+
+kd_event *
+kdq_get(q)
+ kd_event_queue *q;
+{
+ kd_event *result = q->events + q->firstout;
+
+ q->firstout = q_next(q->firstout);
+ return(result);
+}
+
+void
+kdq_reset(q)
+ kd_event_queue *q;
+{
+ q->firstout = q->firstfree = 0;
+}
diff --git a/i386/i386at/kd_queue.h b/i386/i386at/kd_queue.h
new file mode 100644
index 00000000..1190e600
--- /dev/null
+++ b/i386/i386at/kd_queue.h
@@ -0,0 +1,79 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: kd_queue.h
+ Description: definitions for keybd/display Event queue
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1989.
+ All rights reserved.
+********************************************************************** */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * Definitions for keyboard/mouse events.
+ *
+ * The keyboard and mouse can be read as a stream of events. The event
+ * definition is the same in both cases, but only keyboard events will
+ * be generated by /dev/kbd, and only mouse events will be generated by
+ * /dev/mouse.
+ */
+
+#include <mach/std_types.h>
+#include <i386at/kd.h>
+
+#define KDQSIZE 100 /* is this a good size? */
+
+typedef struct {
+ kd_event events[KDQSIZE];
+ int firstfree, firstout;
+} kd_event_queue;
+
+extern void kdq_put(), kdq_reset();
+extern boolean_t kdq_empty(), kdq_full();
+extern kd_event *kdq_get();
diff --git a/i386/i386at/kdasm.S b/i386/i386at/kdasm.S
new file mode 100644
index 00000000..46b1ee6b
--- /dev/null
+++ b/i386/i386at/kdasm.S
@@ -0,0 +1,145 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Some inline code to speed up major block copies to and from the
+ * screen buffer.
+ *
+ * Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ * All rights reserved.
+ *
+ * orc!eugene 28 Oct 1988
+ *
+ */
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/* $ Header: $ */
+
+
+#include <mach/machine/asm.h>
+
+/*
+ * Function: kd_slmwd()
+ *
+ * This function "slams" a word (char/attr) into the screen memory using
+ * a block fill operation on the 386.
+ *
+ */
+
+#define start 0x08(%ebp)
+#define count 0x0c(%ebp)
+#define value 0x10(%ebp)
+
+ENTRY(kd_slmwd)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %edi
+
+ movl start, %edi
+ movl count, %ecx
+ movw value, %ax
+ cld
+ rep
+ stosw
+
+ popl %edi
+ leave
+ ret
+#undef start
+#undef count
+#undef value
+
+/*
+ * "slam up"
+ */
+
+#define from 0x08(%ebp)
+#define to 0x0c(%ebp)
+#define count 0x10(%ebp)
+ENTRY(kd_slmscu)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+ pushl %edi
+
+ movl from, %esi
+ movl to, %edi
+ movl count, %ecx
+ cmpl %edi, %esi
+ cld
+ rep
+ movsw
+
+ popl %edi
+ popl %esi
+ leave
+ ret
+
+/*
+ * "slam down"
+ */
+ENTRY(kd_slmscd)
+ pushl %ebp
+ movl %esp, %ebp
+ pushl %esi
+ pushl %edi
+
+ movl from, %esi
+ movl to, %edi
+ movl count, %ecx
+ cmpl %edi, %esi
+ std
+ rep
+ movsw
+ cld
+
+ popl %edi
+ popl %esi
+ leave
+ ret
+#undef from
+#undef to
+#undef count
diff --git a/i386/i386at/kdsoft.h b/i386/i386at/kdsoft.h
new file mode 100644
index 00000000..2be21d04
--- /dev/null
+++ b/i386/i386at/kdsoft.h
@@ -0,0 +1,201 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/* **********************************************************************
+ File: kdsoft.h
+ Description: Software structures for keyboard/display driver, shared with
+ drivers for specific graphics cards.
+
+ $ Header: $
+
+ Copyright Ing. C. Olivetti & C. S.p.A. 1988, 1989.
+ All rights reserved.
+********************************************************************** */
+
+/*
+ Copyright 1988, 1989 by Olivetti Advanced Technology Center, Inc.,
+Cupertino, California.
+
+ All Rights Reserved
+
+ Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Olivetti
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+ OLIVETTI DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL OLIVETTI BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUR OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+/*
+ * This driver handles two types of graphics cards. The first type
+ * (e.g., EGA, CGA), treats the screen as a page of characters and
+ * has a hardware cursor. The second type (e.g., the Blit) treats the
+ * screen as a bitmap. A hardware cursor may be present, but it is
+ * ignored in favor of a software cursor.
+ *
+ *
+ * Most of the driver uses the following abstraction for the display:
+ *
+ * The cursor position is simply an index into a (logical) linear char
+ * array that wraps around at the end of each line. Each character
+ * takes up ONE_SPACE bytes. Values in [0..ONE_PAGE) are positions in
+ * the displayed page. Values < 0 and >= ONE_PAGE are off the page
+ * and require some scrolling to put the cursor back on the page.
+ *
+ * The kd_dxxx routines handle the conversion from this abstraction to
+ * what the hardware requires.
+ *
+ * (*kd_dput)(pos, ch, chattr)
+ * csrpos_t pos;
+ * char ch, chattr;
+ * Displays a character at "pos", where "ch" = the character to
+ * be displayed and "chattr" is its attribute byte.
+ *
+ * (*kd_dmvup)(from, to, count)
+ * csrpos_t from, to;
+ * int count;
+ * Does a (relatively) fast block transfer of characters upward.
+ * "count" is the number of character positions (not bytes) to move.
+ * "from" is the character position to start moving from (at the start
+ * of the block to be moved). "to" is the character position to start
+ * moving to.
+ *
+ * (*kd_dmvdown)(from, to, count)
+ * csrpos_t from, to;
+ * int count;
+ * "count" is the number of character positions (not bytes) to move.
+ * "from" is the character position to start moving from (at the end
+ * of the block to be moved). "to" is the character position to
+ * start moving to.
+ *
+ * (*kd_dclear)(to, count, chattr)
+ * csrpos_t, to;
+ * int count;
+ * char chattr;
+ * Erases "count" character positions, starting with "to".
+ *
+ * (*kd_dsetcursor)(pos)
+ * Sets kd_curpos and moves the displayed cursor to track it. "pos"
+ * should be in the range [0..ONE_PAGE).
+ *
+ * (*kd_dreset)()
+ * In some cases, the boot program expects the display to be in a
+ * particular state, and doing a soft reset (i.e.,
+ * software-controlled reboot) doesn't put it into that state. For
+ * these cases, the machine-specific driver should provide a "reset"
+ * procedure, which will be called just before the kd code causes the
+ * system to reboot.
+ */
+
+extern void bmpput(), bmpmvup(), bmpmvdown(), bmpclear(), bmpsetcursor();
+
+extern void (*kd_dput)(); /* put attributed char */
+extern void (*kd_dmvup)(); /* block move up */
+extern void (*kd_dmvdown)(); /* block move down */
+extern void (*kd_dclear)(); /* block clear */
+extern void (*kd_dsetcursor)();
+ /* set cursor position on displayed page */
+extern void (*kd_dreset)(); /* prepare for reboot */
+
+
+/*
+ * Globals used for both character-based controllers and bitmap-based
+ * controllers.
+ */
+typedef short csrpos_t; /* cursor position, ONE_SPACE bytes per char */
+
+extern u_char *vid_start; /* VM start of video RAM or frame buffer */
+extern csrpos_t kd_curpos; /* should be set only by kd_setpos */
+extern short kd_lines; /* num lines in tty display */
+extern short kd_cols;
+extern char kd_attr; /* current character attribute */
+
+
+/*
+ * Globals used only for bitmap-based controllers.
+ * XXX - probably needs reworking for color.
+ */
+
+/*
+ * The following font layout is assumed:
+ *
+ * The top scan line of all the characters comes first. Then the
+ * second scan line, then the third, etc.
+ *
+ * ------ ... ---------|-----N--------|-------------- ... -----------
+ * ------ ... ---------|-----N--------|-------------- ... -----------
+ * .
+ * .
+ * .
+ * ------ ... ---------|-----N--------|-------------- ... -----------
+ *
+ * In the picture, each line is a scan line from the font. Each scan
+ * line is stored in memory immediately after the previous one. The
+ * bits between the vertical lines are the bits for a single character
+ * (e.g., the letter "N").
+ * There are "char_height" scan lines. Each character is "char_width"
+ * bits wide. We make the simplifying assumption that characters are
+ * on byte boundaries. (We also assume that a byte is 8 bits.)
+ */
+
+extern u_char *font_start; /* starting addr of font */
+
+extern short fb_width; /* bits in frame buffer scan line */
+extern short fb_height; /* scan lines in frame buffer*/
+extern short char_width; /* bit width of 1 char */
+extern short char_height; /* bit height of 1 char */
+extern short chars_in_font;
+extern short cursor_height; /* bit height of cursor */
+ /* char_height + cursor_height = line_height */
+
+extern u_char char_black; /* 8 black (off) bits */
+extern u_char char_white; /* 8 white (on) bits */
+
+
+/*
+ * The tty emulation does not usually require the entire frame buffer.
+ * (xstart, ystart) is the bit address for the upper left corner of the
+ * tty "screen".
+ */
+
+extern short xstart, ystart;
+
+
+/*
+ * Accelerators for bitmap displays.
+ */
+
+extern short char_byte_width; /* char_width/8 */
+extern short fb_byte_width; /* fb_width/8 */
+extern short font_byte_width; /* num bytes in 1 scan line of font */
diff --git a/i386/i386at/lpr.c b/i386/i386at/lpr.c
new file mode 100644
index 00000000..09afbfc6
--- /dev/null
+++ b/i386/i386at/lpr.c
@@ -0,0 +1,419 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1993-1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Parallel port printer driver v1.0
+ * All rights reserved.
+ */
+
+#include <lpr.h>
+#if NLPR > 0
+#include <par.h>
+#include <de6c.h>
+
+#ifdef MACH_KERNEL
+#include <mach/std_types.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <device/conf.h>
+#include <device/errno.h>
+#include <device/tty.h>
+#include <device/io_req.h>
+#else MACH_KERNEL
+#include <sys/param.h>
+#include <sys/conf.h>
+#include <sys/dir.h>
+#include <sys/user.h>
+#include <sys/proc.h>
+#include <sys/ioctl.h>
+#include <sys/tty.h>
+#include <sys/systm.h>
+#include <sys/uio.h>
+#include <sys/file.h>
+#endif MACH_KERNEL
+
+#include <i386/ipl.h>
+#include <i386/pio.h>
+#include <chips/busses.h>
+#include <i386at/lprreg.h>
+
+#if NPAR > 0
+extern int parattach();
+#endif
+
+#if NDE6C > 0
+extern int de6cattach();
+#endif
+
+extern void splx();
+extern spl_t spltty();
+extern void timeout();
+extern void ttrstrt();
+
+/*
+ * Driver information for auto-configuration stuff.
+ */
+
+int lprprobe(), lprintr(), lprstart(), lprstop();
+void lprattach(struct bus_device *);
+#ifdef MACH_KERNEL
+int lprstop(), lprgetstat(), lprsetstat();
+#endif MACH_KERNEL
+
+struct bus_device *lprinfo[NLPR]; /* ??? */
+
+static vm_offset_t lpr_std[NLPR] = { 0 };
+static struct bus_device *lpr_info[NLPR];
+struct bus_driver lprdriver = {
+ lprprobe, 0, lprattach, 0, lpr_std, "lpr", lpr_info, 0, 0, 0};
+
+struct tty lpr_tty[NLPR];
+
+int lpr_alive[NLPR];
+
+lprprobe(port, dev)
+struct bus_device *dev;
+{
+ u_short addr = (u_short) dev->address;
+ int unit = dev->unit;
+ int ret;
+
+ if ((unit < 0) || (unit > NLPR)) {
+ printf("com %d out of range\n", unit);
+ return(0);
+ }
+
+ outb(INTR_ENAB(addr),0x07);
+ outb(DATA(addr),0xaa);
+ ret = inb(DATA(addr)) == 0xaa;
+ if (ret) {
+ if (lpr_alive[unit]) {
+ printf("lpr: Multiple alive entries for unit %d.\n", unit);
+ printf("lpr: Ignoring entry with address = %x .\n", addr);
+ ret = 0;
+ } else
+ lpr_alive[unit]++;
+ }
+ return(ret);
+}
+
+void lprattach(struct bus_device *dev)
+{
+ u_char unit = dev->unit;
+ u_short addr = (u_short) dev->address;
+ struct tty *tp = &lpr_tty[unit];
+
+ take_dev_irq(dev);
+ printf(", port = %x, spl = %d, pic = %d.",
+ dev->address, dev->sysdep, dev->sysdep1);
+ lprinfo[unit] = dev;
+
+ outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) & 0x0f);
+
+#if NPAR > 0
+ parattach(dev);
+#endif
+
+#if NDE6C > 0 && !defined(LINUX_DEV)
+ de6cattach(dev);
+#endif
+ return;
+}
+
+lpropen(dev, flag, ior)
+int dev;
+int flag;
+#ifdef MACH_KERNEL
+io_req_t ior;
+#endif MACH_KERNEL
+{
+int unit = minor(dev);
+struct bus_device *isai;
+struct tty *tp;
+u_short addr;
+
+ if (unit >= NLPR || (isai = lprinfo[unit]) == 0 || isai->alive == 0)
+ return(ENXIO);
+ tp = &lpr_tty[unit];
+#ifndef MACH_KERNEL
+ if (tp->t_state & TS_XCLUDE && u.u_uid != 0)
+ return(EBUSY);
+#endif MACH_KERNEL
+ addr = (u_short) isai->address;
+ tp->t_dev = dev;
+ tp->t_addr = *(caddr_t *)&addr;
+ tp->t_oproc = lprstart;
+ tp->t_state |= TS_WOPEN;
+#ifdef MACH_KERNEL
+ tp->t_stop = lprstop;
+ tp->t_getstat = lprgetstat;
+ tp->t_setstat = lprsetstat;
+#endif MACH_KERNEL
+ if ((tp->t_state & TS_ISOPEN) == 0)
+ ttychars(tp);
+ outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) | 0x10);
+ tp->t_state |= TS_CARR_ON;
+ return (char_open(dev, tp, flag, ior));
+}
+
+lprclose(dev, flag)
+int dev;
+int flag;
+{
+int unit = minor(dev);
+struct tty *tp = &lpr_tty[unit];
+u_short addr = (u_short) lprinfo[unit]->address;
+
+#ifndef MACH_KERNEL
+ (*linesw[tp->t_line].l_close)(tp);
+#endif MACH_KERNEL
+ ttyclose(tp);
+ if (tp->t_state&TS_HUPCLS || (tp->t_state&TS_ISOPEN)==0) {
+ outb(INTR_ENAB(addr), inb(INTR_ENAB(addr)) & 0x0f);
+ tp->t_state &= ~TS_BUSY;
+ }
+}
+
+#ifdef MACH_KERNEL
+lprread(dev, ior)
+int dev;
+io_req_t ior;
+{
+ return char_read(&lpr_tty[minor(dev)], ior);
+}
+
+lprwrite(dev, ior)
+int dev;
+io_req_t ior;
+{
+ return char_write(&lpr_tty[minor(dev)], ior);
+}
+
+lprportdeath(dev, port)
+dev_t dev;
+mach_port_t port;
+{
+ return (tty_portdeath(&lpr_tty[minor(dev)], port));
+}
+
+io_return_t
+lprgetstat(dev, flavor, data, count)
+dev_t dev;
+int flavor;
+int *data; /* pointer to OUT array */
+unsigned int *count; /* out */
+{
+ io_return_t result = D_SUCCESS;
+ int unit = minor(dev);
+
+ switch (flavor) {
+ default:
+ result = tty_get_status(&lpr_tty[unit], flavor, data, count);
+ break;
+ }
+ return (result);
+}
+
+io_return_t
+lprsetstat(dev, flavor, data, count)
+dev_t dev;
+int flavor;
+int * data;
+unsigned int count;
+{
+ io_return_t result = D_SUCCESS;
+ int unit = minor(dev);
+ u_short dev_addr = (u_short) lprinfo[unit]->address;
+ int s;
+
+ switch (flavor) {
+ default:
+ result = tty_set_status(&lpr_tty[unit], flavor, data, count);
+/* if (result == D_SUCCESS && flavor == TTY_STATUS)
+ lprparam(unit);
+*/ return (result);
+ }
+ return (D_SUCCESS);
+}
+#else MACH_KERNEL
+int lprwrite(dev, uio)
+ int dev;
+ struct uio *uio;
+{
+ struct tty *tp= &lpr_tty[minor(dev)];
+
+ return ((*linesw[tp->t_line].l_write)(tp, uio));
+}
+
+int lprioctl(dev, cmd, addr, mode)
+ int dev;
+ int cmd;
+ caddr_t addr;
+ int mode;
+{
+ int error;
+ spl_t s;
+ int unit = minor(dev);
+ struct tty *tp = &lpr_tty[unit];
+
+ error = (*linesw[tp->t_line].l_ioctl)(tp, cmd, addr,mode);
+ if (error >= 0)
+ return(error);
+ error = ttioctl(tp, cmd, addr,mode);
+ if (error >= 0)
+ return (error);
+ s = spltty();
+ switch (cmd) {
+ default:
+ splx(s);
+ return(ENOTTY);
+ }
+ splx(s);
+ return(0);
+}
+#endif MACH_KERNEL
+
+int lprintr(unit)
+int unit;
+{
+ register struct tty *tp = &lpr_tty[unit];
+
+ if ((tp->t_state & TS_ISOPEN) == 0)
+ return;
+
+ tp->t_state &= ~TS_BUSY;
+ if (tp->t_state&TS_FLUSH)
+ tp->t_state &=~TS_FLUSH;
+ tt_write_wakeup(tp);
+ lprstart(tp);
+}
+
+int lprstart(tp)
+struct tty *tp;
+{
+ spl_t s = spltty();
+ u_short addr = (natural_t) tp->t_addr;
+ int status = inb(STATUS(addr));
+ char nch;
+
+ if (tp->t_state & (TS_TIMEOUT|TS_TTSTOP|TS_BUSY)) {
+ splx(s);
+ return(0);
+ }
+
+ if (status & 0x20) {
+ printf("Printer out of paper!\n");
+ splx(s);
+ return(0);
+ }
+
+ if (tp->t_outq.c_cc <= TTLOWAT(tp)) {
+#ifdef MACH_KERNEL
+ tt_write_wakeup(tp);
+#else MACH_KERNEL
+ if (tp->t_state & TS_ASLEEP) {
+ tp->t_state &= ~TS_ASLEEP;
+ wakeup ((caddr_t)&tp->t_outq);
+ }
+ if (tp->t_wsel) {
+ selwakeup(tp->t_wsel, tp->t_state & TS_WCOLL);
+ tp->t_wsel = 0;
+ tp->t_state &= ~TS_WCOLL;
+ }
+#endif MACH_KERNEL
+ }
+ if (tp->t_outq.c_cc == 0) {
+ splx(s);
+ return(0);
+ }
+#ifdef MACH_KERNEL
+ nch = getc(&tp->t_outq);
+ if ((tp->t_flags & LITOUT) == 0 && (nch & 0200)) {
+ timeout(ttrstrt, (char *)tp, (nch & 0x7f) + 6);
+ tp->t_state |= TS_TIMEOUT;
+ return;
+ }
+ outb(DATA(addr), nch);
+ outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) | 0x01);
+ outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) & 0x1e);
+ tp->t_state |= TS_BUSY;
+#else MACH_KERNEL
+ if (tp->t_flags & (RAW|LITOUT))
+ nch = ndqb(&tp->t_outq,0);
+ else {
+ nch = ndqb(&tp->t_outq, 0200);
+ if (nch == 0) {
+ nch = getc(&tp->t_outq);
+ timeout(ttrstrt,(caddr_t)tp,(nch&0x7f)+6);
+ tp->t_state |= TS_TIMEOUT;
+ splx(s);
+ return(0);
+ }
+ }
+ if (nch) {
+ nch=getc(&tp->t_outq);
+ outb(DATA(addr), nch);
+ outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) | 0x01);
+ outb(INTR_ENAB(addr),inb(INTR_ENAB(addr)) & 0x1e);
+ tp->t_state |= TS_BUSY;
+ }
+#endif MACH_KERNEL
+ splx(s);
+ return(0);
+}
+
+#ifdef MACH_KERNEL
+lprstop(tp, flags)
+register struct tty *tp;
+int flags;
+{
+ if ((tp->t_state & TS_BUSY) && (tp->t_state & TS_TTSTOP) == 0)
+ tp->t_state |= TS_FLUSH;
+}
+#else MACH_KERNEL
+int lprstop(tp, flag)
+struct tty *tp;
+{
+ int s = spltty();
+
+ if ((tp->t_state&TS_BUSY) && (!(tp->t_state&TS_TTSTOP)))
+ tp->t_state |= TS_FLUSH;
+ splx(s);
+}
+#endif MACH_KERNEL
+lprpr(unit)
+{
+ lprpr_addr(lprinfo[unit]->address);
+ return 0;
+}
+
+lprpr_addr(addr)
+{
+ printf("DATA(%x) %x, STATUS(%x) %x, INTR_ENAB(%x) %x\n",
+ DATA(addr), inb(DATA(addr)),
+ STATUS(addr), inb(STATUS(addr)),
+ INTR_ENAB(addr), inb(INTR_ENAB(addr)));
+}
+#endif NLPR
diff --git a/i386/i386at/lprreg.h b/i386/i386at/lprreg.h
new file mode 100644
index 00000000..c6fbed43
--- /dev/null
+++ b/i386/i386at/lprreg.h
@@ -0,0 +1,33 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * Parallel port printer driver v1.0
+ * All rights reserved.
+ */
+
+#define DATA(addr) (addr + 0)
+#define STATUS(addr) (addr + 1)
+#define INTR_ENAB(addr) (addr + 2)
diff --git a/i386/i386at/model_dep.c b/i386/i386at/model_dep.c
new file mode 100644
index 00000000..3ef53a94
--- /dev/null
+++ b/i386/i386at/model_dep.c
@@ -0,0 +1,651 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989, 1988 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ * File: model_dep.c
+ * Author: Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Copyright (C) 1986, Avadis Tevanian, Jr., Michael Wayne Young
+ *
+ * Basic initialization for I386 - ISA bus machines.
+ */
+
+#include <platforms.h>
+#include <mach_kdb.h>
+
+#include <mach/vm_param.h>
+#include <mach/vm_prot.h>
+#include <mach/machine.h>
+#include <mach/machine/multiboot.h>
+
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <sys/time.h>
+#include <vm/vm_page.h>
+#include <i386/machspl.h>
+#include <i386/pmap.h>
+#include "proc_reg.h"
+
+/* Location of the kernel's symbol table.
+ Both of these are 0 if none is available. */
+#if MACH_KDB
+static vm_offset_t kern_sym_start, kern_sym_end;
+#else
+#define kern_sym_start 0
+#define kern_sym_end 0
+#endif
+
+/* These indicate the total extent of physical memory addresses we're using.
+ They are page-aligned. */
+vm_offset_t phys_first_addr = 0;
+vm_offset_t phys_last_addr;
+
+/* Virtual address of physical memory, for the kvtophys/phystokv macros. */
+vm_offset_t phys_mem_va;
+
+struct multiboot_info *boot_info;
+
+/* Command line supplied to kernel. */
+char *kernel_cmdline = "";
+
+/* This is used for memory initialization:
+ it gets bumped up through physical memory
+ that exists and is not occupied by boot gunk.
+ It is not necessarily page-aligned. */
+static vm_offset_t avail_next = 0x1000; /* XX end of BIOS data area */
+
+/* Possibly overestimated amount of available memory
+ still remaining to be handed to the VM system. */
+static vm_size_t avail_remaining;
+
+/* Configuration parameter:
+ if zero, only use physical memory in the low 16MB of addresses.
+ Only SCSI still has DMA problems. */
+#ifdef LINUX_DEV
+int use_all_mem = 1;
+#else
+#include "nscsi.h"
+#if NSCSI > 0
+int use_all_mem = 0;
+#else
+int use_all_mem = 1;
+#endif
+#endif
+
+extern char version[];
+
+extern void setup_main();
+
+void inittodr(); /* forward */
+
+int rebootflag = 0; /* exported to kdintr */
+
+/* XX interrupt stack pointer and highwater mark, for locore.S. */
+vm_offset_t int_stack_top, int_stack_high;
+
+#ifdef LINUX_DEV
+extern void linux_init(void);
+#endif
+
+/*
+ * Find devices. The system is alive.
+ */
+void machine_init()
+{
+ /*
+ * Initialize the console.
+ */
+ cninit();
+
+ /*
+ * Set up to use floating point.
+ */
+ init_fpu();
+
+#ifdef LINUX_DEV
+ /*
+ * Initialize Linux drivers.
+ */
+ linux_init();
+#endif
+
+ /*
+ * Find the devices
+ */
+ probeio();
+
+ /*
+ * Get the time
+ */
+ inittodr();
+
+ /*
+ * Tell the BIOS not to clear and test memory.
+ */
+ *(unsigned short *)phystokv(0x472) = 0x1234;
+
+ /*
+ * Unmap page 0 to trap NULL references.
+ */
+ pmap_unmap_page_zero();
+}
+
+/*
+ * Halt a cpu.
+ */
+halt_cpu()
+{
+ asm volatile("cli");
+ while(1);
+}
+
+/*
+ * Halt the system or reboot.
+ */
+halt_all_cpus(reboot)
+ boolean_t reboot;
+{
+ if (reboot) {
+ kdreboot();
+ }
+ else {
+ rebootflag = 1;
+ printf("In tight loop: hit ctl-alt-del to reboot\n");
+ (void) spl0();
+ }
+ for (;;)
+ continue;
+}
+
+void exit(int rc)
+{
+ halt_all_cpus(0);
+}
+
+void db_reset_cpu()
+{
+ halt_all_cpus(1);
+}
+
+
+/*
+ * Compute physical memory size and other parameters.
+ */
+void
+mem_size_init()
+{
+ /* Physical memory on all PCs starts at physical address 0.
+ XX make it a constant. */
+ phys_first_addr = 0;
+
+ phys_last_addr = 0x100000 + (boot_info->mem_upper * 0x400);
+ avail_remaining
+ = phys_last_addr - (0x100000 - (boot_info->mem_lower * 0x400)
+ - 0x1000);
+
+ printf("AT386 boot: physical memory from 0x%x to 0x%x\n",
+ phys_first_addr, phys_last_addr);
+
+ if ((!use_all_mem) && phys_last_addr > 16 * 1024*1024) {
+ printf("** Limiting useable memory to 16 Meg to avoid DMA problems.\n");
+ /* This is actually enforced below, in init_alloc_aligned. */
+ }
+
+ phys_first_addr = round_page(phys_first_addr);
+ phys_last_addr = trunc_page(phys_last_addr);
+}
+
+/*
+ * Basic PC VM initialization.
+ * Turns on paging and changes the kernel segments to use high linear addresses.
+ */
+i386at_init()
+{
+ /* XXX move to intel/pmap.h */
+ extern pt_entry_t *kernel_page_dir;
+
+ /*
+ * Initialize the PIC prior to any possible call to an spl.
+ */
+ picinit();
+
+ /*
+ * Find memory size parameters.
+ */
+ mem_size_init();
+
+ /*
+ * Initialize kernel physical map, mapping the
+ * region from loadpt to avail_start.
+ * Kernel virtual address starts at VM_KERNEL_MIN_ADDRESS.
+ * XXX make the BIOS page (page 0) read-only.
+ */
+ pmap_bootstrap();
+
+ /*
+ * Turn paging on.
+ * We'll have to temporarily install a direct mapping
+ * between physical memory and low linear memory,
+ * until we start using our new kernel segment descriptors.
+ * One page table (4MB) should do the trick.
+ * Also, set the WP bit so that on 486 or better processors
+ * page-level write protection works in kernel mode.
+ */
+ kernel_page_dir[lin2pdenum(0)] =
+ kernel_page_dir[lin2pdenum(LINEAR_MIN_KERNEL_ADDRESS)];
+ set_cr3((unsigned)kernel_page_dir);
+ set_cr0(get_cr0() | CR0_PG | CR0_WP);
+ flush_instr_queue();
+
+ /*
+ * Initialize and activate the real i386 protected-mode structures.
+ */
+ gdt_init();
+ idt_init();
+ int_init();
+ ldt_init();
+ ktss_init();
+
+ /* Get rid of the temporary direct mapping and flush it out of the TLB. */
+ kernel_page_dir[lin2pdenum(0)] = 0;
+ set_cr3((unsigned)kernel_page_dir);
+
+
+
+ /* XXX We'll just use the initialization stack we're already running on
+ as the interrupt stack for now. Later this will have to change,
+ because the init stack will get freed after bootup. */
+ asm("movl %%esp,%0" : "=m" (int_stack_top));
+
+ /* Interrupt stacks are allocated in physical memory,
+ while kernel stacks are allocated in kernel virtual memory,
+ so phys_last_addr serves as a convenient dividing point. */
+ int_stack_high = phys_last_addr;
+}
+
+/*
+ * C boot entrypoint - called by boot_entry in boothdr.S.
+ * Running in 32-bit flat mode, but without paging yet.
+ */
+void c_boot_entry(vm_offset_t bi)
+{
+ /* Stash the boot_image_info pointer. */
+ boot_info = (struct multiboot_info*)phystokv(bi);
+
+ /* XXX we currently assume phys_mem_va is always 0 here -
+ if it isn't, we must tweak the pointers in the boot_info. */
+
+ /* Before we do _anything_ else, print the hello message.
+ If there are no initialized console devices yet,
+ it will be stored and printed at the first opportunity. */
+ printf(version);
+ printf("\n");
+
+ /* Find the kernel command line, if there is one. */
+ if (boot_info->flags & MULTIBOOT_CMDLINE)
+ kernel_cmdline = (char*)phystokv(boot_info->cmdline);
+
+#if MACH_KDB
+ /*
+ * Locate the kernel's symbol table, if the boot loader provided it.
+ * We need to do this before i386at_init()
+ * so that the symbol table's memory won't be stomped on.
+ */
+ if ((boot_info->flags & MULTIBOOT_AOUT_SYMS)
+ && boot_info->syms.a.addr)
+ {
+ vm_size_t symtab_size, strtab_size;
+
+ kern_sym_start = (vm_offset_t)phystokv(boot_info->syms.a.addr);
+ symtab_size = (vm_offset_t)phystokv(boot_info->syms.a.tabsize);
+ strtab_size = (vm_offset_t)phystokv(boot_info->syms.a.strsize);
+ kern_sym_end = kern_sym_start + 4 + symtab_size + strtab_size;
+
+ printf("kernel symbol table at %08x-%08x (%d,%d)\n",
+ kern_sym_start, kern_sym_end,
+ symtab_size, strtab_size);
+ }
+#endif MACH_KDB
+
+ /*
+ * Do basic VM initialization
+ */
+ i386at_init();
+
+#if MACH_KDB
+ /*
+ * Initialize the kernel debugger's kernel symbol table.
+ */
+ if (kern_sym_start)
+ {
+ aout_db_sym_init(kern_sym_start, kern_sym_end, "mach", 0);
+ }
+
+ /*
+ * Cause a breakpoint trap to the debugger before proceeding
+ * any further if the proper option flag was specified
+ * on the kernel's command line.
+ * XXX check for surrounding spaces.
+ */
+ if (strstr(kernel_cmdline, "-d ")) {
+ cninit(); /* need console for debugger */
+ Debugger();
+ }
+#endif MACH_KDB
+
+ machine_slot[0].is_cpu = TRUE;
+ machine_slot[0].running = TRUE;
+ machine_slot[0].cpu_type = CPU_TYPE_I386;
+ machine_slot[0].cpu_subtype = CPU_SUBTYPE_AT386;
+
+ /*
+ * Start the system.
+ */
+ setup_main();
+
+}
+
+#include <mach/vm_prot.h>
+#include <vm/pmap.h>
+#include <mach/time_value.h>
+
+timemmap(dev,off,prot)
+ vm_prot_t prot;
+{
+ extern time_value_t *mtime;
+
+#ifdef lint
+ dev++; off++;
+#endif lint
+
+ if (prot & VM_PROT_WRITE) return (-1);
+
+ return (i386_btop(pmap_extract(pmap_kernel(), (vm_offset_t) mtime)));
+}
+
+startrtclock()
+{
+ clkstart();
+}
+
+void
+inittodr()
+{
+ time_value_t new_time;
+
+ new_time.seconds = 0;
+ new_time.microseconds = 0;
+
+ (void) readtodc(&new_time.seconds);
+
+ {
+ spl_t s = splhigh();
+ time = new_time;
+ splx(s);
+ }
+}
+
+void
+resettodr()
+{
+ writetodc();
+}
+
+unsigned int pmap_free_pages()
+{
+ return atop(avail_remaining);
+}
+
+/* Always returns page-aligned regions. */
+boolean_t
+init_alloc_aligned(vm_size_t size, vm_offset_t *addrp)
+{
+ vm_offset_t addr;
+ extern char start[], end[];
+ int i;
+
+ /* Memory regions to skip. */
+ vm_offset_t boot_info_start_pa = kvtophys(boot_info);
+ vm_offset_t boot_info_end_pa = boot_info_start_pa + sizeof(*boot_info);
+ vm_offset_t cmdline_start_pa = boot_info->flags & MULTIBOOT_CMDLINE
+ ? boot_info->cmdline : 0;
+ vm_offset_t cmdline_end_pa = cmdline_start_pa
+ ? cmdline_start_pa+strlen((char*)phystokv(cmdline_start_pa))+1
+ : 0;
+ vm_offset_t mods_start_pa = boot_info->flags & MULTIBOOT_MODS
+ ? boot_info->mods_addr : 0;
+ vm_offset_t mods_end_pa = mods_start_pa
+ ? mods_start_pa
+ + boot_info->mods_count * sizeof(struct multiboot_module)
+ : 0;
+
+ retry:
+
+ /* Page-align the start address. */
+ avail_next = round_page(avail_next);
+
+ /* Check if we have reached the end of memory. */
+ if (avail_next == phys_last_addr)
+ return FALSE;
+
+ /* Tentatively assign the current location to the caller. */
+ addr = avail_next;
+
+ /* Bump the pointer past the newly allocated region
+ and see where that puts us. */
+ avail_next += size;
+
+ /* Skip past the I/O and ROM area. */
+ if ((avail_next > (boot_info->mem_lower * 0x400)) && (addr < 0x100000))
+ {
+ avail_next = 0x100000;
+ goto retry;
+ }
+
+ /* If we're only supposed to use the low 16 megs, enforce that. */
+ if ((!use_all_mem) && (addr >= 16 * 1024*1024)) {
+ return FALSE;
+ }
+
+ /* Skip our own kernel code, data, and bss. */
+ if ((avail_next >= (vm_offset_t)start) && (addr < (vm_offset_t)end))
+ {
+ avail_next = (vm_offset_t)end;
+ goto retry;
+ }
+
+ /* Skip any areas occupied by valuable boot_info data. */
+ if ((avail_next > boot_info_start_pa) && (addr < boot_info_end_pa))
+ {
+ avail_next = boot_info_end_pa;
+ goto retry;
+ }
+ if ((avail_next > cmdline_start_pa) && (addr < cmdline_end_pa))
+ {
+ avail_next = cmdline_end_pa;
+ goto retry;
+ }
+ if ((avail_next > mods_start_pa) && (addr < mods_end_pa))
+ {
+ avail_next = mods_end_pa;
+ goto retry;
+ }
+ if ((avail_next > kern_sym_start) && (addr < kern_sym_end))
+ {
+ avail_next = kern_sym_end;
+ goto retry;
+ }
+ if (boot_info->flags & MULTIBOOT_MODS)
+ {
+ struct multiboot_module *m = (struct multiboot_module *)
+ phystokv(boot_info->mods_addr);
+ for (i = 0; i < boot_info->mods_count; i++)
+ {
+ if ((avail_next > m[i].mod_start)
+ && (addr < m[i].mod_end))
+ {
+ avail_next = m[i].mod_end;
+ goto retry;
+ }
+ /* XXX string */
+ }
+ }
+
+ avail_remaining -= size;
+
+ *addrp = addr;
+ return TRUE;
+}
+
+boolean_t pmap_next_page(addrp)
+ vm_offset_t *addrp;
+{
+ return init_alloc_aligned(PAGE_SIZE, addrp);
+}
+
+/* Grab a physical page:
+ the standard memory allocation mechanism
+ during system initialization. */
+vm_offset_t
+pmap_grab_page()
+{
+ vm_offset_t addr;
+ if (!pmap_next_page(&addr))
+ panic("Not enough memory to initialize Mach");
+ return addr;
+}
+
+boolean_t pmap_valid_page(x)
+ vm_offset_t x;
+{
+ /* XXX is this OK? What does it matter for? */
+ return (((phys_first_addr <= x) && (x < phys_last_addr)) &&
+ !(((boot_info->mem_lower * 1024) <= x) && (x < 1024*1024)));
+}
+
+#ifndef NBBY
+#define NBBY 8
+#endif
+#ifndef NBPW
+#define NBPW (NBBY * sizeof(int))
+#endif
+#define DMA_MAX (16*1024*1024)
+
+/*
+ * Allocate contiguous pages below 16 MB
+ * starting at specified boundary for DMA.
+ */
+vm_offset_t
+alloc_dma_mem(size, align)
+ vm_size_t size;
+ vm_offset_t align;
+{
+ int *bits, i, j, k, n;
+ int npages, count, bit, mask;
+ int first_page, last_page;
+ vm_offset_t addr;
+ vm_page_t p, prevp;
+
+ npages = round_page(size) / PAGE_SIZE;
+ mask = align ? (align - 1) / PAGE_SIZE : 0;
+
+ /*
+ * Allocate bit array.
+ */
+ n = ((DMA_MAX / PAGE_SIZE) + NBPW - 1) / NBPW;
+ i = n * NBPW;
+ bits = (unsigned *)kalloc(i);
+ if (bits == 0) {
+ printf("alloc_dma_mem: unable alloc bit array\n");
+ return (0);
+ }
+ bzero((char *)bits, i);
+
+ /*
+ * Walk the page free list and set a bit for
+ * every usable page in bit array.
+ */
+ simple_lock(&vm_page_queue_free_lock);
+ for (p = vm_page_queue_free; p; p = (vm_page_t)p->pageq.next) {
+ if (p->phys_addr < DMA_MAX) {
+ i = p->phys_addr / PAGE_SIZE;
+ bits[i / NBPW] |= 1 << (i % NBPW);
+ }
+ }
+
+ /*
+ * Search for contiguous pages by scanning bit array.
+ */
+ for (i = 0, first_page = -1; i < n; i++) {
+ for (bit = 1, j = 0; j < NBPW; j++, bit <<= 1) {
+ if (bits[i] & bit) {
+ if (first_page < 0) {
+ k = i * NBPW + j;
+ if (!mask
+ || (((k & mask) + npages)
+ <= mask + 1)) {
+ first_page = k;
+ if (npages == 1)
+ goto found;
+ count = 1;
+ }
+ } else if (++count == npages)
+ goto found;
+ } else
+ first_page = -1;
+ }
+ }
+ addr = 0;
+ goto out;
+
+ found:
+ /*
+ * Remove pages from the free list.
+ */
+ addr = first_page * PAGE_SIZE;
+ last_page = first_page + npages;
+ vm_page_free_count -= npages;
+ p = vm_page_queue_free;
+ prevp = 0;
+ while (1) {
+ i = p->phys_addr / PAGE_SIZE;
+ if (i >= first_page && i < last_page) {
+ if (prevp)
+ prevp->pageq.next = p->pageq.next;
+ else
+ vm_page_queue_free = (vm_page_t)p->pageq.next;
+ p->free = FALSE;
+ if (--npages == 0)
+ break;
+ } else
+ prevp = p;
+ p = (vm_page_t)p->pageq.next;
+ }
+
+ out:
+ simple_unlock(&vm_page_queue_free_lock);
+ kfree((vm_offset_t)bits, n * NBPW);
+ return (addr);
+}
diff --git a/i386/i386at/nfd.c b/i386/i386at/nfd.c
new file mode 100644
index 00000000..950f8964
--- /dev/null
+++ b/i386/i386at/nfd.c
@@ -0,0 +1,1484 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+#include <fd.h>
+#if NFD > 0
+/*
+ * Floppy disk driver.
+ *
+ * Supports:
+ * 1 controller and 2 drives.
+ * Media change and automatic media detection.
+ * Arbitrarily sized read/write requests.
+ * Misaligned requests
+ * DMA above 16 Meg
+ *
+ * TODO:
+ * 1) Real probe routines for controller and drives.
+ * 2) Support for multiple controllers. The driver does
+ * not assume a single controller since all functions
+ * take the controller and/or device structure as an
+ * argument, however the probe routines limit the
+ * number of controllers and drives to 1 and 2 respectively.
+ * 3) V_VERIFY ioctl.
+ * 4) User defined diskette parameters.
+ * 5) Detect Intel 82077 or compatible and use its FIFO mode.
+ *
+ * Shantanu Goel (goel@cs.columbia.edu)
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <vm/pmap.h>
+#include <device/param.h>
+#include <device/buf.h>
+#include <device/errno.h>
+#include <chips/busses.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/cram.h>
+#include <i386at/disk.h>
+#include <i386at/nfdreg.h>
+
+/*
+ * Number of drives supported by an FDC.
+ * The controller is actually capable of
+ * supporting 4 drives, however, most (all?)
+ * board implementations only support 2.
+ */
+#define NDRIVES_PER_FDC 2
+#define NFDC ((NFD + NDRIVES_PER_FDC - 1) / NDRIVES_PER_FDC)
+
+#define fdunit(dev) (((int)(dev) >> 6) & 3)
+#define fdmedia(dev) ((int)(dev) & 3)
+
+#define b_cylin b_resid
+#define B_FORMAT B_MD1
+
+#define SECSIZE 512
+
+#define DMABSIZE (18*1024) /* size of DMA bounce buffer */
+
+#define OP_TIMEOUT 5 /* time to wait (secs) for an
+ operation before giving up */
+#define MOTOR_TIMEOUT 5 /* time to wait (secs) before turning
+ off an idle drive motor */
+#define MAX_RETRIES 48 /* number of times to try
+ an I/O operation */
+
+#define SRTHUT 0xdf /* step rate/head unload time */
+#define HLTND 0x02 /* head load time/dma mode */
+
+/*
+ * DMA controller.
+ *
+ * XXX: There should be a generic <i386/dma.h> file.
+ */
+
+/*
+ * Ports
+ */
+#define DMA2_PAGE 0x81 /* channel 2, page register */
+#define DMA2_ADDR 0x04 /* channel 2, addr register */
+#define DMA2_COUNT 0x05 /* channel 2, count register */
+#define DMA_STATUS 0x08 /* status register */
+#define DMA_COMMAND 0x08 /* command register */
+#define DMA_WREQ 0x09 /* request register */
+#define DMA_SINGLEMSK 0x0a /* single mask register */
+#define DMA_MODE 0x0b /* mode register */
+#define DMA_FLIPFLOP 0x0c /* pointer flip/flop */
+#define DMA_TEMP 0x0d /* temporary register */
+#define DMA_MASTERCLR 0x0d /* master clear */
+#define DMA_CLRMASK 0x0e /* clear mask register */
+#define DMA_ALLMASK 0x0f /* all mask register */
+
+/*
+ * Commands
+ */
+#define DMA_WRITE 0x46 /* write on channel 2 */
+#define DMA_READ 0x4a /* read on channel 2 */
+
+/*
+ * Autoconfiguration stuff.
+ */
+struct bus_ctlr *fdminfo[NFDC];
+struct bus_device *fddinfo[NFD];
+int fdstd[] = { 0 };
+int fdprobe(), fdslave(), fdintr();
+void fdattach();
+struct bus_driver fddriver = {
+ fdprobe, fdslave, fdattach, 0, fdstd, "fd", fddinfo, "fdc", fdminfo
+};
+
+/*
+ * Per-controller state.
+ */
+struct fdcsoftc {
+ int sc_flags;
+#define FDF_WANT 0x01 /* someone needs direct controller access */
+#define FDF_RESET 0x02 /* controller needs reset */
+#define FDF_LIMIT 0x04 /* limit transfer to a single sector */
+#define FDF_BOUNCE 0x08 /* using bounce buffer */
+ int sc_state; /* transfer fsm */
+ caddr_t sc_addr; /* buffer address */
+ int sc_resid; /* amount left to transfer */
+ int sc_amt; /* amount currently being transferred */
+ int sc_op; /* operation being performed */
+ int sc_mode; /* DMA mode */
+ int sc_sn; /* sector number */
+ int sc_tn; /* track number */
+ int sc_cn; /* cylinder number */
+ int sc_recalerr; /* # recalibration errors */
+ int sc_seekerr; /* # seek errors */
+ int sc_ioerr; /* # i/o errors */
+ int sc_dor; /* copy of digital output register */
+ int sc_rate; /* copy of transfer rate register */
+ int sc_wticks; /* watchdog */
+ u_int sc_buf; /* buffer for transfers > 16 Meg */
+ u_char sc_cmd[9]; /* command buffer */
+ u_char sc_results[7]; /* operation results */
+} fdcsoftc[NFDC];
+
+#define sc_st0 sc_results[0]
+#define sc_st3 sc_results[0]
+#define sc_st1 sc_results[1]
+#define sc_pcn sc_results[1]
+#define sc_st2 sc_results[2]
+#define sc_c sc_results[3]
+#define sc_h sc_results[4]
+#define sc_r sc_results[5]
+#define sc_n sc_results[6]
+
+/*
+ * Transfer states.
+ */
+#define IDLE 0 /* controller is idle */
+#define RESET 1 /* reset controller */
+#define RESETDONE 2 /* reset completion interrupt */
+#define RECAL 3 /* recalibrate drive */
+#define RECALDONE 4 /* recalibration complete interrupt */
+#define SEEK 5 /* perform seek on drive */
+#define SEEKDONE 6 /* seek completion interrupt */
+#define TRANSFER 7 /* perform transfer on drive */
+#define TRANSFERDONE 8 /* transfer completion interrupt */
+
+/*
+ * Per-drive state.
+ */
+struct fdsoftc {
+ int sc_flags;
+#define FDF_RECAL 0x02 /* drive needs recalibration */
+#define FDF_SEEK 0x04 /* force seek during auto-detection */
+#define FDF_AUTO 0x08 /* performing auto-density */
+#define FDF_AUTOFORCE 0x10 /* force auto-density */
+#define FDF_INIT 0x20 /* drive is being initialized */
+ int sc_type; /* drive type */
+ struct fddk *sc_dk; /* diskette type */
+ int sc_cyl; /* current head position */
+ int sc_mticks; /* motor timeout */
+} fdsoftc[NFD];
+
+struct buf fdtab[NFDC]; /* controller queues */
+struct buf fdutab[NFD]; /* drive queues */
+
+/*
+ * Floppy drive type names.
+ */
+char *fdnames[] = { "360K", "1.2 Meg", "720K", "1.44 Meg" };
+#define NTYPES (sizeof(fdnames) / sizeof(fdnames[0]))
+
+/*
+ * Floppy diskette parameters.
+ */
+struct fddk {
+ int dk_nspu; /* sectors/unit */
+ int dk_nspc; /* sectors/cylinder */
+ int dk_ncyl; /* cylinders/unit */
+ int dk_nspt; /* sectors/track */
+ int dk_step; /* !=0 means double track steps */
+ int dk_gap; /* read/write gap length */
+ int dk_fgap; /* format gap length */
+ int dk_rate; /* transfer rate */
+ int dk_drives; /* bit mask of drives that accept diskette */
+ char *dk_name; /* type name */
+} fddk[] = {
+ /*
+ * NOTE: largest density for each drive type must be first so
+ * fdauto() tries it before any lower ones.
+ */
+ { 2880, 36, 80, 18, 0, 0x1b, 0x6c, 0x00, 0x08, "1.44 Meg" },
+ { 2400, 30, 80, 15, 0, 0x1b, 0x54, 0x00, 0x02, "1.2 Meg" },
+ { 1440, 18, 80, 9, 0, 0x2a, 0x50, 0x02, 0x0c, "720K" },
+ { 720, 18, 40, 9, 1, 0x23, 0x50, 0x01, 0x02, "360K" },
+ { 720, 18, 40, 9, 0, 0x2a, 0x50, 0x02, 0x01, "360K PC" }
+};
+#define NDKTYPES (sizeof(fddk) / sizeof(fddk[0]))
+
+/*
+ * For compatibility with old driver.
+ * This array is indexed by the old floppy type codes
+ * and points to the corresponding entry for that
+ * type in fddk[] above.
+ */
+struct fddk *fdcompat[NDKTYPES];
+
+int fdwstart = 0;
+int fdstrategy(), fdformat();
+char *fderrmsg();
+void fdwatch(), fdminphys(), fdspinup(), wakeup();
+
+#define FDDEBUG
+#ifdef FDDEBUG
+int fddebug = 0;
+#define DEBUGF(n, stmt) { if (fddebug >= (n)) stmt; }
+#else
+#define DEBUGF(n, stmt)
+#endif
+
+/*
+ * Probe for a controller.
+ */
+int
+fdprobe(xxx, um)
+ int xxx;
+ struct bus_ctlr *um;
+{
+ struct fdcsoftc *fdc;
+
+ if (um->unit >= NFDC) {
+ printf("fdc%d: not configured\n", um->unit);
+ return (0);
+ }
+ if (um->unit > 0) /* XXX: only 1 controller */
+ return (0);
+
+ /*
+ * XXX: need real probe
+ */
+ take_ctlr_irq(um);
+ printf("%s%d: port 0x%x, spl %d, pic %d.\n",
+ um->name, um->unit, um->address, um->sysdep, um->sysdep1);
+
+ /*
+ * Set up compatibility array.
+ */
+ fdcompat[0] = &fddk[2];
+ fdcompat[1] = &fddk[0];
+ fdcompat[2] = &fddk[3];
+ fdcompat[3] = &fddk[1];
+
+ fdc = &fdcsoftc[um->unit];
+ fdc->sc_rate = -1;
+ if (!fdc->sc_buf) {
+ fdc->sc_buf = alloc_dma_mem(DMABSIZE, 64*1024);
+ if (fdc->sc_buf == 0)
+ panic("fd: alloc_dma_mem() failed");
+ }
+ fdc->sc_dor = DOR_RSTCLR | DOR_IENABLE;
+ outb(FD_DOR(um->address), fdc->sc_dor);
+ return (1);
+}
+
+/*
+ * Probe for a drive.
+ */
+int
+fdslave(ui)
+ struct bus_device *ui;
+{
+ struct fdsoftc *sc;
+
+ if (ui->unit >= NFD) {
+ printf("fd%d: not configured\n", ui->unit);
+ return (0);
+ }
+ if (ui->unit > 1) /* XXX: only 2 drives */
+ return (0);
+
+ /*
+ * Find out from CMOS if drive exists.
+ */
+ sc = &fdsoftc[ui->unit];
+ outb(CMOS_ADDR, 0x10);
+ sc->sc_type = inb(CMOS_DATA);
+ if (ui->unit == 0)
+ sc->sc_type >>= 4;
+ sc->sc_type &= 0x0f;
+ return (sc->sc_type);
+}
+
+/*
+ * Attach a drive to the system.
+ */
+void
+fdattach(ui)
+ struct bus_device *ui;
+{
+ struct fdsoftc *sc;
+
+ sc = &fdsoftc[ui->unit];
+ if (--sc->sc_type >= NTYPES) {
+ printf(": unknown drive type %d", sc->sc_type);
+ ui->alive = 0;
+ return;
+ }
+ printf(": %s", fdnames[sc->sc_type]);
+ sc->sc_flags = FDF_RECAL | FDF_SEEK | FDF_AUTOFORCE;
+}
+
+int
+fdopen(dev, mode)
+ dev_t dev;
+ int mode;
+{
+ int unit = fdunit(dev), error;
+ struct bus_device *ui;
+ struct fdsoftc *sc;
+
+ if (unit >= NFD || (ui = fddinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+
+ /*
+ * Start watchdog.
+ */
+ if (!fdwstart) {
+ fdwstart++;
+ timeout(fdwatch, 0, hz);
+ }
+ /*
+ * Do media detection if drive is being opened for the
+ * first time or diskette has been changed since the last open.
+ */
+ sc = &fdsoftc[unit];
+ if ((sc->sc_flags & FDF_AUTOFORCE) || fddskchg(ui)) {
+ if (error = fdauto(dev))
+ return (error);
+ sc->sc_flags &= ~FDF_AUTOFORCE;
+ }
+ return (0);
+}
+
+int
+fdclose(dev)
+ dev_t dev;
+{
+ int s, unit = fdunit(dev);
+ struct fdsoftc *sc = &fdsoftc[unit];
+
+ /*
+ * Wait for pending operations to complete.
+ */
+ s = splbio();
+ while (fdutab[unit].b_active) {
+ sc->sc_flags |= FDF_WANT;
+ assert_wait((event_t)sc, FALSE);
+ thread_block((void (*)())0);
+ }
+ splx(s);
+ return (0);
+}
+
+int
+fdread(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ return (block_io(fdstrategy, fdminphys, ior));
+}
+
+int
+fdwrite(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ return (block_io(fdstrategy, fdminphys, ior));
+}
+
+int
+fdgetstat(dev, flavor, status, status_count)
+ dev_t dev;
+ dev_flavor_t flavor;
+ dev_status_t status;
+ mach_msg_type_number_t *status_count;
+{
+ switch (flavor) {
+
+ case DEV_GET_SIZE:
+ {
+ int *info;
+ io_return_t error;
+ struct disk_parms dp;
+
+ if (error = fdgetparms(dev, &dp))
+ return (error);
+ info = (int *)status;
+ info[DEV_GET_SIZE_DEVICE_SIZE] = dp.dp_pnumsec * SECSIZE;
+ info[DEV_GET_SIZE_RECORD_SIZE] = SECSIZE;
+ *status_count = DEV_GET_SIZE_COUNT;
+ return (D_SUCCESS);
+ }
+ case V_GETPARMS:
+ if (*status_count < (sizeof(struct disk_parms) / sizeof(int)))
+ return (D_INVALID_OPERATION);
+ *status_count = sizeof(struct disk_parms) / sizeof(int);
+ return (fdgetparms(dev, (struct disk_parms *)status));
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+}
+
+int
+fdsetstat(dev, flavor, status, status_count)
+ dev_t dev;
+ dev_flavor_t flavor;
+ dev_status_t status;
+ mach_msg_type_number_t status_count;
+{
+ switch (flavor) {
+
+ case V_SETPARMS:
+ return (fdsetparms(dev, *(int *)status));
+
+ case V_FORMAT:
+ return (fdformat(dev, (union io_arg *)status));
+
+ case V_VERIFY:
+ /*
+ * XXX: needs to be implemented
+ */
+ return (D_SUCCESS);
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+}
+
+int
+fddevinfo(dev, flavor, info)
+ dev_t dev;
+ int flavor;
+ char *info;
+{
+ switch (flavor) {
+
+ case D_INFO_BLOCK_SIZE:
+ *(int *)info = SECSIZE;
+ return (D_SUCCESS);
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+}
+
+/*
+ * Allow arbitrary transfers. Standard minphys restricts
+ * transfers to a maximum of 256K preventing us from reading
+ * an entire diskette in a single system call.
+ */
+void
+fdminphys(ior)
+ io_req_t ior;
+{
+}
+
+/*
+ * Return current media parameters.
+ */
+int
+fdgetparms(dev, dp)
+ dev_t dev;
+ struct disk_parms *dp;
+{
+ struct fddk *dk = fdsoftc[fdunit(dev)].sc_dk;
+
+ dp->dp_type = DPT_FLOPPY;
+ dp->dp_heads = 2;
+ dp->dp_sectors = dk->dk_nspt;
+ dp->dp_pstartsec = 0;
+ dp->dp_cyls = dk->dk_ncyl;
+ dp->dp_pnumsec = dk->dk_nspu;
+ return (0);
+}
+
+/*
+ * Set media parameters.
+ */
+int
+fdsetparms(dev, type)
+ dev_t dev;
+ int type;
+{
+ struct fdsoftc *sc;
+ struct fddk *dk;
+
+ if (type < 0 || type >= NDKTYPES)
+ return (EINVAL);
+ dk = fdcompat[type];
+ sc = &fdsoftc[fdunit(dev)];
+ if ((dk->dk_drives & (1 << sc->sc_type)) == 0)
+ return (EINVAL);
+ sc->sc_dk = dk;
+ return (D_SUCCESS);
+}
+
+/*
+ * Format a floppy.
+ */
+int
+fdformat(dev, arg)
+ dev_t dev;
+ union io_arg *arg;
+{
+ int i, j, sect, error = 0;
+ unsigned track, num_trks;
+ struct buf *bp;
+ struct fddk *dk;
+ struct format_info *fmt;
+
+ dk = fdsoftc[fdunit(dev)].sc_dk;
+ num_trks = arg->ia_fmt.num_trks;
+ track = arg->ia_fmt.start_trk;
+ if (num_trks == 0 || track + num_trks > (dk->dk_ncyl << 1)
+ || arg->ia_fmt.intlv >= dk->dk_nspt)
+ return (EINVAL);
+
+ bp = (struct buf *)geteblk(SECSIZE);
+ bp->b_dev = dev;
+ bp->b_bcount = dk->dk_nspt * sizeof(struct format_info);
+ bp->b_blkno = track * dk->dk_nspt;
+
+ while (num_trks-- > 0) {
+ /*
+ * Set up format information.
+ */
+ fmt = (struct format_info *)bp->b_un.b_addr;
+ for (i = 0; i < dk->dk_nspt; i++)
+ fmt[i].sector = 0;
+ for (i = 0, j = 0, sect = 1; i < dk->dk_nspt; i++) {
+ fmt[j].cyl = track >> 1;
+ fmt[j].head = track & 1;
+ fmt[j].sector = sect++;
+ fmt[j].secsize = 2;
+ if ((j += arg->ia_fmt.intlv) < dk->dk_nspt)
+ continue;
+ for (j -= dk->dk_nspt; j < dk->dk_nspt; j++)
+ if (fmt[j].sector == 0)
+ break;
+ }
+ bp->b_flags = B_FORMAT;
+ fdstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ error = bp->b_error;
+ break;
+ }
+ bp->b_blkno += dk->dk_nspt;
+ track++;
+ }
+ bp->b_flags &= ~B_FORMAT;
+ brelse(bp);
+ return (error);
+}
+
+/*
+ * Strategy routine.
+ * Enqueue a request on drive queue.
+ */
+int
+fdstrategy(bp)
+ struct buf *bp;
+{
+ int unit = fdunit(bp->b_dev), s;
+ int bn, sz, maxsz;
+ struct buf *dp;
+ struct bus_device *ui = fddinfo[unit];
+ struct fddk *dk = fdsoftc[unit].sc_dk;
+
+ bn = bp->b_blkno;
+ sz = (bp->b_bcount + SECSIZE - 1) / SECSIZE;
+ maxsz = dk->dk_nspu;
+ if (bn < 0 || bn + sz > maxsz) {
+ if (bn == maxsz) {
+ bp->b_resid = bp->b_bcount;
+ goto done;
+ }
+ sz = maxsz - bn;
+ if (sz <= 0) {
+ bp->b_error = EINVAL;
+ bp->b_flags |= B_ERROR;
+ goto done;
+ }
+ bp->b_bcount = sz * SECSIZE;
+ }
+ bp->b_cylin = bn / dk->dk_nspc;
+ dp = &fdutab[unit];
+ s = splbio();
+ disksort(dp, bp);
+ if (!dp->b_active) {
+ fdustart(ui);
+ if (!fdtab[ui->mi->unit].b_active)
+ fdstart(ui->mi);
+ }
+ splx(s);
+ return;
+ done:
+ biodone(bp);
+ return;
+}
+
+/*
+ * Unit start routine.
+ * Move request from drive to controller queue.
+ */
+int
+fdustart(ui)
+ struct bus_device *ui;
+{
+ struct buf *bp;
+ struct buf *dp;
+
+ bp = &fdutab[ui->unit];
+ if (bp->b_actf == 0)
+ return;
+ dp = &fdtab[ui->mi->unit];
+ if (dp->b_actf == 0)
+ dp->b_actf = bp;
+ else
+ dp->b_actl->b_forw = bp;
+ bp->b_forw = 0;
+ dp->b_actl = bp;
+ bp->b_active++;
+}
+
+/*
+ * Start output on controller.
+ */
+int
+fdstart(um)
+ struct bus_ctlr *um;
+{
+ struct buf *bp;
+ struct buf *dp;
+ struct fdsoftc *sc;
+ struct fdcsoftc *fdc;
+ struct bus_device *ui;
+ struct fddk *dk;
+
+ /*
+ * Pull a request from the controller queue.
+ */
+ dp = &fdtab[um->unit];
+ if ((bp = dp->b_actf) == 0)
+ return;
+ bp = bp->b_actf;
+
+ fdc = &fdcsoftc[um->unit];
+ ui = fddinfo[fdunit(bp->b_dev)];
+ sc = &fdsoftc[ui->unit];
+ dk = sc->sc_dk;
+
+ /*
+ * Mark controller busy.
+ */
+ dp->b_active++;
+
+ /*
+ * Figure out where this request is going.
+ */
+ fdc->sc_cn = bp->b_cylin;
+ fdc->sc_sn = bp->b_blkno % dk->dk_nspc;
+ fdc->sc_tn = fdc->sc_sn / dk->dk_nspt;
+ fdc->sc_sn %= dk->dk_nspt;
+
+ /*
+ * Set up for multi-sector transfer.
+ */
+ fdc->sc_op = ((bp->b_flags & B_FORMAT) ? CMD_FORMAT
+ : ((bp->b_flags & B_READ) ? CMD_READ : CMD_WRITE));
+ fdc->sc_mode = (bp->b_flags & B_READ) ? DMA_WRITE : DMA_READ;
+ fdc->sc_addr = bp->b_un.b_addr;
+ fdc->sc_resid = bp->b_bcount;
+ fdc->sc_wticks = 0;
+ fdc->sc_recalerr = 0;
+ fdc->sc_seekerr = 0;
+ fdc->sc_ioerr = 0;
+
+ /*
+ * Set initial transfer state.
+ */
+ if (fdc->sc_flags & FDF_RESET)
+ fdc->sc_state = RESET;
+ else if (sc->sc_flags & FDF_RECAL)
+ fdc->sc_state = RECAL;
+ else if (sc->sc_cyl != fdc->sc_cn)
+ fdc->sc_state = SEEK;
+ else
+ fdc->sc_state = TRANSFER;
+
+ /*
+ * Set transfer rate.
+ */
+ if (fdc->sc_rate != dk->dk_rate) {
+ fdc->sc_rate = dk->dk_rate;
+ outb(FD_RATE(um->address), fdc->sc_rate);
+ }
+ /*
+ * Turn on drive motor.
+ * Don't start I/O if drive is spinning up.
+ */
+ if (fdmotoron(ui)) {
+ timeout(fdspinup, (void *)um, hz / 2);
+ return;
+ }
+ /*
+ * Call transfer state routine to do the actual I/O.
+ */
+ fdstate(um);
+}
+
+/*
+ * Interrupt routine.
+ */
+int
+fdintr(ctlr)
+ int ctlr;
+{
+ int timedout;
+ u_char results[7];
+ struct buf *bp;
+ struct bus_device *ui;
+ struct fdsoftc *sc;
+ struct buf *dp = &fdtab[ctlr];
+ struct fdcsoftc *fdc = &fdcsoftc[ctlr];
+ struct bus_ctlr *um = fdminfo[ctlr];
+
+ if (!dp->b_active) {
+ printf("fdc%d: stray interrupt\n", ctlr);
+ return;
+ }
+ timedout = fdc->sc_wticks >= OP_TIMEOUT;
+ fdc->sc_wticks = 0;
+ bp = dp->b_actf->b_actf;
+ ui = fddinfo[fdunit(bp->b_dev)];
+ sc = &fdsoftc[ui->unit];
+
+ /*
+ * Operation timed out, terminate request.
+ */
+ if (timedout) {
+ fderror("timed out", ui);
+ fdmotoroff(ui);
+ sc->sc_flags |= FDF_RECAL;
+ bp->b_flags |= B_ERROR;
+ bp->b_error = ENXIO;
+ fddone(ui, bp);
+ return;
+ }
+ /*
+ * Read results from FDC.
+ * For transfer completion they can be read immediately.
+ * For anything else, we must issue a Sense Interrupt
+ * Status Command. We keep issuing this command till
+ * FDC returns invalid command status. The Controller Busy
+ * bit in the status register indicates completion of a
+ * read/write/format operation.
+ */
+ if (inb(FD_STATUS(um->address)) & ST_CB) {
+ if (!fdresults(um, fdc->sc_results))
+ return;
+ } else {
+ while (1) {
+ fdc->sc_cmd[0] = CMD_SENSEI;
+ if (!fdcmd(um, 1)) {
+ DEBUGF(2, printf(2, "fd%d: SENSEI failed\n"));
+ return;
+ }
+ if (!fdresults(um, results))
+ return;
+ if ((results[0] & ST0_IC) == 0x80)
+ break;
+ if ((results[0] & ST0_US) == ui->slave) {
+ fdc->sc_results[0] = results[0];
+ fdc->sc_results[1] = results[1];
+ }
+ }
+ }
+ /*
+ * Let transfer state routine handle the rest.
+ */
+ fdstate(um);
+}
+
+/*
+ * Transfer finite state machine driver.
+ */
+int
+fdstate(um)
+ struct bus_ctlr *um;
+{
+ int unit, max, pa, s;
+ struct buf *bp;
+ struct fdsoftc *sc;
+ struct bus_device *ui;
+ struct fddk *dk;
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+
+ bp = fdtab[um->unit].b_actf->b_actf;
+ ui = fddinfo[fdunit(bp->b_dev)];
+ sc = &fdsoftc[ui->unit];
+ dk = sc->sc_dk;
+
+ while (1) switch (fdc->sc_state) {
+
+ case RESET:
+ /*
+ * Reset the controller.
+ */
+ fdreset(um);
+ return;
+
+ case RESETDONE:
+ /*
+ * Reset complete.
+ * Mark all drives as needing recalibration
+ * and issue specify command.
+ */
+ for (unit = 0; unit < NFD; unit++)
+ if (fddinfo[unit] && fddinfo[unit]->alive
+ && fddinfo[unit]->mi == um)
+ fdsoftc[unit].sc_flags |= FDF_RECAL;
+ fdc->sc_cmd[0] = CMD_SPECIFY;
+ fdc->sc_cmd[1] = SRTHUT;
+ fdc->sc_cmd[2] = HLTND;
+ if (!fdcmd(um, 3))
+ return;
+ fdc->sc_flags &= ~FDF_RESET;
+ fdc->sc_state = RECAL;
+ break;
+
+ case RECAL:
+ /*
+ * Recalibrate drive.
+ */
+ fdc->sc_state = RECALDONE;
+ fdc->sc_cmd[0] = CMD_RECAL;
+ fdc->sc_cmd[1] = ui->slave;
+ fdcmd(um, 2);
+ return;
+
+ case RECALDONE:
+ /*
+ * Recalibration complete.
+ */
+ if ((fdc->sc_st0 & ST0_IC) || (fdc->sc_st0 & ST0_EC)) {
+ if (++fdc->sc_recalerr == 2) {
+ fderror("recalibrate failed", ui);
+ goto bad;
+ }
+ fdc->sc_state = RESET;
+ break;
+ }
+ sc->sc_flags &= ~FDF_RECAL;
+ fdc->sc_recalerr = 0;
+ sc->sc_cyl = -1;
+ fdc->sc_state = SEEK;
+ break;
+
+ case SEEK:
+ /*
+ * Perform seek operation.
+ */
+ fdc->sc_state = SEEKDONE;
+ fdc->sc_cmd[0] = CMD_SEEK;
+ fdc->sc_cmd[1] = (fdc->sc_tn << 2) | ui->slave;
+ fdc->sc_cmd[2] = fdc->sc_cn;
+ if (dk->dk_step)
+ fdc->sc_cmd[2] <<= 1;
+ fdcmd(um, 3);
+ return;
+
+ case SEEKDONE:
+ /*
+ * Seek complete.
+ */
+ if (dk->dk_step)
+ fdc->sc_pcn >>= 1;
+ if ((fdc->sc_st0 & ST0_IC) || (fdc->sc_st0 & ST0_SE) == 0
+ || fdc->sc_pcn != fdc->sc_cn) {
+ if (++fdc->sc_seekerr == 2) {
+ fderror("seek failed", ui);
+ goto bad;
+ }
+ fdc->sc_state = RESET;
+ break;
+ }
+ fdc->sc_seekerr = 0;
+ sc->sc_cyl = fdc->sc_pcn;
+ fdc->sc_state = TRANSFER;
+ break;
+
+ case TRANSFER:
+ /*
+ * Perform I/O transfer.
+ */
+ fdc->sc_flags &= ~FDF_BOUNCE;
+ pa = pmap_extract(kernel_pmap, fdc->sc_addr);
+ if (fdc->sc_op == CMD_FORMAT) {
+ max = sizeof(struct format_info) * dk->dk_nspt;
+ } else if (fdc->sc_flags & FDF_LIMIT) {
+ fdc->sc_flags &= ~FDF_LIMIT;
+ max = SECSIZE;
+ } else {
+ max = (dk->dk_nspc - dk->dk_nspt * fdc->sc_tn
+ - fdc->sc_sn) * SECSIZE;
+ }
+ if (max > fdc->sc_resid)
+ max = fdc->sc_resid;
+ if (pa >= 16*1024*1024) {
+ fdc->sc_flags |= FDF_BOUNCE;
+ pa = fdc->sc_buf;
+ if (max < DMABSIZE)
+ fdc->sc_amt = max;
+ else
+ fdc->sc_amt = DMABSIZE;
+ } else {
+ int prevpa, curpa, omax;
+ vm_offset_t va;
+
+ omax = max;
+ if (max > 65536 - (pa & 0xffff))
+ max = 65536 - (pa & 0xffff);
+ fdc->sc_amt = I386_PGBYTES - (pa & (I386_PGBYTES - 1));
+ va = (vm_offset_t)fdc->sc_addr + fdc->sc_amt;
+ prevpa = pa & ~(I386_PGBYTES - 1);
+ while (fdc->sc_amt < max) {
+ curpa = pmap_extract(kernel_pmap, va);
+ if (curpa >= 16*1024*1024
+ || curpa != prevpa + I386_PGBYTES)
+ break;
+ fdc->sc_amt += I386_PGBYTES;
+ va += I386_PGBYTES;
+ prevpa = curpa;
+ }
+ if (fdc->sc_amt > max)
+ fdc->sc_amt = max;
+ if (fdc->sc_op == CMD_FORMAT) {
+ if (fdc->sc_amt != omax) {
+ fdc->sc_flags |= FDF_BOUNCE;
+ pa = fdc->sc_buf;
+ fdc->sc_amt = omax;
+ }
+ } else if (fdc->sc_amt != fdc->sc_resid) {
+ if (fdc->sc_amt < SECSIZE) {
+ fdc->sc_flags |= FDF_BOUNCE;
+ pa = fdc->sc_buf;
+ if (omax > DMABSIZE)
+ fdc->sc_amt = DMABSIZE;
+ else
+ fdc->sc_amt = omax;
+ } else
+ fdc->sc_amt &= ~(SECSIZE - 1);
+ }
+ }
+
+ DEBUGF(2, printf("fd%d: TRANSFER: amt %d cn %d tn %d sn %d\n",
+ ui->unit, fdc->sc_amt, fdc->sc_cn,
+ fdc->sc_tn, fdc->sc_sn + 1));
+
+ if ((fdc->sc_flags & FDF_BOUNCE) && fdc->sc_op != CMD_READ) {
+ fdc->sc_flags &= ~FDF_BOUNCE;
+ bcopy(fdc->sc_addr, (caddr_t)phystokv(fdc->sc_buf),
+ fdc->sc_amt);
+ }
+ /*
+ * Set up DMA.
+ */
+ s = sploff();
+ outb(DMA_SINGLEMSK, 0x04 | 0x02);
+ outb(DMA_FLIPFLOP, 0);
+ outb(DMA_MODE, fdc->sc_mode);
+ outb(DMA2_ADDR, pa);
+ outb(DMA2_ADDR, pa >> 8);
+ outb(DMA2_PAGE, pa >> 16);
+ outb(DMA2_COUNT, fdc->sc_amt - 1);
+ outb(DMA2_COUNT, (fdc->sc_amt - 1) >> 8);
+ outb(DMA_SINGLEMSK, 0x02);
+ splon(s);
+
+ /*
+ * Issue command to FDC.
+ */
+ fdc->sc_state = TRANSFERDONE;
+ fdc->sc_cmd[0] = fdc->sc_op;
+ fdc->sc_cmd[1] = (fdc->sc_tn << 2) | ui->slave;
+ if (fdc->sc_op == CMD_FORMAT) {
+ fdc->sc_cmd[2] = 0x02;
+ fdc->sc_cmd[3] = dk->dk_nspt;
+ fdc->sc_cmd[4] = dk->dk_fgap;
+ fdc->sc_cmd[5] = 0xda;
+ fdcmd(um, 6);
+ } else {
+ fdc->sc_cmd[2] = fdc->sc_cn;
+ fdc->sc_cmd[3] = fdc->sc_tn;
+ fdc->sc_cmd[4] = fdc->sc_sn + 1;
+ fdc->sc_cmd[5] = 0x02;
+ fdc->sc_cmd[6] = dk->dk_nspt;
+ fdc->sc_cmd[7] = dk->dk_gap;
+ fdc->sc_cmd[8] = 0xff;
+ fdcmd(um, 9);
+ }
+ return;
+
+ case TRANSFERDONE:
+ /*
+ * Transfer complete.
+ */
+ if (fdc->sc_st0 & ST0_IC) {
+ fdc->sc_ioerr++;
+ if (sc->sc_flags & FDF_AUTO) {
+ /*
+ * Give up on second try if
+ * media detection is in progress.
+ */
+ if (fdc->sc_ioerr == 2)
+ goto bad;
+ fdc->sc_state = RECAL;
+ break;
+ }
+ if (fdc->sc_ioerr == MAX_RETRIES) {
+ fderror(fderrmsg(ui), ui);
+ goto bad;
+ }
+ /*
+ * Give up immediately on write-protected diskettes.
+ */
+ if (fdc->sc_st1 & ST1_NW) {
+ fderror("write-protected diskette", ui);
+ goto bad;
+ }
+ /*
+ * Limit transfer to a single sector.
+ */
+ fdc->sc_flags |= FDF_LIMIT;
+ /*
+ * Every fourth attempt recalibrate the drive.
+ * Every eight attempt reset the controller.
+ * Also, every eighth attempt inform user
+ * about the error.
+ */
+ if (fdc->sc_ioerr & 3)
+ fdc->sc_state = TRANSFER;
+ else if (fdc->sc_ioerr & 7)
+ fdc->sc_state = RECAL;
+ else {
+ fdc->sc_state = RESET;
+ fderror(fderrmsg(ui), ui);
+ }
+ break;
+ }
+ /*
+ * Transfer completed successfully.
+ * Advance counters/pointers, and if more
+ * is left, initiate I/O.
+ */
+ if (fdc->sc_flags & FDF_BOUNCE) {
+ fdc->sc_flags &= ~FDF_BOUNCE;
+ bcopy((caddr_t)phystokv(fdc->sc_buf), fdc->sc_addr,
+ fdc->sc_amt);
+ }
+ if ((fdc->sc_resid -= fdc->sc_amt) == 0) {
+ bp->b_resid = 0;
+ fddone(ui, bp);
+ return;
+ }
+ fdc->sc_state = TRANSFER;
+ fdc->sc_ioerr = 0;
+ fdc->sc_addr += fdc->sc_amt;
+ if (fdc->sc_op == CMD_FORMAT) {
+ fdc->sc_sn = 0;
+ if (fdc->sc_tn == 1) {
+ fdc->sc_tn = 0;
+ fdc->sc_cn++;
+ fdc->sc_state = SEEK;
+ } else
+ fdc->sc_tn = 1;
+ } else {
+ fdc->sc_sn += fdc->sc_amt / SECSIZE;
+ while (fdc->sc_sn >= dk->dk_nspt) {
+ fdc->sc_sn -= dk->dk_nspt;
+ if (fdc->sc_tn == 1) {
+ fdc->sc_tn = 0;
+ fdc->sc_cn++;
+ fdc->sc_state = SEEK;
+ } else
+ fdc->sc_tn = 1;
+ }
+ }
+ break;
+
+ default:
+ printf("fd%d: invalid state\n", ui->unit);
+ panic("fdstate");
+ /*NOTREACHED*/
+ }
+ bad:
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ sc->sc_flags |= FDF_RECAL;
+ fddone(ui, bp);
+}
+
+/*
+ * Terminate current request and start
+ * any others that are queued.
+ */
+int
+fddone(ui, bp)
+ struct bus_device *ui;
+ struct buf *bp;
+{
+ struct bus_ctlr *um = ui->mi;
+ struct fdsoftc *sc = &fdsoftc[ui->unit];
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+ struct buf *dp = &fdtab[um->unit];
+
+ DEBUGF(1, printf("fd%d: fddone()\n", ui->unit));
+
+ /*
+ * Remove this request from queue.
+ */
+ if (bp) {
+ fdutab[ui->unit].b_actf = bp->b_actf;
+ biodone(bp);
+ bp = &fdutab[ui->unit];
+ dp->b_actf = bp->b_forw;
+ } else
+ bp = &fdutab[ui->unit];
+
+ /*
+ * Mark controller and drive idle.
+ */
+ dp->b_active = 0;
+ bp->b_active = 0;
+ fdc->sc_state = IDLE;
+ sc->sc_mticks = 0;
+ fdc->sc_flags &= ~(FDF_LIMIT|FDF_BOUNCE);
+
+ /*
+ * Start up other requests.
+ */
+ fdustart(ui);
+ fdstart(um);
+
+ /*
+ * Wakeup anyone waiting for drive or controller.
+ */
+ if (sc->sc_flags & FDF_WANT) {
+ sc->sc_flags &= ~FDF_WANT;
+ wakeup((void *)sc);
+ }
+ if (fdc->sc_flags & FDF_WANT) {
+ fdc->sc_flags &= ~FDF_WANT;
+ wakeup((void *)fdc);
+ }
+}
+
+/*
+ * Check if diskette change has occured since the last open.
+ */
+int
+fddskchg(ui)
+ struct bus_device *ui;
+{
+ int s, dir;
+ struct fdsoftc *sc = &fdsoftc[ui->unit];
+ struct bus_ctlr *um = ui->mi;
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+
+ /*
+ * Get access to controller.
+ */
+ s = splbio();
+ while (fdtab[um->unit].b_active) {
+ fdc->sc_flags |= FDF_WANT;
+ assert_wait((event_t)fdc, FALSE);
+ thread_block((void (*)())0);
+ }
+ fdtab[um->unit].b_active = 1;
+ fdutab[ui->unit].b_active = 1;
+
+ /*
+ * Turn on drive motor and read digital input register.
+ */
+ if (fdmotoron(ui)) {
+ timeout(wakeup, (void *)fdc, hz / 2);
+ assert_wait((event_t)fdc, FALSE);
+ thread_block((void (*)())0);
+ }
+ dir = inb(FD_DIR(um->address));
+ fddone(ui, NULL);
+ splx(s);
+
+ if (dir & DIR_DSKCHG) {
+ printf("fd%d: diskette change detected\n", ui->unit);
+ sc->sc_flags |= FDF_SEEK;
+ return (1);
+ }
+ return (0);
+}
+
+/*
+ * Do media detection.
+ */
+int
+fdauto(dev)
+ dev_t dev;
+{
+ int i, error = 0;
+ struct buf *bp;
+ struct bus_device *ui = fddinfo[fdunit(dev)];
+ struct fdsoftc *sc = &fdsoftc[ui->unit];
+ struct fddk *dk, *def = 0;
+
+ sc->sc_flags |= FDF_AUTO;
+ bp = (struct buf *)geteblk(SECSIZE);
+ for (i = 0, dk = fddk; i < NDKTYPES; i++, dk++) {
+ if ((dk->dk_drives & (1 << sc->sc_type)) == 0)
+ continue;
+ if (def == 0)
+ def = dk;
+ sc->sc_dk = dk;
+ bp->b_flags = B_READ;
+ bp->b_dev = dev;
+ bp->b_bcount = SECSIZE;
+ if (sc->sc_flags & FDF_SEEK) {
+ sc->sc_flags &= ~FDF_SEEK;
+ bp->b_blkno = 100;
+ } else
+ bp->b_blkno = 0;
+ fdstrategy(bp);
+ biowait(bp);
+ if ((bp->b_flags & B_ERROR) == 0 || bp->b_error == ENXIO)
+ break;
+ }
+ if (i == NDKTYPES) {
+ printf("fd%d: couldn't detect type, using %s\n",
+ ui->unit, def->dk_name);
+ sc->sc_dk = def;
+ } else if ((bp->b_flags & B_ERROR) == 0)
+ printf("fd%d: detected %s\n", ui->unit, sc->sc_dk->dk_name);
+ else
+ error = ENXIO;
+ sc->sc_flags &= ~FDF_AUTO;
+ brelse(bp);
+ return (error);
+}
+
+/*
+ * Turn on drive motor and select drive.
+ */
+int
+fdmotoron(ui)
+ struct bus_device *ui;
+{
+ int bit;
+ struct bus_ctlr *um = ui->mi;
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+
+ bit = 1 << (ui->slave + 4);
+ if ((fdc->sc_dor & bit) == 0) {
+ fdc->sc_dor &= ~3;
+ fdc->sc_dor |= bit | ui->slave;
+ outb(FD_DOR(um->address), fdc->sc_dor);
+ return (1);
+ }
+ if ((fdc->sc_dor & 3) != ui->slave) {
+ fdc->sc_dor &= ~3;
+ fdc->sc_dor |= ui->slave;
+ outb(FD_DOR(um->address), fdc->sc_dor);
+ }
+ return (0);
+}
+
+/*
+ * Turn off drive motor.
+ */
+int
+fdmotoroff(ui)
+ struct bus_device *ui;
+{
+ struct bus_ctlr *um = ui->mi;
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+
+ fdc->sc_dor &= ~(1 << (ui->slave + 4));
+ outb(FD_DOR(um->address), fdc->sc_dor);
+}
+
+/*
+ * This routine is invoked via timeout() by fdstart()
+ * to call fdstate() at splbio.
+ */
+void
+fdspinup(um)
+ struct bus_ctlr *um;
+{
+ int s;
+
+ s = splbio();
+ fdstate(um);
+ splx(s);
+}
+
+/*
+ * Watchdog routine.
+ * Check for hung operations.
+ * Turn off motor of idle drives.
+ */
+void
+fdwatch()
+{
+ int unit, s;
+ struct bus_device *ui;
+
+ timeout(fdwatch, 0, hz);
+ s = splbio();
+ for (unit = 0; unit < NFDC; unit++)
+ if (fdtab[unit].b_active
+ && ++fdcsoftc[unit].sc_wticks == OP_TIMEOUT)
+ fdintr(unit);
+ for (unit = 0; unit < NFD; unit++) {
+ if ((ui = fddinfo[unit]) == 0 || ui->alive == 0)
+ continue;
+ if (fdutab[unit].b_active == 0
+ && (fdcsoftc[ui->mi->unit].sc_dor & (1 << (ui->slave + 4)))
+ && ++fdsoftc[unit].sc_mticks == MOTOR_TIMEOUT)
+ fdmotoroff(ui);
+ }
+ splx(s);
+}
+
+/*
+ * Print an error message.
+ */
+int
+fderror(msg, ui)
+ char *msg;
+ struct bus_device *ui;
+{
+ struct fdcsoftc *fdc = &fdcsoftc[ui->mi->unit];
+
+ printf("fd%d: %s, %sing cn %d tn %d sn %d\n", ui->unit, msg,
+ (fdc->sc_op == CMD_READ ? "read"
+ : (fdc->sc_op == CMD_WRITE ? "writ" : "formatt")),
+ fdc->sc_cn, fdc->sc_tn, fdc->sc_sn + 1);
+}
+
+/*
+ * Return an error message for an I/O error.
+ */
+char *
+fderrmsg(ui)
+ struct bus_device *ui;
+{
+ struct fdcsoftc *fdc = &fdcsoftc[ui->mi->unit];
+
+ if (fdc->sc_st1 & ST1_EC)
+ return ("invalid sector");
+ if (fdc->sc_st1 & ST1_DE)
+ return ("CRC error");
+ if (fdc->sc_st1 & ST1_OR)
+ return ("DMA overrun");
+ if (fdc->sc_st1 & ST1_ND)
+ return ("sector not found");
+ if (fdc->sc_st1 & ST1_NW)
+ return ("write-protected diskette");
+ if (fdc->sc_st1 & ST1_MA)
+ return ("missing address mark");
+ return ("hard error");
+}
+
+/*
+ * Output a command to FDC.
+ */
+int
+fdcmd(um, n)
+ struct bus_ctlr *um;
+ int n;
+{
+ int i, j;
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+
+ for (i = j = 0; i < 200; i++) {
+ if ((inb(FD_STATUS(um->address)) & (ST_RQM|ST_DIO)) != ST_RQM)
+ continue;
+ outb(FD_DATA(um->address), fdc->sc_cmd[j++]);
+ if (--n == 0)
+ return (1);
+ }
+ /*
+ * Controller is not responding, reset it.
+ */
+ DEBUGF(1, printf("fdc%d: fdcmd() failed\n", um->unit));
+ fdreset(um);
+ return (0);
+}
+
+/*
+ * Read results from FDC.
+ */
+int
+fdresults(um, rp)
+ struct bus_ctlr *um;
+ u_char *rp;
+{
+ int i, j, status;
+
+ for (i = j = 0; i < 200; i++) {
+ status = inb(FD_STATUS(um->address));
+ if ((status & ST_RQM) == 0)
+ continue;
+ if ((status & ST_DIO) == 0)
+ return (j);
+ if (j == 7)
+ break;
+ *rp++ = inb(FD_DATA(um->address));
+ j++;
+ }
+ /*
+ * Controller is not responding, reset it.
+ */
+ DEBUGF(1, printf("fdc%d: fdresults() failed\n", um->unit));
+ fdreset(um);
+ return (0);
+}
+
+/*
+ * Reset controller.
+ */
+int
+fdreset(um)
+ struct bus_ctlr *um;
+{
+ struct fdcsoftc *fdc = &fdcsoftc[um->unit];
+
+ outb(FD_DOR(um->address), fdc->sc_dor & ~(DOR_RSTCLR|DOR_IENABLE));
+ fdc->sc_state = RESETDONE;
+ fdc->sc_flags |= FDF_RESET;
+ outb(FD_DOR(um->address), fdc->sc_dor);
+}
+
+#endif /* NFD > 0 */
diff --git a/i386/i386at/nfdreg.h b/i386/i386at/nfdreg.h
new file mode 100644
index 00000000..9bdf44d3
--- /dev/null
+++ b/i386/i386at/nfdreg.h
@@ -0,0 +1,110 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * NEC 765/Intel 8272 floppy disk controller.
+ */
+
+/*
+ * Ports
+ */
+#define FD_DOR(p) (p) /* digital output register */
+#define FD_STATUS(p) ((p) + 2) /* status register */
+#define FD_DATA(p) ((p) + 3) /* data register */
+#define FD_RATE(p) ((p) + 5) /* transfer rate register */
+#define FD_DIR(p) ((p) + 5) /* digital input register */
+
+/*
+ * Digital output register.
+ */
+#define DOR_IENABLE 0x08 /* enable interrupts and DMA */
+#define DOR_RSTCLR 0x04 /* clear reset */
+
+/*
+ * Status register.
+ */
+#define ST_RQM 0x80 /* request for master */
+#define ST_DIO 0x40 /* direction of data transfer
+ 1 = fdc to cpu, 0 = cpu to fdc */
+#define ST_NDM 0x20 /* non DMA mode */
+#define ST_CB 0x10 /* controller busy */
+
+/*
+ * Digital input register.
+ */
+#define DIR_DSKCHG 0x80 /* diskette chnage has occured */
+
+/*
+ * ST0
+ */
+#define ST0_IC 0xc0 /* interrupt code */
+#define ST0_SE 0x20 /* seek end */
+#define ST0_EC 0x10 /* equipment check */
+#define ST0_NR 0x08 /* not ready */
+#define ST0_HD 0x04 /* head address */
+#define ST0_US 0x03 /* unit select */
+
+/*
+ * ST1
+ */
+#define ST1_EC 0x80 /* end of cylinder */
+#define ST1_DE 0x20 /* CRC data error */
+#define ST1_OR 0x10 /* DMA overrun */
+#define ST1_ND 0x04 /* sector not found */
+#define ST1_NW 0x02 /* write-protected diskette */
+#define ST1_MA 0x01 /* missing address mark */
+
+/*
+ * ST2
+ */
+#define ST2_CM 0x40 /* control mark */
+#define ST2_DD 0x20 /* data error */
+#define ST2_WC 0x10 /* wrong cylinder */
+#define ST2_SH 0x08 /* scan equal hit */
+#define ST2_SN 0x04 /* scan not satisfied */
+#define ST2_BC 0x02 /* bad cylinder */
+#define ST2_MD 0x01 /* missing address mark */
+
+/*
+ * ST3
+ */
+#define ST3_FT 0x80 /* fault */
+#define ST3_WP 0x40 /* write protect */
+#define ST3_RY 0x20 /* ready */
+#define ST3_T0 0x10 /* track 0 */
+#define ST3_TS 0x08 /* two side */
+#define ST3_HD 0x04 /* head address */
+#define ST3_US 0x03 /* unit select */
+
+/*
+ * Commands.
+ */
+#define CMD_SPECIFY 0x03
+#define CMD_RECAL 0x07
+#define CMD_SENSEI 0x08
+#define CMD_SEEK 0x0f
+#define CMD_FORMAT 0x4d
+#define CMD_WRITE 0xc5
+#define CMD_READ 0xe6
+
+/*
+ * Information required by FDC when formatting a diskette.
+ */
+struct format_info {
+ unsigned char cyl;
+ unsigned char head;
+ unsigned char sector;
+ unsigned char secsize;
+};
diff --git a/i386/i386at/nhd.c b/i386/i386at/nhd.c
new file mode 100644
index 00000000..72b4cfc3
--- /dev/null
+++ b/i386/i386at/nhd.c
@@ -0,0 +1,1430 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * MODIFIED BY KEVIN T. VAN MAREN, University of Utah, CSL
+ * Copyright (c) 1996, University of Utah, CSL
+ *
+ * Uses a 'unified' partition code with the SCSI driver.
+ * Reading/Writing disklabels through the kernel is NOT recommended.
+ * (The preferred method is through the raw device (wd0), with no
+ * open partitions). setdisklabel() should work for the in-core
+ * fudged disklabel, but will not change the partitioning. The driver
+ * *never* sees the disklabel on the disk.
+ *
+ */
+
+
+#include <hd.h>
+#if NHD > 0 && !defined(LINUX_DEV)
+/*
+ * Hard disk driver.
+ *
+ * Supports:
+ * 1 controller and 2 drives.
+ * Arbitrarily sized read/write requests.
+ * Misaligned requests.
+ * Multiple sector transfer mode (not tested extensively).
+ *
+ * TODO:
+ * 1) Real probe routines for controller and drives.
+ * 2) Support for multiple controllers. The driver does
+ * not assume a single controller since all functions
+ * take the controller and/or device structure as an
+ * argument, however the probe routines limit the
+ * number of controllers and drives to 1 and 2 respectively.
+ *
+ * Shantanu Goel (goel@cs.columbia.edu)
+ */
+#include <sys/types.h>
+#include <sys/ioctl.h>
+#include "vm_param.h"
+#include <kern/time_out.h>
+#include <vm/vm_kern.h>
+#include <vm/pmap.h>
+#include <device/param.h>
+#include <device/buf.h>
+#include <device/errno.h>
+#include <device/device_types.h>
+#include <device/disk_status.h>
+#include <chips/busses.h>
+#include <i386/machspl.h>
+#include <i386/pio.h>
+#include <i386at/cram.h>
+#include <i386at/disk.h>
+#include <i386at/nhdreg.h>
+
+#include <scsi/rz_labels.h>
+
+
+/* this is for the partition code */
+typedef struct ide_driver_info {
+ dev_t dev;
+/* struct buf *bp; */
+ int sectorsize;
+} ide_driver_info;
+
+#define MAX_IDE_PARTS 32 /* max partitions per drive */
+static char *drive_name[4]={"wd0: ","wd1: ","xxx ","yyy "};
+
+/*
+ * XXX: This will have to be fixed for controllers that
+ * can support upto 4 drives.
+ */
+#define NDRIVES_PER_HDC 2
+#define NHDC ((NHD + NDRIVES_PER_HDC - 1) / NDRIVES_PER_HDC)
+
+#define b_cylin b_resid
+
+#define B_ABS B_MD1
+#define B_IDENTIFY (B_MD1 << 1)
+
+/* shift right SLICE_BITS + PARTITION_BITS. Note: 2^10 = 1024 sub-parts */
+#define hdunit(dev) (((dev) >> 10) & 3)
+#define hdpart(dev) ((dev) & 0x3ff)
+
+#define MAX_RETRIES 12 /* maximum number of retries */
+#define OP_TIMEOUT 7 /* time to wait (secs) for an operation */
+
+/*
+ * Autoconfiguration stuff.
+ */
+struct bus_ctlr *hdminfo[NHDC];
+struct bus_device *hddinfo[NHD];
+int hdstd[] = { 0 };
+int hdprobe(), hdslave(), hdstrategy();
+void hdattach();
+struct bus_driver hddriver = {
+ hdprobe, hdslave, hdattach, 0, hdstd, "hd", hddinfo, "hdc", hdminfo, 0
+};
+
+/*
+ * BIOS geometry.
+ */
+struct hdbios {
+ int bg_ncyl; /* cylinders/unit */
+ int bg_ntrk; /* tracks/cylinder */
+ int bg_precomp; /* write precomp cylinder */
+ int bg_nsect; /* sectors/track */
+} hdbios[NHD];
+
+/*
+ * Controller state.
+ */
+struct hdcsoftc {
+ int sc_state; /* transfer fsm */
+ caddr_t sc_addr; /* buffer address */
+ int sc_resid; /* amount left to transfer */
+ int sc_amt; /* amount currently being transferred */
+ int sc_cnt; /* amount transferred per interrupt */
+ int sc_sn; /* sector number */
+ int sc_tn; /* track number */
+ int sc_cn; /* cylinder number */
+ int sc_recalerr; /* # recalibration errors */
+ int sc_ioerr; /* # i/o errors */
+ int sc_wticks; /* watchdog */
+ caddr_t sc_buf; /* buffer for unaligned requests */
+} hdcsoftc[NHDC];
+
+/*
+ * Transfer states.
+ */
+#define IDLE 0 /* controller is idle */
+#define SETPARAM 1 /* set disk parameters */
+#define SETPARAMDONE 2 /* set parameters done */
+#define RESTORE 3 /* recalibrate drive */
+#define RESTOREDONE 4 /* recalibrate done */
+#define TRANSFER 5 /* perform I/O transfer */
+#define TRANSFERDONE 6 /* transfer done */
+#define IDENTIFY 7 /* get drive info */
+#define IDENTIFYDONE 8 /* get drive info done */
+#define SETMULTI 9 /* set multiple mode count */
+#define SETMULTIDONE 10 /* set multiple mode count done */
+
+/*
+ * Drive state.
+ */
+struct hdsoftc {
+ int sc_flags;
+#define HDF_SETPARAM 0x001 /* set drive parameters before I/O operation */
+#define HDF_RESTORE 0x002 /* drive needs recalibration */
+#define HDF_WANT 0x004 /* some one is waiting for drive */
+#define HDF_UNALIGNED 0x008 /* request is not a multiple of sector size */
+#define HDF_SETMULTI 0x010 /* set multiple count before I/O operation */
+#define HDF_MULTIDONE 0x020 /* multicnt field is valid */
+#define HDF_IDENTDONE 0x040 /* identify command done */
+#define HDF_LBA 0x080 /* use LBA mode */
+ int sc_multicnt; /* current multiple count */
+ int sc_abssn; /* absolute sector number (for {RD,WR}ABS) */
+ int sc_abscnt; /* absolute sector count */
+ int sc_openpart; /* bit mask of open partitions */
+ struct hdident sc_id; /* info returned by identify */
+} hdsoftc[NHD];
+
+struct buf hdtab[NHDC]; /* controller queues */
+struct buf hdutab[NHD]; /* drive queues */
+struct disklabel hdlabel[NHD]; /* disklabels -- incorrect info! */
+struct diskpart array[NHD*MAX_IDE_PARTS]; /* partition info */
+
+/*
+ * To enable multiple mode,
+ * set this, recompile, and reboot the machine.
+ */
+int hdenmulti = 0;
+
+char *hderrchk();
+struct buf *geteblk();
+int hdwstart = 0;
+void hdwatch();
+
+/*
+ * Probe for a controller.
+ */
+int
+hdprobe(xxx, um)
+ int xxx;
+ struct bus_ctlr *um;
+{
+ struct hdcsoftc *hdc;
+
+ if (um->unit >= NHDC) {
+ printf("hdc%d: not configured\n", um->unit);
+ return (0);
+ }
+ if (um->unit > 0) { /* XXX: only 1 controller */
+
+ printf("nhd:probe for 2+ controllers -- not implemented\n");
+ return (0);
+ }
+
+ /*
+ * XXX: need real probe
+ */
+ hdc = &hdcsoftc[um->unit];
+ if (!hdc->sc_buf)
+ kmem_alloc(kernel_map,
+ (vm_offset_t *)&hdc->sc_buf, I386_PGBYTES);
+ take_ctlr_irq(um);
+ return (1);
+}
+
+/*
+ * Probe for a drive.
+ */
+int
+hdslave(ui)
+ struct bus_device *ui;
+{
+ int type;
+
+ if (ui->unit >= NHD) {
+ printf("hd%d: not configured\n", ui->unit);
+ return (0);
+ }
+ if (ui->unit > 1) /* XXX: only 2 drives */
+ return (0);
+
+ /*
+ * Find out if drive exists by reading CMOS.
+ */
+ outb(CMOS_ADDR, 0x12);
+ type = inb(CMOS_DATA);
+ if (ui->unit == 0)
+ type >>= 4;
+ type &= 0x0f;
+ return (type);
+}
+
+/*
+ * Attach a drive to the system.
+ */
+void
+hdattach(ui)
+ struct bus_device *ui;
+{
+ char *tbl;
+ unsigned n;
+ /* struct hdsoftc *sc = &hdsoftc[ui->unit]; */
+ struct disklabel *lp = &hdlabel[ui->unit];
+ struct hdbios *bg = &hdbios[ui->unit];
+
+ /*
+ * Set up a temporary disklabel from BIOS parameters.
+ * The actual partition table will be read during open.
+ */
+ n = *(unsigned *)phystokv(ui->address);
+ tbl = (unsigned char *)phystokv((n & 0xffff) + ((n >> 12) & 0xffff0));
+ bg->bg_ncyl = *(unsigned short *)tbl;
+ bg->bg_ntrk = *(unsigned char *)(tbl + 2);
+ bg->bg_precomp = *(unsigned short *)(tbl + 5);
+ bg->bg_nsect = *(unsigned char *)(tbl + 14);
+ fudge_bsd_label(lp, DTYPE_ESDI, bg->bg_ncyl*bg->bg_ntrk*bg->bg_nsect,
+ bg->bg_ntrk, bg->bg_nsect, SECSIZE, 3);
+
+ /* FORCE sector size to 512... */
+
+ printf(": ntrak(heads) %d, ncyl %d, nsec %d, size %u MB",
+ lp->d_ntracks, lp->d_ncylinders, lp->d_nsectors,
+ lp->d_secperunit * lp->d_secsize / (1024*1024));
+}
+
+int
+hdopen(dev, mode)
+ dev_t dev;
+ int mode;
+{
+ int unit = hdunit(dev), part = hdpart(dev) /*, error */;
+ struct bus_device *ui;
+ struct hdsoftc *sc;
+ struct diskpart *label;
+
+ if (unit >= NHD || (ui = hddinfo[unit]) == 0 || ui->alive == 0)
+ return (ENXIO);
+ if (!hdwstart) {
+ hdwstart++;
+ timeout(hdwatch, 0, hz);
+ }
+ sc = &hdsoftc[unit];
+ /* should this be changed so only gets called once, even if all
+ partitions are closed and re-opened? */
+ if (sc->sc_openpart == 0) {
+ hdinit(dev);
+ if (sc->sc_flags & HDF_LBA)
+ printf("hd%d: Using LBA mode\n", ui->unit);
+ }
+
+/* Note: should set a bit in the label structure to ensure that
+ aliasing prevents multiple instances to be opened. */
+#if 0
+ if (part >= MAXPARTITIONS || lp->d_partitions[part].p_size == 0)
+ return (ENXIO);
+#endif 0
+
+ label=lookup_part(&array[MAX_IDE_PARTS*unit], hdpart(dev));
+ if (!label)
+ return (ENXIO);
+
+
+ sc->sc_openpart |= 1 << part;
+ return (0);
+}
+
+int
+hdclose(dev)
+ dev_t dev;
+{
+ int unit = hdunit(dev), s;
+ struct hdsoftc *sc = &hdsoftc[unit];
+
+ sc->sc_openpart &= ~(1 << hdpart(dev));
+ if (sc->sc_openpart == 0) {
+ s = splbio();
+ while (hdutab[unit].b_active) {
+ sc->sc_flags |= HDF_WANT;
+ assert_wait((event_t)sc, FALSE);
+ thread_block((void (*)())0);
+ }
+ splx(s);
+ }
+ return (0);
+}
+
+int
+hdread(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ return (block_io(hdstrategy, minphys, ior));
+}
+
+int
+hdwrite(dev, ior)
+ dev_t dev;
+ io_req_t ior;
+{
+ return (block_io(hdstrategy, minphys, ior));
+}
+
+int
+hdgetstat(dev, flavor, data, count)
+ dev_t dev;
+ dev_flavor_t flavor;
+ dev_status_t data;
+ mach_msg_type_number_t *count;
+{
+ int unit = hdunit(dev), part = hdpart(dev);
+ struct hdsoftc *sc = &hdsoftc[unit];
+ struct disklabel *lp = &hdlabel[unit];
+ struct buf *bp;
+ struct diskpart *label;
+
+ label=lookup_part(&array[MAX_IDE_PARTS*unit], hdpart(dev));
+ switch (flavor) {
+
+ case DEV_GET_SIZE:
+ if (label) {
+ data[DEV_GET_SIZE_DEVICE_SIZE] = (label->size * lp->d_secsize);
+ data[DEV_GET_SIZE_RECORD_SIZE] = lp->d_secsize;
+ *count = DEV_GET_SIZE_COUNT;
+ } else { /* Kevin: added checking here */
+ data[DEV_GET_SIZE_DEVICE_SIZE] = 0;
+ data[DEV_GET_SIZE_RECORD_SIZE] = 0;
+ *count = 0;
+ }
+ break;
+
+ case DIOCGDINFO:
+ case DIOCGDINFO - (0x10 << 16):
+ dkgetlabel(lp, flavor, data, count);
+ break;
+
+ case V_GETPARMS:
+ {
+ struct disk_parms *dp;
+ struct hdbios *bg = &hdbios[unit];
+
+ if (*count < (sizeof(struct disk_parms) / sizeof(int)))
+ return (D_INVALID_OPERATION);
+ dp = (struct disk_parms *)data;
+ dp->dp_type = DPT_WINI;
+ dp->dp_heads = lp->d_ntracks;
+ dp->dp_cyls = lp->d_ncylinders;
+ dp->dp_sectors = lp->d_nsectors;
+ dp->dp_dosheads = bg->bg_ntrk;
+ dp->dp_doscyls = bg->bg_ncyl;
+ dp->dp_dossectors = bg->bg_nsect;
+ dp->dp_secsiz = lp->d_secsize;
+ dp->dp_ptag = 0;
+ dp->dp_pflag = 0;
+ if (label) {
+ dp->dp_pstartsec = label->start;
+ dp->dp_pnumsec = label->size;
+ } else { /* added by Kevin */
+ dp->dp_pstartsec = -1;
+ dp->dp_pnumsec = -1;
+ }
+
+ *count = sizeof(struct disk_parms) / sizeof(int);
+ break;
+ }
+ case V_RDABS:
+ if (*count < lp->d_secsize / sizeof(int)) {
+ printf("hd%d: RDABS, bad size %d\n", unit, *count);
+ return (EINVAL);
+ }
+ bp = geteblk(lp->d_secsize);
+ bp->b_flags = B_READ | B_ABS;
+ bp->b_blkno = sc->sc_abssn;
+ bp->b_dev = dev;
+ bp->b_bcount = lp->d_secsize;
+ hdstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ printf("hd%d: RDABS failed\n", unit);
+ brelse(bp);
+ return (EIO);
+ }
+ bcopy(bp->b_un.b_addr, (caddr_t)data, lp->d_secsize);
+ brelse(bp);
+ *count = lp->d_secsize / sizeof(int);
+ break;
+
+ case V_VERIFY:
+ {
+ int i, amt, n, error = 0;
+
+ bp = geteblk(I386_PGBYTES);
+ bp->b_blkno = sc->sc_abssn;
+ bp->b_dev = dev;
+ amt = sc->sc_abscnt;
+ n = I386_PGBYTES / lp->d_secsize;
+ while (amt > 0) {
+ i = (amt > n) ? n : amt;
+ bp->b_bcount = i * lp->d_secsize;
+ bp->b_flags = B_READ | B_ABS;
+ hdstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ error = BAD_BLK;
+ break;
+ }
+ amt -= bp->b_bcount;
+ bp->b_blkno += i;
+ }
+ brelse(bp);
+ data[0] = error;
+ *count = 1;
+ break;
+ }
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (0);
+}
+
+int
+hdsetstat(dev, flavor, data, count)
+ dev_t dev;
+ dev_flavor_t flavor;
+ dev_status_t data;
+ mach_msg_type_number_t count;
+{
+ int unit = hdunit(dev); /* , part = hdpart(dev); */
+ int error = 0 /*, s */;
+ struct hdsoftc *sc = &hdsoftc[unit];
+ struct disklabel *lp = &hdlabel[unit];
+ struct buf *bp;
+
+ switch (flavor) {
+
+ case DIOCWLABEL:
+ case DIOCWLABEL - (0x10 << 16):
+ break;
+
+ case DIOCSDINFO:
+ case DIOCSDINFO - (0x10 << 16):
+ if (count != (sizeof(struct disklabel) / sizeof(int)))
+ return (D_INVALID_SIZE);
+ error = setdisklabel(lp, (struct disklabel *)data);
+ if (error == 0 && (sc->sc_flags & HDF_LBA) == 0)
+ sc->sc_flags |= HDF_SETPARAM;
+ break;
+
+ case DIOCWDINFO:
+ case DIOCWDINFO - (0x10 << 16):
+ if (count != (sizeof(struct disklabel) / sizeof(int)))
+ return (D_INVALID_SIZE);
+ error = setdisklabel(lp, (struct disklabel *)data);
+ if (error == 0) {
+ if ((sc->sc_flags & HDF_LBA) == 0)
+ sc->sc_flags |= HDF_SETPARAM;
+ error = hdwritelabel(dev);
+ }
+ break;
+
+ case V_REMOUNT:
+ hdinit(dev);
+ break;
+
+ case V_ABS:
+ if (count != 1 && count != 2)
+ return (D_INVALID_OPERATION);
+ sc->sc_abssn = *(int *)data;
+ if (sc->sc_abssn < 0 || sc->sc_abssn >= lp->d_secperunit)
+ return (D_INVALID_OPERATION);
+ if (count == 2)
+ sc->sc_abscnt = *((int *)data + 1);
+ else
+ sc->sc_abscnt = 1;
+ if (sc->sc_abscnt <= 0
+ || sc->sc_abssn + sc->sc_abscnt > lp->d_secperunit)
+ return (D_INVALID_OPERATION);
+ break;
+
+ case V_WRABS:
+ if (count < (lp->d_secsize / sizeof(int))) {
+ printf("hd%d: WRABS, bad size %d\n", unit, count);
+ return (D_INVALID_OPERATION);
+ }
+ bp = geteblk(lp->d_secsize);
+ bcopy((caddr_t)data, bp->b_un.b_addr, lp->d_secsize);
+ bp->b_flags = B_WRITE | B_ABS;
+ bp->b_blkno = sc->sc_abssn;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_dev = dev;
+ hdstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ printf("hd%d: WRABS failed\n", unit);
+ error = EIO;
+ }
+ brelse(bp);
+ break;
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (error);
+}
+
+int
+hddevinfo(dev, flavor, info)
+ dev_t dev;
+ int flavor;
+ char *info;
+{
+ switch (flavor) {
+
+ case D_INFO_BLOCK_SIZE:
+ *((int *)info) = SECSIZE; /* #defined to 512 */
+ break;
+
+ default:
+ return (D_INVALID_OPERATION);
+ }
+ return (0);
+}
+
+
+
+
+/* Kevin T. Van Maren: Added this low-level routine for the unified
+ partition code. A pointer to this routine is passed, along with param* */
+int
+ide_read_fun(struct ide_driver_info *param, int sectornum, char *buff)
+{
+ struct buf *bp;
+
+ bp = geteblk(param->sectorsize);
+ bp->b_flags = B_READ | B_ABS;
+
+ bp->b_bcount = param->sectorsize;
+ bp->b_blkno = sectornum;
+
+ /* WARNING: DEPENDS ON NUMBER OF BITS FOR PARTITIONS */
+ bp->b_dev = param->dev & ~0x3ff;
+ hdstrategy(bp);
+ biowait(bp);
+ if ((bp->b_flags & B_ERROR) == 0)
+ bcopy((char *)bp->b_un.b_addr, buff, param->sectorsize);
+ else {
+ printf("ERROR!\n");
+ return(B_ERROR);
+ }
+
+ brelse(bp);
+ return(0);
+}
+
+
+
+/*
+ * Initialize drive.
+ */
+int
+hdinit(dev)
+ dev_t dev;
+{
+ int unit = hdunit(dev);
+ struct hdsoftc *sc = &hdsoftc[unit];
+ struct disklabel *lp = &hdlabel[unit], *dlp;
+ struct buf *bp = 0;
+ int numpart;
+
+ struct ide_driver_info ide_param = { dev, /* bp, */ lp->d_secsize };
+ int ret;
+
+ /*
+ * Issue identify command.
+ */
+ if ((sc->sc_flags & HDF_IDENTDONE) == 0) {
+ sc->sc_flags |= HDF_IDENTDONE;
+ bp = geteblk(lp->d_secsize);
+ /* sector size #defined to 512 */
+ bp->b_flags = B_IDENTIFY;
+ bp->b_dev = dev;
+ hdstrategy(bp);
+ biowait(bp);
+ if ((bp->b_flags & B_ERROR) == 0) {
+ bcopy((char *)bp->b_un.b_addr,
+ (char *)&sc->sc_id, sizeof(struct hdident));
+
+ /*
+ * Check if drive supports LBA mode.
+ */
+ if (sc->sc_id.id_capability & 2)
+ sc->sc_flags |= HDF_LBA;
+ }
+ }
+
+ /*
+ * Check if drive supports multiple read/write mode.
+ */
+ hdmulti(dev);
+
+ /* Note: label was fudged during attach! */
+
+ /* ensure the 'raw disk' can be accessed reliably */
+ array[MAX_IDE_PARTS*unit].start=0;
+ array[MAX_IDE_PARTS*unit].size=lp->d_secperunit; /* fill in root for MY reads */
+#if 0
+ array[MAX_IDE_PARTS*unit].subs=0;
+ array[MAX_IDE_PARTS*unit].nsubs=0;
+ array[MAX_IDE_PARTS*unit].type=0;
+ array[MAX_IDE_PARTS*unit].fsys=0;
+#endif 0
+
+ numpart=get_only_partition(&ide_param, (*ide_read_fun),
+ &array[MAX_IDE_PARTS*unit],MAX_IDE_PARTS,lp->d_secperunit,
+ drive_name[unit]);
+
+ printf("%s %d partitions found\n",drive_name[unit],numpart);
+
+ if ((sc->sc_flags & HDF_LBA) == 0)
+ sc->sc_flags |= HDF_SETPARAM;
+
+ brelse(bp);
+ return(ret);
+}
+
+
+
+
+/*
+ * Check if drive supports multiple read/write mode.
+ */
+int
+hdmulti(dev)
+ dev_t dev;
+{
+ int unit = hdunit(dev);
+ struct hdsoftc *sc = &hdsoftc[unit];
+ struct buf *bp;
+ struct hdident *id;
+
+ if (sc->sc_flags & HDF_MULTIDONE)
+ return(0);
+
+ sc->sc_flags |= HDF_MULTIDONE;
+
+ if (hdenmulti == 0)
+ return(0);
+
+ /*
+ * Get drive information by issuing IDENTIFY command.
+ */
+ bp = geteblk(DEV_BSIZE);
+ bp->b_flags = B_IDENTIFY;
+ bp->b_dev = dev;
+ hdstrategy(bp);
+ biowait(bp);
+ id = (struct hdident *)bp->b_un.b_addr;
+
+ /*
+ * If controller does not recognise IDENTIFY command,
+ * or does not support multiple mode, clear count.
+ */
+ if ((bp->b_flags & B_ERROR) || !id->id_multisize)
+ sc->sc_multicnt = 0;
+ else {
+ sc->sc_multicnt = id->id_multisize;
+ printf("hd%d: max multiple size %u", unit, sc->sc_multicnt);
+ /*
+ * Use 4096 since it is the minimum block size in FFS.
+ */
+ if (sc->sc_multicnt > 4096 / 512)
+ sc->sc_multicnt = 4096 / 512;
+ printf(", using %u\n", sc->sc_multicnt);
+ sc->sc_flags |= HDF_SETMULTI;
+ }
+ brelse(bp);
+}
+
+/*
+ * Write label to disk.
+ */
+int
+hdwritelabel(dev)
+ dev_t dev;
+{
+ int unit = hdunit(dev), error = 0;
+ long labelsect;
+ struct buf *bp;
+ struct disklabel *lp = &hdlabel[unit];
+
+ printf("hdwritelabel: no longer implemented\n");
+
+#if 0
+ bp = geteblk(lp->d_secsize);
+ bp->b_flags = B_READ | B_ABS;
+ bp->b_blkno = LBLLOC + lp->d_partitions[PART_DISK].p_offset;
+ bp->b_bcount = lp->d_secsize;
+ bp->b_dev = dev;
+ hdstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ printf("hd%d: hdwritelabel(), error reading disklabel\n",unit);
+ error = EIO;
+ goto out;
+ }
+ *(struct disklabel *)bp->b_un.b_addr = *lp; /* copy disk label */
+ bp->b_flags = B_WRITE | B_ABS;
+ hdstrategy(bp);
+ biowait(bp);
+ if (bp->b_flags & B_ERROR) {
+ printf("hd%d: hdwritelabel(), error writing disklabel\n",unit);
+ error = EIO;
+ }
+ out:
+ brelse(bp);
+#endif 0
+
+ return (error);
+}
+
+/*
+ * Strategy routine.
+ * Enqueue request on drive.
+ */
+int
+hdstrategy(bp)
+ struct buf *bp;
+{
+ int unit = hdunit(bp->b_dev), part = hdpart(bp->b_dev), s;
+ long bn, sz, maxsz;
+ struct buf *dp;
+ struct hdsoftc *sc = &hdsoftc[unit];
+ struct bus_device *ui = hddinfo[unit];
+ struct disklabel *lp = &hdlabel[unit];
+ struct diskpart *label;
+
+ if (bp->b_flags & B_IDENTIFY) {
+ bp->b_cylin = 0;
+ goto q;
+ }
+ bn = bp->b_blkno;
+ if (bp->b_flags & B_ABS)
+ goto q1;
+ sz = (bp->b_bcount + lp->d_secsize - 1) / lp->d_secsize;
+ label=lookup_part(&array[MAX_IDE_PARTS*unit], hdpart(bp->b_dev));
+ if (label) {
+ maxsz = label->size;
+ } else {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EINVAL;
+ goto done;
+ }
+
+ if (bn < 0 || bn + sz > maxsz) {
+ if (bn == maxsz) {
+ bp->b_resid = bp->b_bcount;
+ goto done;
+ }
+ sz = maxsz - bn;
+ if (sz <= 0) {
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EINVAL;
+ goto done;
+ }
+ bp->b_bcount = sz * lp->d_secsize;
+ }
+ bn += lp->d_partitions[part].p_offset;
+ bn += label->start;
+
+ q1:
+ bp->b_cylin = (sc->sc_flags & HDF_LBA) ? bn : bn / lp->d_secpercyl;
+ q:
+ dp = &hdutab[unit];
+ s = splbio();
+ disksort(dp, bp);
+ if (!dp->b_active) {
+ hdustart(ui);
+ if (!hdtab[ui->mi->unit].b_active)
+ hdstart(ui->mi);
+ }
+ splx(s);
+ return(0);
+ done:
+ biodone(bp);
+ return(0);
+}
+
+/*
+ * Unit start routine.
+ * Move request from drive to controller queue.
+ */
+int
+hdustart(ui)
+ struct bus_device *ui;
+{
+ struct buf *bp;
+ struct buf *dp;
+
+ bp = &hdutab[ui->unit];
+ if (bp->b_actf == 0)
+ return(0);
+ dp = &hdtab[ui->mi->unit];
+ if (dp->b_actf == 0)
+ dp->b_actf = bp;
+ else
+ dp->b_actl->b_forw = bp;
+ bp->b_forw = 0;
+ dp->b_actl = bp;
+ bp->b_active++;
+}
+
+/*
+ * Start output on controller.
+ */
+int
+hdstart(um)
+ struct bus_ctlr *um;
+{
+ long bn;
+ struct buf *bp;
+ struct buf *dp;
+ struct hdsoftc *sc;
+ struct hdcsoftc *hdc;
+ struct bus_device *ui;
+ struct disklabel *lp;
+ struct diskpart *label;
+
+ /*
+ * Pull a request from the controller queue.
+ */
+ dp = &hdtab[um->unit];
+ if ((bp = dp->b_actf) == 0)
+ return(0);
+ bp = bp->b_actf;
+
+ hdc = &hdcsoftc[um->unit];
+ ui = hddinfo[hdunit(bp->b_dev)];
+ sc = &hdsoftc[ui->unit];
+ lp = &hdlabel[ui->unit];
+
+ label = lookup_part(&array[MAX_IDE_PARTS*hdunit(bp->b_dev)], hdpart(bp->b_dev));
+
+ /*
+ * Mark controller busy.
+ */
+ dp->b_active++;
+
+ if (bp->b_flags & B_IDENTIFY) {
+ hdc->sc_state = IDENTIFY;
+ goto doit;
+ }
+
+ /*
+ * Figure out where this request is going.
+ */
+ if (sc->sc_flags & HDF_LBA)
+ hdc->sc_cn = bp->b_cylin;
+ else {
+ bn = bp->b_blkno;
+ if ((bp->b_flags & B_ABS) == 0) {
+ bn += label->start; /* partition must be valid */
+ }
+ hdc->sc_cn = bp->b_cylin;
+ hdc->sc_sn = bn % lp->d_secpercyl;
+ hdc->sc_tn = hdc->sc_sn / lp->d_nsectors;
+ hdc->sc_sn %= lp->d_nsectors;
+ }
+
+ /*
+ * Set up for multi-sector transfer.
+ */
+ hdc->sc_addr = bp->b_un.b_addr;
+ hdc->sc_resid = bp->b_bcount;
+ hdc->sc_wticks = 0;
+ hdc->sc_recalerr = 0;
+ hdc->sc_ioerr = 0;
+
+ /*
+ * Set initial transfer state.
+ */
+ if (sc->sc_flags & HDF_SETPARAM)
+ hdc->sc_state = SETPARAM;
+ else if (sc->sc_flags & HDF_RESTORE)
+ hdc->sc_state = RESTORE;
+ else if (sc->sc_flags & HDF_SETMULTI)
+ hdc->sc_state = SETMULTI;
+ else
+ hdc->sc_state = TRANSFER;
+
+ doit:
+ /*
+ * Call transfer state routine to do the actual I/O.
+ */
+ hdstate(um);
+}
+
+/*
+ * Interrupt routine.
+ */
+int
+hdintr(ctlr)
+ int ctlr;
+{
+ int timedout;
+ struct bus_ctlr *um = hdminfo[ctlr];
+ struct bus_device *ui;
+ struct buf *bp;
+ struct buf *dp = &hdtab[ctlr];
+ struct hdcsoftc *hdc = &hdcsoftc[ctlr];
+
+ if (!dp->b_active) {
+ (void) inb(HD_STATUS(um->address));
+ printf("hdc%d: stray interrupt\n", ctlr);
+ return(0);
+ }
+ timedout = hdc->sc_wticks >= OP_TIMEOUT;
+ hdc->sc_wticks = 0;
+
+ /*
+ * Operation timed out, terminate request.
+ */
+ if (timedout) {
+ bp = dp->b_actf->b_actf;
+ ui = hddinfo[hdunit(bp->b_dev)];
+ hderror("timed out", ui);
+ hdsoftc[ui->unit].sc_flags |= HDF_RESTORE;
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ hddone(ui, bp);
+ return(0);
+ }
+
+ /*
+ * Let transfer state routine handle the rest.
+ */
+ hdstate(um);
+}
+
+/*
+ * Transfer finite state machine driver.
+ */
+int
+hdstate(um)
+ struct bus_ctlr *um;
+{
+ char *msg;
+ int op;
+ struct buf *bp;
+ struct hdsoftc *sc;
+ struct bus_device *ui;
+ struct disklabel *lp;
+ struct hdcsoftc *hdc = &hdcsoftc[um->unit];
+ struct hdbios *bg;
+
+ bp = hdtab[um->unit].b_actf->b_actf;
+ ui = hddinfo[hdunit(bp->b_dev)];
+ lp = &hdlabel[ui->unit];
+ sc = &hdsoftc[ui->unit];
+ bg = &hdbios[ui->unit];
+
+ /*
+ * Ensure controller is not busy.
+ */
+ if (!hdwait(um))
+ goto ctlr_err;
+
+ while (1) switch (hdc->sc_state) {
+
+ case SETPARAM:
+ /*
+ * Set drive parameters.
+ */
+ outb(HD_DRVHD(um->address),
+ 0xa0 | (ui->slave << 4) | (lp->d_ntracks - 1));
+ outb(HD_SECTCNT(um->address), lp->d_nsectors);
+ outb(HD_CMD(um->address), CMD_SETPARAM);
+ hdc->sc_state = SETPARAMDONE;
+ return(0);
+
+ case SETPARAMDONE:
+ /*
+ * Set parameters complete.
+ */
+ if (msg = hderrchk(um))
+ goto bad;
+ sc->sc_flags &= ~HDF_SETPARAM;
+ hdc->sc_state = RESTORE;
+ break;
+
+ case RESTORE:
+ /*
+ * Recalibrate drive.
+ */
+ outb(HD_DRVHD(um->address), 0xa0 | (ui->slave << 4));
+ outb(HD_CMD(um->address), CMD_RESTORE);
+ hdc->sc_state = RESTOREDONE;
+ return(0);
+
+ case RESTOREDONE:
+ /*
+ * Recalibration complete.
+ */
+ if (msg = hderrchk(um)) {
+ if (++hdc->sc_recalerr == 2)
+ goto bad;
+ hdc->sc_state = RESTORE;
+ break;
+ }
+ sc->sc_flags &= ~HDF_RESTORE;
+ hdc->sc_recalerr = 0;
+ if (sc->sc_flags & HDF_SETMULTI)
+ hdc->sc_state = SETMULTI;
+ else
+ hdc->sc_state = TRANSFER;
+ break;
+
+ case TRANSFER:
+ /*
+ * Perform I/O transfer.
+ */
+ sc->sc_flags &= ~HDF_UNALIGNED;
+ hdc->sc_state = TRANSFERDONE;
+ hdc->sc_amt = hdc->sc_resid / lp->d_secsize;
+ if (hdc->sc_amt == 0) {
+ sc->sc_flags |= HDF_UNALIGNED;
+ hdc->sc_amt = 1;
+ } else if (hdc->sc_amt > 256)
+ hdc->sc_amt = 256;
+ if (sc->sc_multicnt > 1 && hdc->sc_amt >= sc->sc_multicnt) {
+ hdc->sc_cnt = sc->sc_multicnt;
+ hdc->sc_amt -= hdc->sc_amt % hdc->sc_cnt;
+ if (bp->b_flags & B_READ)
+ op = CMD_READMULTI;
+ else
+ op = CMD_WRITEMULTI;
+ } else {
+ hdc->sc_cnt = 1;
+ if (bp->b_flags & B_READ)
+ op = CMD_READ;
+ else
+ op = CMD_WRITE;
+ }
+ if (sc->sc_flags & HDF_LBA) {
+ outb(HD_DRVHD(um->address),
+ (0xe0 | (ui->slave << 4)
+ | ((hdc->sc_cn >> 24) & 0x0f)));
+ outb(HD_SECT(um->address), hdc->sc_cn);
+ outb(HD_CYLLO(um->address), hdc->sc_cn >> 8);
+ outb(HD_CYLHI(um->address), hdc->sc_cn >> 16);
+ } else {
+ outb(HD_DRVHD(um->address),
+ 0xa0 | (ui->slave << 4) | hdc->sc_tn);
+ outb(HD_SECT(um->address), hdc->sc_sn + 1);
+ outb(HD_CYLLO(um->address), hdc->sc_cn);
+ outb(HD_CYLHI(um->address), hdc->sc_cn >> 8);
+ }
+ outb(HD_SECTCNT(um->address), hdc->sc_amt & 0xff);
+ outb(HD_PRECOMP(um->address), bg->bg_precomp / 4);
+ outb(HD_CMD(um->address), op);
+ if ((bp->b_flags & B_READ) == 0) {
+ int i;
+ caddr_t buf;
+
+ if (sc->sc_flags & HDF_UNALIGNED) {
+ buf = hdc->sc_buf;
+ bcopy(hdc->sc_addr, buf, hdc->sc_resid);
+ bzero(buf + hdc->sc_resid,
+ lp->d_secsize - hdc->sc_resid);
+ } else
+ buf = hdc->sc_addr;
+ for (i = 0; i < 1000000; i++)
+ if (inb(HD_STATUS(um->address)) & ST_DREQ) {
+ loutw(HD_DATA(um->address), buf,
+ hdc->sc_cnt * lp->d_secsize / 2);
+ return(0);
+ }
+ goto ctlr_err;
+ }
+ return(0);
+
+ case TRANSFERDONE:
+ /*
+ * Transfer complete.
+ */
+ if (msg = hderrchk(um)) {
+ if (++hdc->sc_ioerr == MAX_RETRIES)
+ goto bad;
+ /*
+ * Every fourth attempt print a message
+ * and recalibrate the drive.
+ */
+ if (hdc->sc_ioerr & 3)
+ hdc->sc_state = TRANSFER;
+ else {
+ hderror(msg, ui);
+ hdc->sc_state = RESTORE;
+ }
+ break;
+ }
+ if (bp->b_flags & B_READ) {
+ if (sc->sc_flags & HDF_UNALIGNED) {
+ linw(HD_DATA(um->address), hdc->sc_buf,
+ lp->d_secsize / 2);
+ bcopy(hdc->sc_buf, hdc->sc_addr,
+ hdc->sc_resid);
+ } else
+ linw(HD_DATA(um->address), hdc->sc_addr,
+ hdc->sc_cnt * lp->d_secsize / 2);
+ }
+ hdc->sc_resid -= hdc->sc_cnt * lp->d_secsize;
+ if (hdc->sc_resid <= 0) {
+ bp->b_resid = 0;
+ hddone(ui, bp);
+ return(0);
+ }
+ if (sc->sc_flags & HDF_LBA)
+ hdc->sc_cn += hdc->sc_cnt;
+ else {
+ hdc->sc_sn += hdc->sc_cnt;
+ while (hdc->sc_sn >= lp->d_nsectors) {
+ hdc->sc_sn -= lp->d_nsectors;
+ if (++hdc->sc_tn == lp->d_ntracks) {
+ hdc->sc_tn = 0;
+ hdc->sc_cn++;
+ }
+ }
+ }
+ hdc->sc_ioerr = 0;
+ hdc->sc_addr += hdc->sc_cnt * lp->d_secsize;
+ hdc->sc_amt -= hdc->sc_cnt;
+ if (hdc->sc_amt == 0) {
+ hdc->sc_state = TRANSFER;
+ break;
+ }
+ if ((bp->b_flags & B_READ) == 0) {
+ int i;
+
+ for (i = 0; i < 1000000; i++)
+ if (inb(HD_STATUS(um->address)) & ST_DREQ) {
+ loutw(HD_DATA(um->address),
+ hdc->sc_addr,
+ hdc->sc_cnt * lp->d_secsize / 2);
+ return(0);
+ }
+ goto ctlr_err;
+ }
+ return(0);
+
+ case IDENTIFY:
+ /*
+ * Get drive info.
+ */
+ hdc->sc_state = IDENTIFYDONE;
+ outb(HD_DRVHD(um->address), 0xa0 | (ui->slave << 4));
+ outb(HD_CMD(um->address), CMD_IDENTIFY);
+ return(0);
+
+ case IDENTIFYDONE:
+ /*
+ * Get drive info complete.
+ */
+ if (msg = hderrchk(um))
+ goto bad;
+ linw(HD_DATA(um->address), (u_short *)bp->b_un.b_addr, 256);
+ hddone(ui, bp);
+ return(0);
+
+ case SETMULTI:
+ /*
+ * Set multiple mode count.
+ */
+ hdc->sc_state = SETMULTIDONE;
+ outb(HD_DRVHD(um->address), 0xa0 | (ui->slave << 4));
+ outb(HD_SECTCNT(um->address), sc->sc_multicnt);
+ outb(HD_CMD(um->address), CMD_SETMULTI);
+ return(0);
+
+ case SETMULTIDONE:
+ /*
+ * Set multiple mode count complete.
+ */
+ sc->sc_flags &= ~HDF_SETMULTI;
+ if (msg = hderrchk(um)) {
+ sc->sc_multicnt = 0;
+ goto bad;
+ }
+ hdc->sc_state = TRANSFER;
+ break;
+
+ default:
+ printf("hd%d: invalid state\n", ui->unit);
+ panic("hdstate");
+ /*NOTREACHED*/
+ }
+
+ ctlr_err:
+ msg = "controller error";
+
+ bad:
+ hderror(msg, ui);
+ bp->b_flags |= B_ERROR;
+ bp->b_error = EIO;
+ sc->sc_flags |= HDF_RESTORE;
+ hddone(ui, bp);
+}
+
+/*
+ * Terminate current request and start
+ * any others that are queued.
+ */
+int
+hddone(ui, bp)
+ struct bus_device *ui;
+ struct buf *bp;
+{
+ struct bus_ctlr *um = ui->mi;
+ struct hdsoftc *sc = &hdsoftc[ui->unit];
+ struct hdcsoftc *hdc = &hdcsoftc[um->unit];
+ struct buf *dp = &hdtab[um->unit];
+
+ sc->sc_flags &= ~HDF_UNALIGNED;
+
+ /*
+ * Remove this request from queue.
+ */
+ hdutab[ui->unit].b_actf = bp->b_actf;
+ biodone(bp);
+ bp = &hdutab[ui->unit];
+ dp->b_actf = bp->b_forw;
+
+ /*
+ * Mark controller and drive idle.
+ */
+ dp->b_active = 0;
+ bp->b_active = 0;
+ hdc->sc_state = IDLE;
+
+ /*
+ * Start up other requests.
+ */
+ hdustart(ui);
+ hdstart(um);
+
+ /*
+ * Wakeup anyone waiting for drive.
+ */
+ if (sc->sc_flags & HDF_WANT) {
+ sc->sc_flags &= ~HDF_WANT;
+ wakeup((caddr_t)sc);
+ }
+}
+
+/*
+ * Wait for controller to be idle.
+ */
+int
+hdwait(um)
+ struct bus_ctlr *um;
+{
+ int i, status;
+
+ for (i = 0; i < 1000000; i++) {
+ status = inb(HD_STATUS(um->address));
+ if ((status & ST_BUSY) == 0 && (status & ST_READY))
+ return (status);
+ }
+ return (0);
+}
+
+/*
+ * Check for errors on completion of an operation.
+ */
+char *
+hderrchk(um)
+ struct bus_ctlr *um;
+{
+ int status;
+
+ status = inb(HD_STATUS(um->address));
+ if (status & ST_WRTFLT)
+ return ("write fault");
+ if (status & ST_ERROR) {
+ status = inb(HD_ERROR(um->address));
+ if (status & ERR_DAM)
+ return ("data address mark not found");
+ if (status & ERR_TR0)
+ return ("track 0 not found");
+ if (status & ERR_ID)
+ return ("sector not found");
+ if (status & ERR_ECC)
+ return ("uncorrectable ECC error");
+ if (status & ERR_BADBLK)
+ return ("bad block detected");
+ if (status & ERR_ABORT)
+ return ("command aborted");
+ return ("hard error");
+ }
+ return (NULL);
+}
+
+/*
+ * Print an error message.
+ */
+hderror(msg, ui)
+ char *msg;
+ struct bus_device *ui;
+{
+ char *op;
+ int prn_sn = 0;
+ struct hdcsoftc *hdc = &hdcsoftc[ui->mi->unit];
+
+ switch (hdc->sc_state) {
+
+ case SETPARAM:
+ case SETPARAMDONE:
+ op = "SETPARAM: ";
+ break;
+
+ case RESTORE:
+ case RESTOREDONE:
+ op = "RESTORE: ";
+ break;
+
+ case TRANSFER:
+ case TRANSFERDONE:
+ if (hdutab[ui->unit].b_actf->b_flags & B_READ)
+ op = "READ: ";
+ else
+ op = "WRITE: ";
+ prn_sn = 1;
+ break;
+
+ case IDENTIFY:
+ case IDENTIFYDONE:
+ op = "IDENTIFY: ";
+ break;
+
+ case SETMULTI:
+ case SETMULTIDONE:
+ op = "SETMULTI: ";
+ break;
+
+ default:
+ op = "";
+ break;
+ }
+ printf("hd%d: %s%s", ui->unit, op, msg);
+ if (prn_sn) {
+ if (hdsoftc[ui->unit].sc_flags & HDF_LBA)
+ printf(", bn %d", hdc->sc_cn);
+ else
+ printf(", cn %d tn %d sn %d",
+ hdc->sc_cn, hdc->sc_tn, hdc->sc_sn + 1);
+ }
+ printf("\n");
+}
+
+/*
+ * Watchdog routine.
+ * Check for any hung operations.
+ */
+void
+hdwatch()
+{
+ int unit, s;
+
+ timeout(hdwatch, 0, hz);
+ s = splbio();
+ for (unit = 0; unit < NHDC; unit++)
+ if (hdtab[unit].b_active
+ && ++hdcsoftc[unit].sc_wticks >= OP_TIMEOUT)
+ hdintr(unit);
+ splx(s);
+}
+
+#endif /* NHD > 0 && !LINUX_DEV */
diff --git a/i386/i386at/nhdreg.h b/i386/i386at/nhdreg.h
new file mode 100644
index 00000000..d0ef1975
--- /dev/null
+++ b/i386/i386at/nhdreg.h
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 1994 Shantanu Goel
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * THE AUTHOR ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. THE AUTHOR DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ */
+
+/*
+ * Hard disk controller.
+ */
+
+#define HD_DATA(p) (p) /* data register */
+#define HD_ERROR(p) ((p) + 1) /* error register */
+#define HD_PRECOMP(p) ((p) + 1) /* precomp register */
+#define HD_SECTCNT(p) ((p) + 2) /* sector count register */
+#define HD_SECT(p) ((p) + 3) /* sector number register */
+#define HD_CYLLO(p) ((p) + 4) /* cylinder number low */
+#define HD_CYLHI(p) ((p) + 5) /* cylinder number high */
+#define HD_DRVHD(p) ((p) + 6) /* drive head register */
+#define HD_STATUS(p) ((p) + 7) /* status register */
+#define HD_CMD(p) ((p) + 7) /* command register */
+
+/*
+ * Status register
+ */
+#define ST_BUSY 0x80 /* controller is busy */
+#define ST_READY 0x40 /* drive is ready */
+#define ST_WRTFLT 0x20 /* write fault */
+#define ST_SEEK 0x10 /* seek complete */
+#define ST_DREQ 0x08 /* data request */
+#define ST_ECC 0x04 /* ECC corrected data */
+#define ST_INDEX 0x02 /* index pulse */
+#define ST_ERROR 0x01 /* an operation resulted in error */
+
+/*
+ * Error register
+ */
+#define ERR_DAM 0x01 /* data address mark not found */
+#define ERR_TR0 0x02 /* track 0 not found */
+#define ERR_ABORT 0x04 /* command aborted */
+#define ERR_ID 0x10 /* sector not found */
+#define ERR_ECC 0x40 /* uncorrectable ECC error */
+#define ERR_BADBLK 0x80 /* bad block detected */
+
+/*
+ * Commands
+ */
+#define CMD_RESTORE 0x10
+#define CMD_READ 0x20
+#define CMD_WRITE 0x30
+#define CMD_SETPARAM 0x91
+#define CMD_READMULTI 0xc4
+#define CMD_WRITEMULTI 0xc5
+#define CMD_SETMULTI 0xc6
+#define CMD_IDENTIFY 0xec
+
+#if 0
+#define PDLOCATION 29 /* XXX: belongs in <i386at/disk.h> */
+#endif
+
+#define BAD_BLK 0x80
+#define SECSIZE 512
+
+/*
+ * Information returned by IDENTIFY command.
+ */
+struct hdident {
+ u_short id_config; /* flags */
+ u_short id_npcyl; /* # physical cylinders */
+ u_short id_rsvd2; /* reserved (word 2) */
+ u_short id_nptrk; /* # physical tracks */
+ u_short id_bptrk; /* unformatted bytes/track */
+ u_short id_bpsect; /* unformatted bytes/sector */
+ u_short id_npsect; /* # physical sectors/track */
+ u_short id_vendor0; /* vendor unique */
+ u_short id_vendor1; /* vendor unique */
+ u_short id_vendor2; /* vendor unique */
+ u_char id_serno[20]; /* serial #: 0 = unspecified */
+ u_short id_buftype; /* ??? */
+ u_short id_bufsize; /* 512 byte increments: 0 = unspecified */
+ u_short id_eccbytes; /* for R/W LONG commands: 0 = unspecified */
+ u_char id_rev[8]; /* firmware revision: 0 = unspecified */
+ u_char id_model[40]; /* model name: 0 = unspecified */
+ u_char id_multisize; /* max multiple I/O size: 0 = unsupported */
+ u_char id_vendor3; /* vendor unique */
+ u_short id_dwordio; /* 0 = unsupported; 1 = implemented */
+ u_char id_vendor4; /* vendor unique */
+ u_char id_capability; /* 0:DMA 1:LBA 2:IORDYsw 3:IORDY:sup */
+ u_short id_rsvd50; /* reserved (word 50) */
+ u_char id_vendor5; /* vendor unique */
+ u_char id_pio; /* 0=slow, 1=medium, 2=fast */
+ u_char id_vendor6; /* vendor unique */
+ u_char id_dma; /* 0=slow, 1=medium, 2=fast */
+ u_short id_valid; /* 0:logical 1:eide */
+ u_short id_nlcyl; /* # logical cylinders */
+ u_short id_nltrk; /* # logical tracks */
+ u_short id_nlsect; /* # logical sectors/track */
+ u_short id_capacity0; /* logical total sectors on drive */
+ u_short id_capacity1; /* (2 words, misaligned int) */
+ u_char id_multisect; /* current multiple sector count */
+ u_char id_multivalid; /* bit 0=1, multisect field is valid */
+ u_short id_totsect; /* total number of sectors */
+ u_short id_dma1; /* single word DMA info */
+ u_short id_dmamulti; /* multiple word DMA info */
+ u_short id_eidepiomode; /* 0:mode3 1:mode4 */
+ u_short id_eidedmamin; /* min multiple word DMA cycle time (ns) */
+ u_short id_eidedmatime; /* recomended DMA cycle time (ns) */
+ u_short id_eidepio; /* min cycle time (ns), no IORDY */
+ u_short id_eidepioiordy;/* min cycle time (ns, with IORDY */
+ u_short id_rsvd69; /* reserved (word 69) */
+ u_short id_rsvd70; /* reserved (word 70) */
+};
diff --git a/i386/i386at/phys_mem_grab_page.c b/i386/i386at/phys_mem_grab_page.c
new file mode 100644
index 00000000..8ceaca60
--- /dev/null
+++ b/i386/i386at/phys_mem_grab_page.c
@@ -0,0 +1 @@
+/*XXX bogus kludge */
diff --git a/i386/i386at/pic_isa.c b/i386/i386at/pic_isa.c
new file mode 100644
index 00000000..49eff4d3
--- /dev/null
+++ b/i386/i386at/pic_isa.c
@@ -0,0 +1,68 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+#include <sys/types.h>
+#include <i386/ipl.h>
+#include <i386/pic.h>
+#include <rc.h>
+
+
+/* These interrupts are always present */
+extern intnull(), fpintr(), hardclock(), kdintr();
+extern prtnull();
+
+int (*ivect[NINTR])() = {
+ /* 00 */ hardclock, /* always */
+#if RCLINE < 0
+ /* 01 */ kdintr, /* kdintr, ... */
+#else
+ /* 01 */ intnull, /* kdintr, ... */
+#endif
+ /* 02 */ intnull,
+ /* 03 */ intnull, /* lnpoll, comintr, ... */
+
+ /* 04 */ intnull, /* comintr, ... */
+ /* 05 */ intnull, /* comintr, wtintr, ... */
+ /* 06 */ intnull, /* fdintr, ... */
+ /* 07 */ prtnull, /* qdintr, ... */
+
+ /* 08 */ intnull,
+ /* 09 */ intnull, /* ether */
+ /* 10 */ intnull,
+ /* 11 */ intnull,
+
+ /* 12 */ intnull,
+ /* 13 */ fpintr, /* always */
+ /* 14 */ intnull, /* hdintr, ... */
+ /* 15 */ intnull, /* ??? */
+};
+
+int intpri[NINTR] = {
+ /* 00 */ 0, SPL6, 0, 0,
+ /* 04 */ 0, 0, 0, 0,
+ /* 08 */ 0, 0, 0, 0,
+ /* 12 */ 0, SPL1, 0, 0,
+};
diff --git a/i386/i386at/rtc.c b/i386/i386at/rtc.c
new file mode 100644
index 00000000..7a8d1d7b
--- /dev/null
+++ b/i386/i386at/rtc.c
@@ -0,0 +1,237 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <kern/time_out.h>
+#include <i386/machspl.h>
+#include <i386at/rtc.h>
+
+static unsigned char rtc[RTC_NREG];
+static int first_rtcopen_ever = 1;
+
+rtcinit()
+{
+ outb(RTC_ADDR, RTC_A);
+ outb(RTC_DATA, RTC_DIV2 | RTC_RATE6);
+ outb(RTC_ADDR, RTC_B);
+ outb(RTC_DATA, RTC_HM);
+}
+
+
+int
+rtcget(regs)
+unsigned char *regs;
+{
+ if (first_rtcopen_ever) {
+ rtcinit();
+ first_rtcopen_ever = 0;
+ }
+ outb(RTC_ADDR, RTC_D);
+ if (inb(RTC_DATA) & RTC_VRT == 0) return(-1);
+ outb(RTC_ADDR, RTC_A);
+ while (inb(RTC_DATA) & RTC_UIP) /* busy wait */
+ outb(RTC_ADDR, RTC_A);
+ load_rtc(regs);
+ return(0);
+}
+
+rtcput(regs)
+unsigned char *regs;
+{
+ register unsigned char x;
+
+ if (first_rtcopen_ever) {
+ rtcinit();
+ first_rtcopen_ever = 0;
+ }
+ outb(RTC_ADDR, RTC_B);
+ x = inb(RTC_DATA);
+ outb(RTC_ADDR, RTC_B);
+ outb(RTC_DATA, x | RTC_SET);
+ save_rtc(regs);
+ outb(RTC_ADDR, RTC_B);
+ outb(RTC_DATA, x & ~RTC_SET);
+}
+
+
+extern struct timeval time;
+extern struct timezone tz;
+
+static int month[12] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};
+
+yeartoday(year)
+int year;
+{
+ return((year%4) ? 365 : 366);
+}
+
+hexdectodec(n)
+char n;
+{
+ return(((n>>4)&0x0F)*10 + (n&0x0F));
+}
+
+char
+dectohexdec(n)
+int n;
+{
+ return((char)(((n/10)<<4)&0xF0) | ((n%10)&0x0F));
+}
+
+
+readtodc(tp)
+ u_int *tp;
+{
+ struct rtc_st rtclk;
+ time_t n;
+ int sec, min, hr, dom, mon, yr;
+ int i, days = 0;
+ spl_t ospl;
+
+#ifdef MACH_KERNEL
+ ospl = splclock();
+#else MACH_KERNEL
+ ospl = spl5();
+#endif MACH_KERNEL
+ if (rtcget(&rtclk)) {
+ splx(ospl);
+ return(-1);
+ }
+ splx (ospl);
+
+ sec = hexdectodec(rtclk.rtc_sec);
+ min = hexdectodec(rtclk.rtc_min);
+ hr = hexdectodec(rtclk.rtc_hr);
+ dom = hexdectodec(rtclk.rtc_dom);
+ mon = hexdectodec(rtclk.rtc_mon);
+ yr = hexdectodec(rtclk.rtc_yr);
+ yr = (yr < 70) ? yr+100 : yr;
+
+ n = sec + 60 * min + 3600 * hr;
+ n += (dom - 1) * 3600 * 24;
+
+ if (yeartoday(yr) == 366)
+ month[1] = 29;
+ for (i = mon - 2; i >= 0; i--)
+ days += month[i];
+ month[1] = 28;
+ for (i = 70; i < yr; i++)
+ days += yeartoday(i);
+ n += days * 3600 * 24;
+
+#ifdef MACH_KERNEL
+#else MACH_KERNEL
+ n += tz.tz_minuteswest * 60;
+ if (tz.tz_dsttime)
+ n -= 3600;
+#endif MACH_KERNEL
+
+ *tp = n;
+
+ return(0);
+}
+
+writetodc()
+{
+ struct rtc_st rtclk;
+ time_t n;
+ int diff, i, j;
+ spl_t ospl;
+
+#ifdef MACH_KERNEL
+ ospl = splclock();
+#else MACH_KERNEL
+ ospl = spl5();
+#endif MACH_KERNEL
+ if (rtcget(&rtclk)) {
+ splx(ospl);
+ return(-1);
+ }
+ splx(ospl);
+
+#ifdef MACH_KERNEL
+ diff = 0;
+#else MACH_KERNEL
+ diff = tz.tz_minuteswest * 60;
+ if (tz.tz_dsttime)
+ diff -= 3600;
+#endif MACH_KERNEL
+ n = (time.tv_sec - diff) % (3600 * 24); /* hrs+mins+secs */
+ rtclk.rtc_sec = dectohexdec(n%60);
+ n /= 60;
+ rtclk.rtc_min = dectohexdec(n%60);
+ rtclk.rtc_hr = dectohexdec(n/60);
+
+ n = (time.tv_sec - diff) / (3600 * 24); /* days */
+ rtclk.rtc_dow = (n + 4) % 7; /* 1/1/70 is Thursday */
+
+ for (j = 1970, i = yeartoday(j); n >= i; j++, i = yeartoday(j))
+ n -= i;
+
+ rtclk.rtc_yr = dectohexdec(j - 1900);
+
+ if (i == 366)
+ month[1] = 29;
+ for (i = 0; n >= month[i]; i++)
+ n -= month[i];
+ month[1] = 28;
+ rtclk.rtc_mon = dectohexdec(++i);
+
+ rtclk.rtc_dom = dectohexdec(++n);
+
+#ifdef MACH_KERNEL
+ ospl = splclock();
+#else MACH_KERNEL
+ ospl = spl5();
+#endif MACH_KERNEL
+ rtcput(&rtclk);
+ splx(ospl);
+
+ return(0);
+}
diff --git a/i386/i386at/rtc.h b/i386/i386at/rtc.h
new file mode 100644
index 00000000..e8d19670
--- /dev/null
+++ b/i386/i386at/rtc.h
@@ -0,0 +1,137 @@
+/*
+ * Mach Operating System
+ * Copyright (c) 1991,1990,1989 Carnegie Mellon University
+ * All Rights Reserved.
+ *
+ * Permission to use, copy, modify and distribute this software and its
+ * documentation is hereby granted, provided that both the copyright
+ * notice and this permission notice appear in all copies of the
+ * software, derivative works or modified versions, and any portions
+ * thereof, and that both notices appear in supporting documentation.
+ *
+ * CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
+ * CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
+ * ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
+ *
+ * Carnegie Mellon requests users of this software to return to
+ *
+ * Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
+ * School of Computer Science
+ * Carnegie Mellon University
+ * Pittsburgh PA 15213-3890
+ *
+ * any improvements or extensions that they make and grant Carnegie Mellon
+ * the rights to redistribute these changes.
+ */
+/*
+ Copyright 1988, 1989 by Intel Corporation, Santa Clara, California.
+
+ All Rights Reserved
+
+Permission to use, copy, modify, and distribute this software and
+its documentation for any purpose and without fee is hereby
+granted, provided that the above copyright notice appears in all
+copies and that both the copyright notice and this permission notice
+appear in supporting documentation, and that the name of Intel
+not be used in advertising or publicity pertaining to distribution
+of the software without specific, written prior permission.
+
+INTEL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE
+INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS,
+IN NO EVENT SHALL INTEL BE LIABLE FOR ANY SPECIAL, INDIRECT, OR
+CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+LOSS OF USE, DATA OR PROFITS, WHETHER IN ACTION OF CONTRACT,
+NEGLIGENCE, OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+*/
+
+#define RTC_ADDR 0x70 /* I/O port address for register select */
+#define RTC_DATA 0x71 /* I/O port address for data read/write */
+
+/*
+ * Register A definitions
+ */
+#define RTC_A 0x0a /* register A address */
+#define RTC_UIP 0x80 /* Update in progress bit */
+#define RTC_DIV0 0x00 /* Time base of 4.194304 MHz */
+#define RTC_DIV1 0x10 /* Time base of 1.048576 MHz */
+#define RTC_DIV2 0x20 /* Time base of 32.768 KHz */
+#define RTC_RATE6 0x06 /* interrupt rate of 976.562 */
+
+/*
+ * Register B definitions
+ */
+#define RTC_B 0x0b /* register B address */
+#define RTC_SET 0x80 /* stop updates for time set */
+#define RTC_PIE 0x40 /* Periodic interrupt enable */
+#define RTC_AIE 0x20 /* Alarm interrupt enable */
+#define RTC_UIE 0x10 /* Update ended interrupt enable */
+#define RTC_SQWE 0x08 /* Square wave enable */
+#define RTC_DM 0x04 /* Date mode, 1 = binary, 0 = BCD */
+#define RTC_HM 0x02 /* hour mode, 1 = 24 hour, 0 = 12 hour */
+#define RTC_DSE 0x01 /* Daylight savings enable */
+
+/*
+ * Register C definitions
+ */
+#define RTC_C 0x0c /* register C address */
+#define RTC_IRQF 0x80 /* IRQ flag */
+#define RTC_PF 0x40 /* PF flag bit */
+#define RTC_AF 0x20 /* AF flag bit */
+#define RTC_UF 0x10 /* UF flag bit */
+
+/*
+ * Register D definitions
+ */
+#define RTC_D 0x0d /* register D address */
+#define RTC_VRT 0x80 /* Valid RAM and time bit */
+
+#define RTC_NREG 0x0e /* number of RTC registers */
+#define RTC_NREGP 0x0a /* number of RTC registers to set time */
+
+#define RTCRTIME _IOR('c', 0x01, struct rtc_st) /* Read time from RTC */
+#define RTCSTIME _IOW('c', 0x02, struct rtc_st) /* Set time into RTC */
+
+struct rtc_st {
+ char rtc_sec;
+ char rtc_asec;
+ char rtc_min;
+ char rtc_amin;
+ char rtc_hr;
+ char rtc_ahr;
+ char rtc_dow;
+ char rtc_dom;
+ char rtc_mon;
+ char rtc_yr;
+ char rtc_statusa;
+ char rtc_statusb;
+ char rtc_statusc;
+ char rtc_statusd;
+};
+
+/*
+ * this macro reads contents of real time clock to specified buffer
+ */
+#define load_rtc(regs) \
+{\
+ register int i; \
+ \
+ for (i = 0; i < RTC_NREG; i++) { \
+ outb(RTC_ADDR, i); \
+ regs[i] = inb(RTC_DATA); \
+ } \
+}
+
+/*
+ * this macro writes contents of specified buffer to real time clock
+ */
+#define save_rtc(regs) \
+{ \
+ register int i; \
+ for (i = 0; i < RTC_NREGP; i++) { \
+ outb(RTC_ADDR, i); \
+ outb(RTC_DATA, regs[i]);\
+ } \
+}
+
+